khang_91

Committer:
nguyenhoang9x5555
Date:
Tue Jun 23 07:26:42 2020 +0000
Revision:
183:16414698889c
phienbanthunghiem

Who changed what in which revision?

UserRevisionLine numberNew contents of line
nguyenhoang9x5555 183:16414698889c 1 /**********************************************************************
nguyenhoang9x5555 183:16414698889c 2 * COPYRIGHT 2016 MULTI-TECH SYSTEMS, INC.
nguyenhoang9x5555 183:16414698889c 3 *
nguyenhoang9x5555 183:16414698889c 4 * ALL RIGHTS RESERVED BY AND FOR THE EXCLUSIVE BENEFIT OF
nguyenhoang9x5555 183:16414698889c 5 * MULTI-TECH SYSTEMS, INC.
nguyenhoang9x5555 183:16414698889c 6 *
nguyenhoang9x5555 183:16414698889c 7 * MULTI-TECH SYSTEMS, INC. - CONFIDENTIAL AND PROPRIETARY
nguyenhoang9x5555 183:16414698889c 8 * INFORMATION AND/OR TRADE SECRET.
nguyenhoang9x5555 183:16414698889c 9 *
nguyenhoang9x5555 183:16414698889c 10 * NOTICE: ALL CODE, PROGRAM, INFORMATION, SCRIPT, INSTRUCTION,
nguyenhoang9x5555 183:16414698889c 11 * DATA, AND COMMENT HEREIN IS AND SHALL REMAIN THE CONFIDENTIAL
nguyenhoang9x5555 183:16414698889c 12 * INFORMATION AND PROPERTY OF MULTI-TECH SYSTEMS, INC.
nguyenhoang9x5555 183:16414698889c 13 * USE AND DISCLOSURE THEREOF, EXCEPT AS STRICTLY AUTHORIZED IN A
nguyenhoang9x5555 183:16414698889c 14 * WRITTEN AGREEMENT SIGNED BY MULTI-TECH SYSTEMS, INC. IS PROHIBITED.
nguyenhoang9x5555 183:16414698889c 15 *
nguyenhoang9x5555 183:16414698889c 16 ***********************************************************************/
nguyenhoang9x5555 183:16414698889c 17
nguyenhoang9x5555 183:16414698889c 18 #include "ChannelPlan_VI919.h"
nguyenhoang9x5555 183:16414698889c 19 #include "limits.h"
nguyenhoang9x5555 183:16414698889c 20
nguyenhoang9x5555 183:16414698889c 21 using namespace lora;
nguyenhoang9x5555 183:16414698889c 22
nguyenhoang9x5555 183:16414698889c 23 const uint8_t ChannelPlan_VI919::VI919_RADIO_POWERS[] = { 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20 };
nguyenhoang9x5555 183:16414698889c 24 const uint8_t ChannelPlan_VI919::VI919_MAX_PAYLOAD_SIZE[] = { 51, 51, 51, 115, 242, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0 };
nguyenhoang9x5555 183:16414698889c 25 const uint8_t ChannelPlan_VI919::VI919_MAX_PAYLOAD_SIZE_REPEATER[] = { 51, 51, 51, 115, 222, 222, 222, 222, 0, 0, 0, 0, 0, 0, 0, 0 };
nguyenhoang9x5555 183:16414698889c 26 const uint8_t ChannelPlan_VI919::VI919_MAX_PAYLOAD_SIZE_400[] = { 0, 0, 11, 53, 125, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0 };
nguyenhoang9x5555 183:16414698889c 27 const uint8_t ChannelPlan_VI919::VI919_MAX_PAYLOAD_SIZE_REPEATER_400[] = { 0, 0, 11, 53, 125, 222, 222, 222, 0, 0, 0, 0, 0, 0, 0, 0 };
nguyenhoang9x5555 183:16414698889c 28
nguyenhoang9x5555 183:16414698889c 29 const uint8_t MAX_ERP_VALUES[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 };
nguyenhoang9x5555 183:16414698889c 30
nguyenhoang9x5555 183:16414698889c 31 ChannelPlan_VI919::ChannelPlan_VI919()
nguyenhoang9x5555 183:16414698889c 32 :
nguyenhoang9x5555 183:16414698889c 33 ChannelPlan(NULL, NULL)
nguyenhoang9x5555 183:16414698889c 34 {
nguyenhoang9x5555 183:16414698889c 35
nguyenhoang9x5555 183:16414698889c 36 }
nguyenhoang9x5555 183:16414698889c 37
nguyenhoang9x5555 183:16414698889c 38 ChannelPlan_VI919::ChannelPlan_VI919(Settings* settings)
nguyenhoang9x5555 183:16414698889c 39 :
nguyenhoang9x5555 183:16414698889c 40 ChannelPlan(NULL, settings)
nguyenhoang9x5555 183:16414698889c 41 {
nguyenhoang9x5555 183:16414698889c 42
nguyenhoang9x5555 183:16414698889c 43 }
nguyenhoang9x5555 183:16414698889c 44
nguyenhoang9x5555 183:16414698889c 45 ChannelPlan_VI919::ChannelPlan_VI919(SxRadio* radio, Settings* settings)
nguyenhoang9x5555 183:16414698889c 46 :
nguyenhoang9x5555 183:16414698889c 47 ChannelPlan(radio, settings)
nguyenhoang9x5555 183:16414698889c 48 {
nguyenhoang9x5555 183:16414698889c 49
nguyenhoang9x5555 183:16414698889c 50 }
nguyenhoang9x5555 183:16414698889c 51
nguyenhoang9x5555 183:16414698889c 52 ChannelPlan_VI919::~ChannelPlan_VI919() {
nguyenhoang9x5555 183:16414698889c 53
nguyenhoang9x5555 183:16414698889c 54 }
nguyenhoang9x5555 183:16414698889c 55
nguyenhoang9x5555 183:16414698889c 56 void ChannelPlan_VI919::Init() {
nguyenhoang9x5555 183:16414698889c 57
nguyenhoang9x5555 183:16414698889c 58 _datarates.clear();
nguyenhoang9x5555 183:16414698889c 59 _channels.clear();
nguyenhoang9x5555 183:16414698889c 60 _dutyBands.clear();
nguyenhoang9x5555 183:16414698889c 61
nguyenhoang9x5555 183:16414698889c 62 DutyBand band;
nguyenhoang9x5555 183:16414698889c 63
nguyenhoang9x5555 183:16414698889c 64 band.Index = 0;
nguyenhoang9x5555 183:16414698889c 65 band.DutyCycle = 0;
nguyenhoang9x5555 183:16414698889c 66
nguyenhoang9x5555 183:16414698889c 67 Datarate dr;
nguyenhoang9x5555 183:16414698889c 68
nguyenhoang9x5555 183:16414698889c 69 _plan = VI919;
nguyenhoang9x5555 183:16414698889c 70 _planName = "VI919";
nguyenhoang9x5555 183:16414698889c 71 _maxTxPower = 14;
nguyenhoang9x5555 183:16414698889c 72 _minTxPower = 0;
nguyenhoang9x5555 183:16414698889c 73
nguyenhoang9x5555 183:16414698889c 74 _minFrequency = 915000000;
nguyenhoang9x5555 183:16414698889c 75 _maxFrequency = 928000000;
nguyenhoang9x5555 183:16414698889c 76
nguyenhoang9x5555 183:16414698889c 77 RADIO_POWERS = VI919_RADIO_POWERS;
nguyenhoang9x5555 183:16414698889c 78 MAX_PAYLOAD_SIZE = VI919_MAX_PAYLOAD_SIZE;
nguyenhoang9x5555 183:16414698889c 79 MAX_PAYLOAD_SIZE_REPEATER = VI919_MAX_PAYLOAD_SIZE_REPEATER;
nguyenhoang9x5555 183:16414698889c 80
nguyenhoang9x5555 183:16414698889c 81 _minDatarate = 0;
nguyenhoang9x5555 183:16414698889c 82 _maxDatarate = 7;
nguyenhoang9x5555 183:16414698889c 83
nguyenhoang9x5555 183:16414698889c 84 _minRx2Datarate = DR_0;
nguyenhoang9x5555 183:16414698889c 85 _maxRx2Datarate = DR_7;
nguyenhoang9x5555 183:16414698889c 86
nguyenhoang9x5555 183:16414698889c 87 _minDatarateOffset = 0;
nguyenhoang9x5555 183:16414698889c 88 _maxDatarateOffset = 7;
nguyenhoang9x5555 183:16414698889c 89
nguyenhoang9x5555 183:16414698889c 90 _numChans125k = 16;
nguyenhoang9x5555 183:16414698889c 91 _numChans500k = 0;
nguyenhoang9x5555 183:16414698889c 92
nguyenhoang9x5555 183:16414698889c 93 GetSettings()->Session.Rx2Frequency = 919600000; //923200000
nguyenhoang9x5555 183:16414698889c 94 GetSettings()->Session.Rx2DatarateIndex = DR_2;
nguyenhoang9x5555 183:16414698889c 95
nguyenhoang9x5555 183:16414698889c 96 GetSettings()->Session.BeaconFrequency = VI919_BEACON_FREQ;
nguyenhoang9x5555 183:16414698889c 97 GetSettings()->Session.BeaconDatarateIndex = VI919_BEACON_DR;
nguyenhoang9x5555 183:16414698889c 98 GetSettings()->Session.PingSlotFrequency = VI919_BEACON_FREQ;
nguyenhoang9x5555 183:16414698889c 99 GetSettings()->Session.PingSlotDatarateIndex = VI919_BEACON_DR;
nguyenhoang9x5555 183:16414698889c 100
nguyenhoang9x5555 183:16414698889c 101 GetSettings()->Session.Max_EIRP = 16;
nguyenhoang9x5555 183:16414698889c 102
nguyenhoang9x5555 183:16414698889c 103 logInfo("Initialize datarates...");
nguyenhoang9x5555 183:16414698889c 104
nguyenhoang9x5555 183:16414698889c 105 dr.SpreadingFactor = SF_10;//12
nguyenhoang9x5555 183:16414698889c 106
nguyenhoang9x5555 183:16414698889c 107 // Add DR0-5
nguyenhoang9x5555 183:16414698889c 108 while (dr.SpreadingFactor >= SF_7) {
nguyenhoang9x5555 183:16414698889c 109 AddDatarate(-1, dr);
nguyenhoang9x5555 183:16414698889c 110 dr.SpreadingFactor--;
nguyenhoang9x5555 183:16414698889c 111 dr.Index++;
nguyenhoang9x5555 183:16414698889c 112 }
nguyenhoang9x5555 183:16414698889c 113
nguyenhoang9x5555 183:16414698889c 114 // Add DR6
nguyenhoang9x5555 183:16414698889c 115 dr.SpreadingFactor = SF_7;
nguyenhoang9x5555 183:16414698889c 116 dr.Bandwidth = BW_250;
nguyenhoang9x5555 183:16414698889c 117 AddDatarate(-1, dr);
nguyenhoang9x5555 183:16414698889c 118 dr.Index++;
nguyenhoang9x5555 183:16414698889c 119
nguyenhoang9x5555 183:16414698889c 120 // Add DR7
nguyenhoang9x5555 183:16414698889c 121 dr.SpreadingFactor = SF_FSK;
nguyenhoang9x5555 183:16414698889c 122 dr.Bandwidth = BW_FSK;
nguyenhoang9x5555 183:16414698889c 123 dr.PreambleLength = 10;
nguyenhoang9x5555 183:16414698889c 124 dr.Coderate = 0;
nguyenhoang9x5555 183:16414698889c 125 AddDatarate(-1, dr);
nguyenhoang9x5555 183:16414698889c 126 dr.Index++;
nguyenhoang9x5555 183:16414698889c 127
nguyenhoang9x5555 183:16414698889c 128 _maxDatarate = DR_7;
nguyenhoang9x5555 183:16414698889c 129
nguyenhoang9x5555 183:16414698889c 130 // Skip DR8-15 RFU
nguyenhoang9x5555 183:16414698889c 131 dr.SpreadingFactor = SF_INVALID;
nguyenhoang9x5555 183:16414698889c 132 while (dr.Index++ <= DR_15) {
nguyenhoang9x5555 183:16414698889c 133 AddDatarate(-1, dr);
nguyenhoang9x5555 183:16414698889c 134 }
nguyenhoang9x5555 183:16414698889c 135
nguyenhoang9x5555 183:16414698889c 136 GetSettings()->Session.TxDatarate = 0;
nguyenhoang9x5555 183:16414698889c 137
nguyenhoang9x5555 183:16414698889c 138 logInfo("Initialize channels...");
nguyenhoang9x5555 183:16414698889c 139
nguyenhoang9x5555 183:16414698889c 140 Channel chan;
nguyenhoang9x5555 183:16414698889c 141 chan.DrRange.Fields.Min = DR_0;
nguyenhoang9x5555 183:16414698889c 142 chan.DrRange.Fields.Max = DR_5;
nguyenhoang9x5555 183:16414698889c 143 chan.Index = 0;
nguyenhoang9x5555 183:16414698889c 144 chan.Frequency = 919600000; //923200000
nguyenhoang9x5555 183:16414698889c 145 SetNumberOfChannels(16);
nguyenhoang9x5555 183:16414698889c 146
nguyenhoang9x5555 183:16414698889c 147 for (uint8_t i = 0; i < 2; i++) {
nguyenhoang9x5555 183:16414698889c 148 AddChannel(i, chan);
nguyenhoang9x5555 183:16414698889c 149 chan.Index++;
nguyenhoang9x5555 183:16414698889c 150 chan.Frequency += 200000;
nguyenhoang9x5555 183:16414698889c 151 }
nguyenhoang9x5555 183:16414698889c 152
nguyenhoang9x5555 183:16414698889c 153 chan.DrRange.Value = 0;
nguyenhoang9x5555 183:16414698889c 154 chan.Frequency = 0;
nguyenhoang9x5555 183:16414698889c 155
nguyenhoang9x5555 183:16414698889c 156 for (uint8_t i = 2; i < 16; i++) {
nguyenhoang9x5555 183:16414698889c 157 AddChannel(i, chan);
nguyenhoang9x5555 183:16414698889c 158 chan.Index++;
nguyenhoang9x5555 183:16414698889c 159 }
nguyenhoang9x5555 183:16414698889c 160
nguyenhoang9x5555 183:16414698889c 161 SetChannelMask(0, 0x03);
nguyenhoang9x5555 183:16414698889c 162
nguyenhoang9x5555 183:16414698889c 163
nguyenhoang9x5555 183:16414698889c 164 // Add downlink channel defaults
nguyenhoang9x5555 183:16414698889c 165 chan.Index = 0;
nguyenhoang9x5555 183:16414698889c 166 _dlChannels.resize(16);
nguyenhoang9x5555 183:16414698889c 167 for (uint8_t i = 0; i < 16; i++) {
nguyenhoang9x5555 183:16414698889c 168 AddDownlinkChannel(i, chan);
nguyenhoang9x5555 183:16414698889c 169 chan.Index++;
nguyenhoang9x5555 183:16414698889c 170 }
nguyenhoang9x5555 183:16414698889c 171
nguyenhoang9x5555 183:16414698889c 172 band.Index = 0;
nguyenhoang9x5555 183:16414698889c 173 band.FrequencyMin = _minFrequency;
nguyenhoang9x5555 183:16414698889c 174 band.FrequencyMax = _maxFrequency;
nguyenhoang9x5555 183:16414698889c 175 band.PowerMax = 14;
nguyenhoang9x5555 183:16414698889c 176 band.TimeOffEnd = 0;
nguyenhoang9x5555 183:16414698889c 177
nguyenhoang9x5555 183:16414698889c 178 // Disable duty-cycle limits
nguyenhoang9x5555 183:16414698889c 179 band.DutyCycle = 0;
nguyenhoang9x5555 183:16414698889c 180
nguyenhoang9x5555 183:16414698889c 181 AddDutyBand(-1, band);
nguyenhoang9x5555 183:16414698889c 182
nguyenhoang9x5555 183:16414698889c 183 GetSettings()->Session.TxPower = GetSettings()->Network.TxPower;
nguyenhoang9x5555 183:16414698889c 184 }
nguyenhoang9x5555 183:16414698889c 185
nguyenhoang9x5555 183:16414698889c 186 uint8_t ChannelPlan_VI919::AddChannel(int8_t index, Channel channel) {
nguyenhoang9x5555 183:16414698889c 187 logTrace("Add Channel %d : %lu : %02x %d", index, channel.Frequency, channel.DrRange.Value, _channels.size());
nguyenhoang9x5555 183:16414698889c 188
nguyenhoang9x5555 183:16414698889c 189 assert(index < (int) _channels.size());
nguyenhoang9x5555 183:16414698889c 190
nguyenhoang9x5555 183:16414698889c 191 if (index >= 0) {
nguyenhoang9x5555 183:16414698889c 192 _channels[index] = channel;
nguyenhoang9x5555 183:16414698889c 193 } else {
nguyenhoang9x5555 183:16414698889c 194 _channels.push_back(channel);
nguyenhoang9x5555 183:16414698889c 195 }
nguyenhoang9x5555 183:16414698889c 196
nguyenhoang9x5555 183:16414698889c 197 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 198 }
nguyenhoang9x5555 183:16414698889c 199
nguyenhoang9x5555 183:16414698889c 200 uint8_t ChannelPlan_VI919::HandleJoinAccept(const uint8_t* buffer, uint8_t size) {
nguyenhoang9x5555 183:16414698889c 201
nguyenhoang9x5555 183:16414698889c 202 if (size == 33) {
nguyenhoang9x5555 183:16414698889c 203 Channel ch;
nguyenhoang9x5555 183:16414698889c 204 int index = 2;
nguyenhoang9x5555 183:16414698889c 205 for (int i = 13; i < size - 5; i += 3) {
nguyenhoang9x5555 183:16414698889c 206
nguyenhoang9x5555 183:16414698889c 207 ch.Frequency = ((buffer[i]) | (buffer[i + 1] << 8) | (buffer[i + 2] << 16)) * 100u;
nguyenhoang9x5555 183:16414698889c 208
nguyenhoang9x5555 183:16414698889c 209 if (ch.Frequency > 0) {
nguyenhoang9x5555 183:16414698889c 210 ch.Index = index;
nguyenhoang9x5555 183:16414698889c 211 ch.DrRange.Fields.Min = static_cast<int8_t>(DR_0);
nguyenhoang9x5555 183:16414698889c 212 ch.DrRange.Fields.Max = static_cast<int8_t>(DR_5);
nguyenhoang9x5555 183:16414698889c 213 AddChannel(index, ch);
nguyenhoang9x5555 183:16414698889c 214
nguyenhoang9x5555 183:16414698889c 215 if (GetDutyBand(ch.Frequency) > -1)
nguyenhoang9x5555 183:16414698889c 216 _channelMask[0] |= (1 << index);
nguyenhoang9x5555 183:16414698889c 217 else
nguyenhoang9x5555 183:16414698889c 218 _channelMask[0] |= ~(1 << index);
nguyenhoang9x5555 183:16414698889c 219
nguyenhoang9x5555 183:16414698889c 220 index += 1;
nguyenhoang9x5555 183:16414698889c 221 }
nguyenhoang9x5555 183:16414698889c 222 }
nguyenhoang9x5555 183:16414698889c 223 }
nguyenhoang9x5555 183:16414698889c 224
nguyenhoang9x5555 183:16414698889c 225 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 226 }
nguyenhoang9x5555 183:16414698889c 227
nguyenhoang9x5555 183:16414698889c 228 uint8_t ChannelPlan_VI919::SetTxConfig() {
nguyenhoang9x5555 183:16414698889c 229
nguyenhoang9x5555 183:16414698889c 230 logInfo("Configure radio for TX");
nguyenhoang9x5555 183:16414698889c 231
nguyenhoang9x5555 183:16414698889c 232 Datarate txDr = GetDatarate(GetSettings()->Session.TxDatarate);
nguyenhoang9x5555 183:16414698889c 233 int8_t max_pwr = GetSettings()->Session.Max_EIRP;
nguyenhoang9x5555 183:16414698889c 234
nguyenhoang9x5555 183:16414698889c 235 int8_t pwr = 0;
nguyenhoang9x5555 183:16414698889c 236
nguyenhoang9x5555 183:16414698889c 237 pwr = std::min < int8_t > (GetSettings()->Session.TxPower, max_pwr);
nguyenhoang9x5555 183:16414698889c 238 pwr -= GetSettings()->Network.AntennaGain;
nguyenhoang9x5555 183:16414698889c 239
nguyenhoang9x5555 183:16414698889c 240 for (int i = 20; i >= 0; i--) {
nguyenhoang9x5555 183:16414698889c 241 if (RADIO_POWERS[i] <= pwr) {
nguyenhoang9x5555 183:16414698889c 242 pwr = i;
nguyenhoang9x5555 183:16414698889c 243 break;
nguyenhoang9x5555 183:16414698889c 244 }
nguyenhoang9x5555 183:16414698889c 245 if (i == 0) {
nguyenhoang9x5555 183:16414698889c 246 pwr = i;
nguyenhoang9x5555 183:16414698889c 247 }
nguyenhoang9x5555 183:16414698889c 248 }
nguyenhoang9x5555 183:16414698889c 249
nguyenhoang9x5555 183:16414698889c 250 logDebug("Session pwr: %d ant: %d max: %d", GetSettings()->Session.TxPower, GetSettings()->Network.AntennaGain, max_pwr);
nguyenhoang9x5555 183:16414698889c 251 logDebug("Radio Power index: %d output: %d total: %d", pwr, RADIO_POWERS[pwr], RADIO_POWERS[pwr] + GetSettings()->Network.AntennaGain);
nguyenhoang9x5555 183:16414698889c 252
nguyenhoang9x5555 183:16414698889c 253 uint32_t bw = txDr.Bandwidth;
nguyenhoang9x5555 183:16414698889c 254 uint32_t sf = txDr.SpreadingFactor;
nguyenhoang9x5555 183:16414698889c 255 uint8_t cr = txDr.Coderate;
nguyenhoang9x5555 183:16414698889c 256 uint8_t pl = txDr.PreambleLength;
nguyenhoang9x5555 183:16414698889c 257 uint16_t fdev = 0;
nguyenhoang9x5555 183:16414698889c 258 bool crc = txDr.Crc;
nguyenhoang9x5555 183:16414698889c 259 bool iq = txDr.TxIQ;
nguyenhoang9x5555 183:16414698889c 260
nguyenhoang9x5555 183:16414698889c 261 if (GetSettings()->Network.DisableCRC == true)
nguyenhoang9x5555 183:16414698889c 262 crc = false;
nguyenhoang9x5555 183:16414698889c 263
nguyenhoang9x5555 183:16414698889c 264 SxRadio::RadioModems_t modem = SxRadio::MODEM_LORA;
nguyenhoang9x5555 183:16414698889c 265
nguyenhoang9x5555 183:16414698889c 266 if (sf == SF_FSK) {
nguyenhoang9x5555 183:16414698889c 267 modem = SxRadio::MODEM_FSK;
nguyenhoang9x5555 183:16414698889c 268 sf = 50e3;
nguyenhoang9x5555 183:16414698889c 269 fdev = 25e3;
nguyenhoang9x5555 183:16414698889c 270 bw = 0;
nguyenhoang9x5555 183:16414698889c 271 }
nguyenhoang9x5555 183:16414698889c 272
nguyenhoang9x5555 183:16414698889c 273 GetRadio()->SetTxConfig(modem, pwr, fdev, bw, sf, cr, pl, false, crc, false, 0, iq, 3e3);
nguyenhoang9x5555 183:16414698889c 274
nguyenhoang9x5555 183:16414698889c 275 logDebug("TX PWR: %u DR: %u SF: %u BW: %u CR: %u PL: %u CRC: %d IQ: %d", pwr, txDr.Index, sf, bw, cr, pl, crc, iq);
nguyenhoang9x5555 183:16414698889c 276
nguyenhoang9x5555 183:16414698889c 277 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 278 }
nguyenhoang9x5555 183:16414698889c 279
nguyenhoang9x5555 183:16414698889c 280 uint8_t ChannelPlan_VI919::SetRxConfig(uint8_t window, bool continuous, uint16_t wnd_growth) {
nguyenhoang9x5555 183:16414698889c 281
nguyenhoang9x5555 183:16414698889c 282 RxWindow rxw = GetRxWindow(window);
nguyenhoang9x5555 183:16414698889c 283
nguyenhoang9x5555 183:16414698889c 284 if (_dlChannels[_txChannel].Frequency != 0 && window == 1)
nguyenhoang9x5555 183:16414698889c 285 GetRadio()->SetChannel(_dlChannels[_txChannel].Frequency);
nguyenhoang9x5555 183:16414698889c 286 else
nguyenhoang9x5555 183:16414698889c 287 GetRadio()->SetChannel(rxw.Frequency);
nguyenhoang9x5555 183:16414698889c 288
nguyenhoang9x5555 183:16414698889c 289 Datarate rxDr = GetDatarate(rxw.DatarateIndex);
nguyenhoang9x5555 183:16414698889c 290 uint32_t bw = rxDr.Bandwidth;
nguyenhoang9x5555 183:16414698889c 291 uint32_t sf = rxDr.SpreadingFactor;
nguyenhoang9x5555 183:16414698889c 292 uint8_t cr = rxDr.Coderate;
nguyenhoang9x5555 183:16414698889c 293 uint8_t pl = rxDr.PreambleLength;
nguyenhoang9x5555 183:16414698889c 294 uint16_t sto = rxDr.SymbolTimeout() * wnd_growth;
nguyenhoang9x5555 183:16414698889c 295 uint32_t afc = 0;
nguyenhoang9x5555 183:16414698889c 296 bool fixLen = false;
nguyenhoang9x5555 183:16414698889c 297 uint8_t payloadLen = 0U;
nguyenhoang9x5555 183:16414698889c 298 bool crc = false; // downlink does not use CRC according to LORAWAN
nguyenhoang9x5555 183:16414698889c 299
nguyenhoang9x5555 183:16414698889c 300 if (GetSettings()->Network.DisableCRC == true)
nguyenhoang9x5555 183:16414698889c 301 crc = false;
nguyenhoang9x5555 183:16414698889c 302
nguyenhoang9x5555 183:16414698889c 303 Datarate txDr = GetDatarate(GetSettings()->Session.TxDatarate);
nguyenhoang9x5555 183:16414698889c 304 bool iq = txDr.RxIQ;
nguyenhoang9x5555 183:16414698889c 305
nguyenhoang9x5555 183:16414698889c 306 if (P2PEnabled()) {
nguyenhoang9x5555 183:16414698889c 307 iq = txDr.TxIQ;
nguyenhoang9x5555 183:16414698889c 308 }
nguyenhoang9x5555 183:16414698889c 309
nguyenhoang9x5555 183:16414698889c 310 // Beacon modifications - no I/Q inversion, fixed length rx, preamble
nguyenhoang9x5555 183:16414698889c 311 if (window == RX_BEACON) {
nguyenhoang9x5555 183:16414698889c 312 iq = txDr.TxIQ;
nguyenhoang9x5555 183:16414698889c 313 fixLen = true;
nguyenhoang9x5555 183:16414698889c 314 payloadLen = sizeof(BCNPayload);
nguyenhoang9x5555 183:16414698889c 315 pl = BEACON_PREAMBLE_LENGTH;
nguyenhoang9x5555 183:16414698889c 316 }
nguyenhoang9x5555 183:16414698889c 317
nguyenhoang9x5555 183:16414698889c 318 SxRadio::RadioModems_t modem = SxRadio::MODEM_LORA;
nguyenhoang9x5555 183:16414698889c 319
nguyenhoang9x5555 183:16414698889c 320 if (sf == SF_FSK) {
nguyenhoang9x5555 183:16414698889c 321 modem = SxRadio::MODEM_FSK;
nguyenhoang9x5555 183:16414698889c 322 sf = 50e3;
nguyenhoang9x5555 183:16414698889c 323 cr = 0;
nguyenhoang9x5555 183:16414698889c 324 bw = 50e3;
nguyenhoang9x5555 183:16414698889c 325 afc = 83333;
nguyenhoang9x5555 183:16414698889c 326 iq = false;
nguyenhoang9x5555 183:16414698889c 327 crc = true; // FSK must use CRC
nguyenhoang9x5555 183:16414698889c 328 }
nguyenhoang9x5555 183:16414698889c 329
nguyenhoang9x5555 183:16414698889c 330 // Disable printf's to actually receive packets, printing to debug may mess up the timing
nguyenhoang9x5555 183:16414698889c 331 // logTrace("Configure radio for RX%d on freq: %lu", window, rxw.Frequency);
nguyenhoang9x5555 183:16414698889c 332 // logTrace("RX SF: %u BW: %u CR: %u PL: %u STO: %u CRC: %d IQ: %d", sf, bw, cr, pl, sto, crc, iq);
nguyenhoang9x5555 183:16414698889c 333
nguyenhoang9x5555 183:16414698889c 334 GetRadio()->SetRxConfig(modem, bw, sf, cr, afc, pl, sto, fixLen, payloadLen, crc, false, 0, iq, continuous);
nguyenhoang9x5555 183:16414698889c 335
nguyenhoang9x5555 183:16414698889c 336 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 337 }
nguyenhoang9x5555 183:16414698889c 338
nguyenhoang9x5555 183:16414698889c 339 Channel ChannelPlan_VI919::GetChannel(int8_t index) {
nguyenhoang9x5555 183:16414698889c 340 Channel chan;
nguyenhoang9x5555 183:16414698889c 341 memset(&chan, 0, sizeof(Channel));
nguyenhoang9x5555 183:16414698889c 342
nguyenhoang9x5555 183:16414698889c 343 chan = _channels[index];
nguyenhoang9x5555 183:16414698889c 344
nguyenhoang9x5555 183:16414698889c 345 return chan;
nguyenhoang9x5555 183:16414698889c 346 }
nguyenhoang9x5555 183:16414698889c 347
nguyenhoang9x5555 183:16414698889c 348 uint8_t ChannelPlan_VI919::SetFrequencySubBand(uint8_t sub_band) {
nguyenhoang9x5555 183:16414698889c 349 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 350 }
nguyenhoang9x5555 183:16414698889c 351
nguyenhoang9x5555 183:16414698889c 352 void ChannelPlan_VI919::LogRxWindow(uint8_t wnd) {
nguyenhoang9x5555 183:16414698889c 353
nguyenhoang9x5555 183:16414698889c 354 RxWindow rxw = GetRxWindow(wnd);
nguyenhoang9x5555 183:16414698889c 355 Datarate rxDr = GetDatarate(rxw.DatarateIndex);
nguyenhoang9x5555 183:16414698889c 356 uint8_t bw = rxDr.Bandwidth;
nguyenhoang9x5555 183:16414698889c 357 uint8_t sf = rxDr.SpreadingFactor;
nguyenhoang9x5555 183:16414698889c 358 uint8_t cr = rxDr.Coderate;
nguyenhoang9x5555 183:16414698889c 359 uint8_t pl = rxDr.PreambleLength;
nguyenhoang9x5555 183:16414698889c 360 uint16_t sto = rxDr.SymbolTimeout();
nguyenhoang9x5555 183:16414698889c 361 bool crc = false; // downlink does not use CRC according to LORAWAN
nguyenhoang9x5555 183:16414698889c 362 bool iq = GetTxDatarate().RxIQ;
nguyenhoang9x5555 183:16414698889c 363 uint32_t freq = rxw.Frequency;
nguyenhoang9x5555 183:16414698889c 364
nguyenhoang9x5555 183:16414698889c 365 if (_dlChannels[_txChannel].Frequency != 0)
nguyenhoang9x5555 183:16414698889c 366 freq = _dlChannels[_txChannel].Frequency;
nguyenhoang9x5555 183:16414698889c 367
nguyenhoang9x5555 183:16414698889c 368 logTrace("RX%d on freq: %lu", wnd, freq);
nguyenhoang9x5555 183:16414698889c 369 logTrace("RX DR: %u SF: %u BW: %u CR: %u PL: %u STO: %u CRC: %d IQ: %d", rxDr.Index, sf, bw, cr, pl, sto, crc, iq);
nguyenhoang9x5555 183:16414698889c 370 }
nguyenhoang9x5555 183:16414698889c 371
nguyenhoang9x5555 183:16414698889c 372 uint8_t ChannelPlan_VI919::GetMaxPayloadSize() {
nguyenhoang9x5555 183:16414698889c 373 if (GetSettings()->Session.UplinkDwelltime == 1) {
nguyenhoang9x5555 183:16414698889c 374 if (GetSettings()->Network.RepeaterMode)
nguyenhoang9x5555 183:16414698889c 375 return VI919_MAX_PAYLOAD_SIZE_REPEATER_400[GetSettings()->Session.TxDatarate];
nguyenhoang9x5555 183:16414698889c 376 else
nguyenhoang9x5555 183:16414698889c 377 return VI919_MAX_PAYLOAD_SIZE_400[GetSettings()->Session.TxDatarate];
nguyenhoang9x5555 183:16414698889c 378 } else {
nguyenhoang9x5555 183:16414698889c 379 if (GetSettings()->Network.RepeaterMode)
nguyenhoang9x5555 183:16414698889c 380 return MAX_PAYLOAD_SIZE_REPEATER[GetSettings()->Session.TxDatarate];
nguyenhoang9x5555 183:16414698889c 381 else
nguyenhoang9x5555 183:16414698889c 382 return MAX_PAYLOAD_SIZE[GetSettings()->Session.TxDatarate];
nguyenhoang9x5555 183:16414698889c 383 }
nguyenhoang9x5555 183:16414698889c 384 }
nguyenhoang9x5555 183:16414698889c 385
nguyenhoang9x5555 183:16414698889c 386 RxWindow ChannelPlan_VI919::GetRxWindow(uint8_t window) {
nguyenhoang9x5555 183:16414698889c 387 RxWindow rxw;
nguyenhoang9x5555 183:16414698889c 388 int index = 0;
nguyenhoang9x5555 183:16414698889c 389
nguyenhoang9x5555 183:16414698889c 390 if (P2PEnabled()) {
nguyenhoang9x5555 183:16414698889c 391 rxw.Frequency = GetSettings()->Network.TxFrequency;
nguyenhoang9x5555 183:16414698889c 392 index = GetSettings()->Session.TxDatarate;
nguyenhoang9x5555 183:16414698889c 393 } else {
nguyenhoang9x5555 183:16414698889c 394 switch (window) {
nguyenhoang9x5555 183:16414698889c 395 case RX_1:
nguyenhoang9x5555 183:16414698889c 396 {
nguyenhoang9x5555 183:16414698889c 397 // Use same frequency as TX
nguyenhoang9x5555 183:16414698889c 398 rxw.Frequency = _channels[_txChannel].Frequency;
nguyenhoang9x5555 183:16414698889c 399
nguyenhoang9x5555 183:16414698889c 400 if (GetSettings()->Session.Rx1DatarateOffset >= 6) {
nguyenhoang9x5555 183:16414698889c 401 index = GetSettings()->Session.TxDatarate + (GetSettings()->Session.Rx1DatarateOffset == 6 ? 1 : 2);
nguyenhoang9x5555 183:16414698889c 402 index = std::min<int>(index, _maxDatarate);
nguyenhoang9x5555 183:16414698889c 403 } else if (GetSettings()->Session.TxDatarate > GetSettings()->Session.Rx1DatarateOffset) {
nguyenhoang9x5555 183:16414698889c 404 index = GetSettings()->Session.TxDatarate - GetSettings()->Session.Rx1DatarateOffset;
nguyenhoang9x5555 183:16414698889c 405 } else {
nguyenhoang9x5555 183:16414698889c 406 index = 0;
nguyenhoang9x5555 183:16414698889c 407 }
nguyenhoang9x5555 183:16414698889c 408
nguyenhoang9x5555 183:16414698889c 409 // LoRaWAN 1.0.2 Sec 2.7.7
nguyenhoang9x5555 183:16414698889c 410 uint8_t minDr = GetSettings()->Session.DownlinkDwelltime == 1 ? 2 : 0;
nguyenhoang9x5555 183:16414698889c 411 index = std::min<uint8_t>(5, std::max<uint8_t>(minDr, index));
nguyenhoang9x5555 183:16414698889c 412
nguyenhoang9x5555 183:16414698889c 413 break;
nguyenhoang9x5555 183:16414698889c 414 }
nguyenhoang9x5555 183:16414698889c 415
nguyenhoang9x5555 183:16414698889c 416 case RX_BEACON:
nguyenhoang9x5555 183:16414698889c 417 rxw.Frequency = GetSettings()->Session.BeaconFrequency;
nguyenhoang9x5555 183:16414698889c 418 index = GetSettings()->Session.BeaconDatarateIndex;
nguyenhoang9x5555 183:16414698889c 419 break;
nguyenhoang9x5555 183:16414698889c 420
nguyenhoang9x5555 183:16414698889c 421 case RX_SLOT:
nguyenhoang9x5555 183:16414698889c 422 rxw.Frequency = GetSettings()->Session.PingSlotFrequency;
nguyenhoang9x5555 183:16414698889c 423 index = GetSettings()->Session.PingSlotDatarateIndex;
nguyenhoang9x5555 183:16414698889c 424 break;
nguyenhoang9x5555 183:16414698889c 425
nguyenhoang9x5555 183:16414698889c 426 // RX2, RXC, RX_TEST, etc..
nguyenhoang9x5555 183:16414698889c 427 default:
nguyenhoang9x5555 183:16414698889c 428 rxw.Frequency = GetSettings()->Session.Rx2Frequency;
nguyenhoang9x5555 183:16414698889c 429 index = GetSettings()->Session.Rx2DatarateIndex;
nguyenhoang9x5555 183:16414698889c 430 }
nguyenhoang9x5555 183:16414698889c 431 }
nguyenhoang9x5555 183:16414698889c 432
nguyenhoang9x5555 183:16414698889c 433 rxw.DatarateIndex = index;
nguyenhoang9x5555 183:16414698889c 434
nguyenhoang9x5555 183:16414698889c 435 return rxw;
nguyenhoang9x5555 183:16414698889c 436 }
nguyenhoang9x5555 183:16414698889c 437
nguyenhoang9x5555 183:16414698889c 438 uint8_t ChannelPlan_VI919::HandleRxParamSetup(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
nguyenhoang9x5555 183:16414698889c 439 status = 0x07;
nguyenhoang9x5555 183:16414698889c 440 int8_t datarate = 0;
nguyenhoang9x5555 183:16414698889c 441 int8_t drOffset = 0;
nguyenhoang9x5555 183:16414698889c 442 uint32_t freq = 0;
nguyenhoang9x5555 183:16414698889c 443
nguyenhoang9x5555 183:16414698889c 444 drOffset = payload[index++];
nguyenhoang9x5555 183:16414698889c 445 datarate = drOffset & 0x0F;
nguyenhoang9x5555 183:16414698889c 446 drOffset = (drOffset >> 4) & 0x07;
nguyenhoang9x5555 183:16414698889c 447
nguyenhoang9x5555 183:16414698889c 448 freq = payload[index++];
nguyenhoang9x5555 183:16414698889c 449 freq |= payload[index++] << 8;
nguyenhoang9x5555 183:16414698889c 450 freq |= payload[index++] << 16;
nguyenhoang9x5555 183:16414698889c 451 freq *= 100;
nguyenhoang9x5555 183:16414698889c 452
nguyenhoang9x5555 183:16414698889c 453 if (!CheckRfFrequency(freq)) {
nguyenhoang9x5555 183:16414698889c 454 logInfo("Freq KO");
nguyenhoang9x5555 183:16414698889c 455 status &= 0xFE; // Channel frequency KO
nguyenhoang9x5555 183:16414698889c 456 }
nguyenhoang9x5555 183:16414698889c 457
nguyenhoang9x5555 183:16414698889c 458 if (datarate < _minRx2Datarate || datarate > _maxRx2Datarate) {
nguyenhoang9x5555 183:16414698889c 459 logInfo("DR KO");
nguyenhoang9x5555 183:16414698889c 460 status &= 0xFD; // Datarate KO
nguyenhoang9x5555 183:16414698889c 461 }
nguyenhoang9x5555 183:16414698889c 462
nguyenhoang9x5555 183:16414698889c 463 if (drOffset < 0 || drOffset > _maxDatarateOffset) {
nguyenhoang9x5555 183:16414698889c 464 logInfo("DR Offset KO");
nguyenhoang9x5555 183:16414698889c 465 status &= 0xFB; // Rx1DrOffset range KO
nguyenhoang9x5555 183:16414698889c 466 }
nguyenhoang9x5555 183:16414698889c 467
nguyenhoang9x5555 183:16414698889c 468 if ((status & 0x07) == 0x07) {
nguyenhoang9x5555 183:16414698889c 469 logInfo("RxParamSetup accepted Rx2DR: %d Rx2Freq: %d Rx1Offset: %d", datarate, freq, drOffset);
nguyenhoang9x5555 183:16414698889c 470 SetRx2DatarateIndex(datarate);
nguyenhoang9x5555 183:16414698889c 471 SetRx2Frequency(freq);
nguyenhoang9x5555 183:16414698889c 472 SetRx1Offset(drOffset);
nguyenhoang9x5555 183:16414698889c 473 } else {
nguyenhoang9x5555 183:16414698889c 474 logInfo("RxParamSetup rejected Rx2DR: %d Rx2Freq: %d Rx1Offset: %d", datarate, freq, drOffset);
nguyenhoang9x5555 183:16414698889c 475 }
nguyenhoang9x5555 183:16414698889c 476
nguyenhoang9x5555 183:16414698889c 477 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 478 }
nguyenhoang9x5555 183:16414698889c 479
nguyenhoang9x5555 183:16414698889c 480 uint8_t ChannelPlan_VI919::HandleNewChannel(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
nguyenhoang9x5555 183:16414698889c 481
nguyenhoang9x5555 183:16414698889c 482 status = 0x03;
nguyenhoang9x5555 183:16414698889c 483 uint8_t channelIndex = 0;
nguyenhoang9x5555 183:16414698889c 484 Channel chParam;
nguyenhoang9x5555 183:16414698889c 485
nguyenhoang9x5555 183:16414698889c 486 channelIndex = payload[index++];
nguyenhoang9x5555 183:16414698889c 487 lora::CopyFreqtoInt(payload + index, chParam.Frequency);
nguyenhoang9x5555 183:16414698889c 488 index += 3;
nguyenhoang9x5555 183:16414698889c 489 chParam.DrRange.Value = payload[index++];
nguyenhoang9x5555 183:16414698889c 490
nguyenhoang9x5555 183:16414698889c 491 if (channelIndex < 2 || channelIndex > _channels.size() - 1) {
nguyenhoang9x5555 183:16414698889c 492 logError("New Channel index KO");
nguyenhoang9x5555 183:16414698889c 493 status &= 0xFE; // Channel index KO
nguyenhoang9x5555 183:16414698889c 494 }
nguyenhoang9x5555 183:16414698889c 495
nguyenhoang9x5555 183:16414698889c 496 if (chParam.Frequency == 0) {
nguyenhoang9x5555 183:16414698889c 497 chParam.DrRange.Value = 0;
nguyenhoang9x5555 183:16414698889c 498 } else if (chParam.Frequency < _minFrequency || chParam.Frequency > _maxFrequency) {
nguyenhoang9x5555 183:16414698889c 499 logError("New Channel frequency KO");
nguyenhoang9x5555 183:16414698889c 500 status &= 0xFE; // Channel frequency KO
nguyenhoang9x5555 183:16414698889c 501 }
nguyenhoang9x5555 183:16414698889c 502
nguyenhoang9x5555 183:16414698889c 503 if (chParam.DrRange.Fields.Min > chParam.DrRange.Fields.Max && chParam.Frequency != 0) {
nguyenhoang9x5555 183:16414698889c 504 logError("New Channel datarate min/max KO");
nguyenhoang9x5555 183:16414698889c 505 status &= 0xFD; // Datarate range KO
nguyenhoang9x5555 183:16414698889c 506 } else if ((chParam.DrRange.Fields.Min < _minDatarate || chParam.DrRange.Fields.Min > _maxDatarate) &&
nguyenhoang9x5555 183:16414698889c 507 chParam.Frequency != 0) {
nguyenhoang9x5555 183:16414698889c 508 logError("New Channel datarate min KO");
nguyenhoang9x5555 183:16414698889c 509 status &= 0xFD; // Datarate range KO
nguyenhoang9x5555 183:16414698889c 510 } else if ((chParam.DrRange.Fields.Max < _minDatarate || chParam.DrRange.Fields.Max > _maxDatarate) &&
nguyenhoang9x5555 183:16414698889c 511 chParam.Frequency != 0) {
nguyenhoang9x5555 183:16414698889c 512 logError("New Channel datarate max KO");
nguyenhoang9x5555 183:16414698889c 513 status &= 0xFD; // Datarate range KO
nguyenhoang9x5555 183:16414698889c 514 }
nguyenhoang9x5555 183:16414698889c 515
nguyenhoang9x5555 183:16414698889c 516 if ((status & 0x03) == 0x03) {
nguyenhoang9x5555 183:16414698889c 517 logInfo("New Channel accepted index: %d freq: %lu drRange: %02x", channelIndex, chParam.Frequency, chParam.DrRange.Value);
nguyenhoang9x5555 183:16414698889c 518 AddChannel(channelIndex, chParam);
nguyenhoang9x5555 183:16414698889c 519 SetChannelMask(0, _channelMask[0] | 1 << (channelIndex));
nguyenhoang9x5555 183:16414698889c 520 }
nguyenhoang9x5555 183:16414698889c 521
nguyenhoang9x5555 183:16414698889c 522 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 523 }
nguyenhoang9x5555 183:16414698889c 524
nguyenhoang9x5555 183:16414698889c 525 uint8_t ChannelPlan_VI919::HandlePingSlotChannelReq(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
nguyenhoang9x5555 183:16414698889c 526 uint8_t datarate = 0;
nguyenhoang9x5555 183:16414698889c 527 uint32_t freq = 0;
nguyenhoang9x5555 183:16414698889c 528
nguyenhoang9x5555 183:16414698889c 529 status = 0x03;
nguyenhoang9x5555 183:16414698889c 530
nguyenhoang9x5555 183:16414698889c 531 freq = payload[index++];
nguyenhoang9x5555 183:16414698889c 532 freq |= payload[index++] << 8;
nguyenhoang9x5555 183:16414698889c 533 freq |= payload[index++] << 16;
nguyenhoang9x5555 183:16414698889c 534 freq *= 100;
nguyenhoang9x5555 183:16414698889c 535
nguyenhoang9x5555 183:16414698889c 536 datarate = payload[index] & 0x0F;
nguyenhoang9x5555 183:16414698889c 537
nguyenhoang9x5555 183:16414698889c 538 if (freq == 0U) {
nguyenhoang9x5555 183:16414698889c 539 logInfo("Received request to reset ping slot frequency to default");
nguyenhoang9x5555 183:16414698889c 540 freq = VI919_BEACON_FREQ;
nguyenhoang9x5555 183:16414698889c 541 } else if (!CheckRfFrequency(freq)) {
nguyenhoang9x5555 183:16414698889c 542 logInfo("Freq KO");
nguyenhoang9x5555 183:16414698889c 543 status &= 0xFE; // Channel frequency KO
nguyenhoang9x5555 183:16414698889c 544 }
nguyenhoang9x5555 183:16414698889c 545
nguyenhoang9x5555 183:16414698889c 546 if (datarate < _minRx2Datarate || datarate > _maxRx2Datarate) {
nguyenhoang9x5555 183:16414698889c 547 logInfo("DR KO");
nguyenhoang9x5555 183:16414698889c 548 status &= 0xFD; // Datarate KO
nguyenhoang9x5555 183:16414698889c 549 }
nguyenhoang9x5555 183:16414698889c 550
nguyenhoang9x5555 183:16414698889c 551 if ((status & 0x03) == 0x03) {
nguyenhoang9x5555 183:16414698889c 552 logInfo("PingSlotChannelReq accepted DR: %d Freq: %d", datarate, freq);
nguyenhoang9x5555 183:16414698889c 553 GetSettings()->Session.PingSlotFrequency = freq;
nguyenhoang9x5555 183:16414698889c 554 GetSettings()->Session.PingSlotDatarateIndex = datarate;
nguyenhoang9x5555 183:16414698889c 555 } else {
nguyenhoang9x5555 183:16414698889c 556 logInfo("PingSlotChannelReq rejected DR: %d Freq: %d", datarate, freq);
nguyenhoang9x5555 183:16414698889c 557 }
nguyenhoang9x5555 183:16414698889c 558
nguyenhoang9x5555 183:16414698889c 559 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 560 }
nguyenhoang9x5555 183:16414698889c 561
nguyenhoang9x5555 183:16414698889c 562 uint8_t ChannelPlan_VI919::HandleBeaconFrequencyReq(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
nguyenhoang9x5555 183:16414698889c 563 uint32_t freq = 0;
nguyenhoang9x5555 183:16414698889c 564
nguyenhoang9x5555 183:16414698889c 565 status = 0x01;
nguyenhoang9x5555 183:16414698889c 566
nguyenhoang9x5555 183:16414698889c 567 freq = payload[index++];
nguyenhoang9x5555 183:16414698889c 568 freq |= payload[index++] << 8;
nguyenhoang9x5555 183:16414698889c 569 freq |= payload[index] << 16;
nguyenhoang9x5555 183:16414698889c 570 freq *= 100;
nguyenhoang9x5555 183:16414698889c 571
nguyenhoang9x5555 183:16414698889c 572 if (freq == 0U) {
nguyenhoang9x5555 183:16414698889c 573 logInfo("Received request to reset beacon frequency to default");
nguyenhoang9x5555 183:16414698889c 574 freq = VI919_BEACON_FREQ;
nguyenhoang9x5555 183:16414698889c 575 } else if (!CheckRfFrequency(freq)) {
nguyenhoang9x5555 183:16414698889c 576 logInfo("Freq KO");
nguyenhoang9x5555 183:16414698889c 577 status &= 0xFE; // Channel frequency KO
nguyenhoang9x5555 183:16414698889c 578 }
nguyenhoang9x5555 183:16414698889c 579
nguyenhoang9x5555 183:16414698889c 580 if (status & 0x01) {
nguyenhoang9x5555 183:16414698889c 581 logInfo("BeaconFrequencyReq accepted Freq: %d", freq);
nguyenhoang9x5555 183:16414698889c 582 GetSettings()->Session.BeaconFrequency = freq;
nguyenhoang9x5555 183:16414698889c 583 } else {
nguyenhoang9x5555 183:16414698889c 584 logInfo("BeaconFrequencyReq rejected Freq: %d", freq);
nguyenhoang9x5555 183:16414698889c 585 }
nguyenhoang9x5555 183:16414698889c 586
nguyenhoang9x5555 183:16414698889c 587 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 588 }
nguyenhoang9x5555 183:16414698889c 589
nguyenhoang9x5555 183:16414698889c 590 uint8_t ChannelPlan_VI919::HandleAdrCommand(const uint8_t* payload, uint8_t index, uint8_t size, uint8_t& status) {
nguyenhoang9x5555 183:16414698889c 591
nguyenhoang9x5555 183:16414698889c 592 uint8_t power = 0;
nguyenhoang9x5555 183:16414698889c 593 uint8_t datarate = 0;
nguyenhoang9x5555 183:16414698889c 594 uint16_t mask = 0;
nguyenhoang9x5555 183:16414698889c 595 uint16_t new_mask = 0;
nguyenhoang9x5555 183:16414698889c 596 uint8_t ctrl = 0;
nguyenhoang9x5555 183:16414698889c 597 uint8_t nbRep = 0;
nguyenhoang9x5555 183:16414698889c 598
nguyenhoang9x5555 183:16414698889c 599 status = 0x07;
nguyenhoang9x5555 183:16414698889c 600 datarate = payload[index++];
nguyenhoang9x5555 183:16414698889c 601 power = datarate & 0x0F;
nguyenhoang9x5555 183:16414698889c 602 datarate = (datarate >> 4) & 0x0F;
nguyenhoang9x5555 183:16414698889c 603
nguyenhoang9x5555 183:16414698889c 604 mask = payload[index++];
nguyenhoang9x5555 183:16414698889c 605 mask |= payload[index++] << 8;
nguyenhoang9x5555 183:16414698889c 606
nguyenhoang9x5555 183:16414698889c 607 nbRep = payload[index++];
nguyenhoang9x5555 183:16414698889c 608 ctrl = (nbRep >> 4) & 0x07;
nguyenhoang9x5555 183:16414698889c 609 nbRep &= 0x0F;
nguyenhoang9x5555 183:16414698889c 610
nguyenhoang9x5555 183:16414698889c 611 if (nbRep == 0) {
nguyenhoang9x5555 183:16414698889c 612 nbRep = 1;
nguyenhoang9x5555 183:16414698889c 613 }
nguyenhoang9x5555 183:16414698889c 614
nguyenhoang9x5555 183:16414698889c 615 if (datarate > _maxDatarate) {
nguyenhoang9x5555 183:16414698889c 616 status &= 0xFD; // Datarate KO
nguyenhoang9x5555 183:16414698889c 617 }
nguyenhoang9x5555 183:16414698889c 618 //
nguyenhoang9x5555 183:16414698889c 619 // Remark MaxTxPower = 0 and MinTxPower = 7
nguyenhoang9x5555 183:16414698889c 620 //
nguyenhoang9x5555 183:16414698889c 621 if (power > 7) {
nguyenhoang9x5555 183:16414698889c 622 status &= 0xFB; // TxPower KO
nguyenhoang9x5555 183:16414698889c 623 }
nguyenhoang9x5555 183:16414698889c 624
nguyenhoang9x5555 183:16414698889c 625 switch (ctrl) {
nguyenhoang9x5555 183:16414698889c 626 case 0:
nguyenhoang9x5555 183:16414698889c 627 SetChannelMask(0, mask);
nguyenhoang9x5555 183:16414698889c 628 break;
nguyenhoang9x5555 183:16414698889c 629
nguyenhoang9x5555 183:16414698889c 630 case 6:
nguyenhoang9x5555 183:16414698889c 631 // enable all currently defined channels
nguyenhoang9x5555 183:16414698889c 632 // set bits 0 - N of a number by (2<<N)-1
nguyenhoang9x5555 183:16414698889c 633 new_mask = (1 << _channels.size()) - 1;
nguyenhoang9x5555 183:16414698889c 634 SetChannelMask(0, new_mask);
nguyenhoang9x5555 183:16414698889c 635 break;
nguyenhoang9x5555 183:16414698889c 636
nguyenhoang9x5555 183:16414698889c 637 default:
nguyenhoang9x5555 183:16414698889c 638 logWarning("rejecting RFU or unknown control value %d", ctrl);
nguyenhoang9x5555 183:16414698889c 639 status &= 0xFE; // ChannelMask KO
nguyenhoang9x5555 183:16414698889c 640 return LORA_ERROR;
nguyenhoang9x5555 183:16414698889c 641 }
nguyenhoang9x5555 183:16414698889c 642
nguyenhoang9x5555 183:16414698889c 643 if (GetSettings()->Network.ADREnabled) {
nguyenhoang9x5555 183:16414698889c 644 GetSettings()->Session.TxDatarate = datarate;
nguyenhoang9x5555 183:16414698889c 645 GetSettings()->Session.TxPower = GetSettings()->Session.Max_EIRP - (power * 2);
nguyenhoang9x5555 183:16414698889c 646 GetSettings()->Session.Redundancy = nbRep;
nguyenhoang9x5555 183:16414698889c 647 } else {
nguyenhoang9x5555 183:16414698889c 648 logDebug("ADR is disabled, DR and Power not changed.");
nguyenhoang9x5555 183:16414698889c 649 status &= 0xFB; // TxPower KO
nguyenhoang9x5555 183:16414698889c 650 status &= 0xFD; // Datarate KO
nguyenhoang9x5555 183:16414698889c 651 }
nguyenhoang9x5555 183:16414698889c 652
nguyenhoang9x5555 183:16414698889c 653 logDebug("ADR DR: %u PWR: %u Ctrl: %02x Mask: %04x NbRep: %u Stat: %02x", datarate, power, ctrl, mask, nbRep, status);
nguyenhoang9x5555 183:16414698889c 654
nguyenhoang9x5555 183:16414698889c 655 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 656 }
nguyenhoang9x5555 183:16414698889c 657
nguyenhoang9x5555 183:16414698889c 658 uint8_t ChannelPlan_VI919::ValidateAdrConfiguration() {
nguyenhoang9x5555 183:16414698889c 659 uint8_t status = 0x07;
nguyenhoang9x5555 183:16414698889c 660 uint8_t datarate = GetSettings()->Session.TxDatarate;
nguyenhoang9x5555 183:16414698889c 661 uint8_t power = GetSettings()->Session.TxPower;
nguyenhoang9x5555 183:16414698889c 662
nguyenhoang9x5555 183:16414698889c 663 if (!GetSettings()->Network.ADREnabled) {
nguyenhoang9x5555 183:16414698889c 664 logDebug("ADR disabled - no applied changes to validate");
nguyenhoang9x5555 183:16414698889c 665 return status;
nguyenhoang9x5555 183:16414698889c 666 }
nguyenhoang9x5555 183:16414698889c 667
nguyenhoang9x5555 183:16414698889c 668 if (datarate > _maxDatarate) {
nguyenhoang9x5555 183:16414698889c 669 logWarning("ADR Datarate KO - outside allowed range");
nguyenhoang9x5555 183:16414698889c 670 status &= 0xFD; // Datarate KO
nguyenhoang9x5555 183:16414698889c 671 }
nguyenhoang9x5555 183:16414698889c 672
nguyenhoang9x5555 183:16414698889c 673 if (GetSettings()->Session.UplinkDwelltime != 0 && datarate < DR_2) {
nguyenhoang9x5555 183:16414698889c 674 logWarning("ADR Datarate KO - TxDwelltime != 0 and DR < 2");
nguyenhoang9x5555 183:16414698889c 675 status &= 0xFD; // Datarate KO
nguyenhoang9x5555 183:16414698889c 676 }
nguyenhoang9x5555 183:16414698889c 677
nguyenhoang9x5555 183:16414698889c 678 if (power < _minTxPower || power > _maxTxPower) {
nguyenhoang9x5555 183:16414698889c 679 logWarning("ADR TX Power KO - outside allowed range");
nguyenhoang9x5555 183:16414698889c 680 status &= 0xFB; // TxPower KO
nguyenhoang9x5555 183:16414698889c 681 }
nguyenhoang9x5555 183:16414698889c 682
nguyenhoang9x5555 183:16414698889c 683 // mask must not contain any undefined channels
nguyenhoang9x5555 183:16414698889c 684 for (int i = 2; i < 16; i++) {
nguyenhoang9x5555 183:16414698889c 685 if ((_channelMask[0] & (1 << i)) && (_channels[i].Frequency == 0)) {
nguyenhoang9x5555 183:16414698889c 686 logWarning("ADR Channel Mask KO - cannot enable undefined channel");
nguyenhoang9x5555 183:16414698889c 687 status &= 0xFE; // ChannelMask KO
nguyenhoang9x5555 183:16414698889c 688 break;
nguyenhoang9x5555 183:16414698889c 689 }
nguyenhoang9x5555 183:16414698889c 690 }
nguyenhoang9x5555 183:16414698889c 691
nguyenhoang9x5555 183:16414698889c 692 return status;
nguyenhoang9x5555 183:16414698889c 693 }
nguyenhoang9x5555 183:16414698889c 694
nguyenhoang9x5555 183:16414698889c 695 uint8_t ChannelPlan_VI919::HandleAckTimeout() {
nguyenhoang9x5555 183:16414698889c 696
nguyenhoang9x5555 183:16414698889c 697 if (!GetSettings()->Network.ADREnabled) {
nguyenhoang9x5555 183:16414698889c 698 return LORA_ADR_OFF;
nguyenhoang9x5555 183:16414698889c 699 }
nguyenhoang9x5555 183:16414698889c 700
nguyenhoang9x5555 183:16414698889c 701 if ((++(GetSettings()->Session.AckCounter) % 2) == 0) {
nguyenhoang9x5555 183:16414698889c 702 if (GetSettings()->Session.TxPower < GetSettings()->Network.TxPowerMax) {
nguyenhoang9x5555 183:16414698889c 703 logTrace("ADR Setting power to maximum");
nguyenhoang9x5555 183:16414698889c 704 GetSettings()->Session.TxPower = GetSettings()->Network.TxPowerMax;
nguyenhoang9x5555 183:16414698889c 705 } else if (GetSettings()->Session.TxDatarate > 0) {
nguyenhoang9x5555 183:16414698889c 706 logTrace("ADR Lowering datarate");
nguyenhoang9x5555 183:16414698889c 707 GetSettings()->Session.TxDatarate--;
nguyenhoang9x5555 183:16414698889c 708 }
nguyenhoang9x5555 183:16414698889c 709 }
nguyenhoang9x5555 183:16414698889c 710
nguyenhoang9x5555 183:16414698889c 711 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 712 }
nguyenhoang9x5555 183:16414698889c 713
nguyenhoang9x5555 183:16414698889c 714
nguyenhoang9x5555 183:16414698889c 715 uint32_t ChannelPlan_VI919::GetTimeOffAir()
nguyenhoang9x5555 183:16414698889c 716 {
nguyenhoang9x5555 183:16414698889c 717 if (GetSettings()->Test.DisableDutyCycle == lora::ON)
nguyenhoang9x5555 183:16414698889c 718 return 0;
nguyenhoang9x5555 183:16414698889c 719
nguyenhoang9x5555 183:16414698889c 720 uint32_t min = 0;
nguyenhoang9x5555 183:16414698889c 721 uint32_t now = _dutyCycleTimer.read_ms();
nguyenhoang9x5555 183:16414698889c 722
nguyenhoang9x5555 183:16414698889c 723
nguyenhoang9x5555 183:16414698889c 724 min = UINT_MAX;
nguyenhoang9x5555 183:16414698889c 725 int8_t band = 0;
nguyenhoang9x5555 183:16414698889c 726
nguyenhoang9x5555 183:16414698889c 727 if (P2PEnabled()) {
nguyenhoang9x5555 183:16414698889c 728 int8_t band = GetDutyBand(GetSettings()->Network.TxFrequency);
nguyenhoang9x5555 183:16414698889c 729 if (_dutyBands[band].TimeOffEnd > now) {
nguyenhoang9x5555 183:16414698889c 730 min = _dutyBands[band].TimeOffEnd - now;
nguyenhoang9x5555 183:16414698889c 731 } else {
nguyenhoang9x5555 183:16414698889c 732 min = 0;
nguyenhoang9x5555 183:16414698889c 733 }
nguyenhoang9x5555 183:16414698889c 734 } else {
nguyenhoang9x5555 183:16414698889c 735 for (size_t i = 0; i < _channels.size(); i++) {
nguyenhoang9x5555 183:16414698889c 736 if (IsChannelEnabled(i) && GetChannel(i).Frequency != 0 &&
nguyenhoang9x5555 183:16414698889c 737 !(GetSettings()->Session.TxDatarate < GetChannel(i).DrRange.Fields.Min ||
nguyenhoang9x5555 183:16414698889c 738 GetSettings()->Session.TxDatarate > GetChannel(i).DrRange.Fields.Max)) {
nguyenhoang9x5555 183:16414698889c 739
nguyenhoang9x5555 183:16414698889c 740 band = GetDutyBand(GetChannel(i).Frequency);
nguyenhoang9x5555 183:16414698889c 741 if (band != -1) {
nguyenhoang9x5555 183:16414698889c 742 // logDebug("band: %d time-off: %d now: %d", band, _dutyBands[band].TimeOffEnd, now);
nguyenhoang9x5555 183:16414698889c 743 if (_dutyBands[band].TimeOffEnd > now) {
nguyenhoang9x5555 183:16414698889c 744 min = std::min < uint32_t > (min, _dutyBands[band].TimeOffEnd - now);
nguyenhoang9x5555 183:16414698889c 745 } else {
nguyenhoang9x5555 183:16414698889c 746 min = 0;
nguyenhoang9x5555 183:16414698889c 747 break;
nguyenhoang9x5555 183:16414698889c 748 }
nguyenhoang9x5555 183:16414698889c 749 }
nguyenhoang9x5555 183:16414698889c 750 }
nguyenhoang9x5555 183:16414698889c 751 }
nguyenhoang9x5555 183:16414698889c 752 }
nguyenhoang9x5555 183:16414698889c 753
nguyenhoang9x5555 183:16414698889c 754
nguyenhoang9x5555 183:16414698889c 755 if (GetSettings()->Session.AggregatedTimeOffEnd > 0 && GetSettings()->Session.AggregatedTimeOffEnd > now) {
nguyenhoang9x5555 183:16414698889c 756 min = std::max < uint32_t > (min, GetSettings()->Session.AggregatedTimeOffEnd - now);
nguyenhoang9x5555 183:16414698889c 757 }
nguyenhoang9x5555 183:16414698889c 758
nguyenhoang9x5555 183:16414698889c 759 now = time(NULL);
nguyenhoang9x5555 183:16414698889c 760 uint32_t join_time = 0;
nguyenhoang9x5555 183:16414698889c 761
nguyenhoang9x5555 183:16414698889c 762 if (GetSettings()->Session.JoinFirstAttempt != 0 && now < GetSettings()->Session.JoinTimeOffEnd) {
nguyenhoang9x5555 183:16414698889c 763 join_time = (GetSettings()->Session.JoinTimeOffEnd - now) * 1000;
nguyenhoang9x5555 183:16414698889c 764 }
nguyenhoang9x5555 183:16414698889c 765
nguyenhoang9x5555 183:16414698889c 766 min = std::max < uint32_t > (join_time, min);
nguyenhoang9x5555 183:16414698889c 767
nguyenhoang9x5555 183:16414698889c 768 return min;
nguyenhoang9x5555 183:16414698889c 769 }
nguyenhoang9x5555 183:16414698889c 770
nguyenhoang9x5555 183:16414698889c 771
nguyenhoang9x5555 183:16414698889c 772 void ChannelPlan_VI919::UpdateDutyCycle(uint32_t freq, uint32_t time_on_air_ms) {
nguyenhoang9x5555 183:16414698889c 773 if (GetSettings()->Test.DisableDutyCycle == lora::ON) {
nguyenhoang9x5555 183:16414698889c 774 _dutyCycleTimer.stop();
nguyenhoang9x5555 183:16414698889c 775 for (size_t i = 0; i < _dutyBands.size(); i++) {
nguyenhoang9x5555 183:16414698889c 776 _dutyBands[i].TimeOffEnd = 0;
nguyenhoang9x5555 183:16414698889c 777 }
nguyenhoang9x5555 183:16414698889c 778 return;
nguyenhoang9x5555 183:16414698889c 779 }
nguyenhoang9x5555 183:16414698889c 780
nguyenhoang9x5555 183:16414698889c 781 _dutyCycleTimer.start();
nguyenhoang9x5555 183:16414698889c 782
nguyenhoang9x5555 183:16414698889c 783 if (GetSettings()->Session.MaxDutyCycle > 0 && GetSettings()->Session.MaxDutyCycle <= 15) {
nguyenhoang9x5555 183:16414698889c 784 GetSettings()->Session.AggregatedTimeOffEnd = _dutyCycleTimer.read_ms() + time_on_air_ms * GetSettings()->Session.AggregateDutyCycle;
nguyenhoang9x5555 183:16414698889c 785 logDebug("Updated Aggregate DCycle Time-off: %lu DC: %f%%", GetSettings()->Session.AggregatedTimeOffEnd, 1 / float(GetSettings()->Session.AggregateDutyCycle));
nguyenhoang9x5555 183:16414698889c 786 } else {
nguyenhoang9x5555 183:16414698889c 787 GetSettings()->Session.AggregatedTimeOffEnd = 0;
nguyenhoang9x5555 183:16414698889c 788 }
nguyenhoang9x5555 183:16414698889c 789
nguyenhoang9x5555 183:16414698889c 790
nguyenhoang9x5555 183:16414698889c 791 uint32_t time_off_air = 0;
nguyenhoang9x5555 183:16414698889c 792 uint32_t now = _dutyCycleTimer.read_ms();
nguyenhoang9x5555 183:16414698889c 793
nguyenhoang9x5555 183:16414698889c 794 for (size_t i = 0; i < _dutyBands.size(); i++) {
nguyenhoang9x5555 183:16414698889c 795 if (_dutyBands[i].TimeOffEnd < now) {
nguyenhoang9x5555 183:16414698889c 796 _dutyBands[i].TimeOffEnd = 0;
nguyenhoang9x5555 183:16414698889c 797 } else {
nguyenhoang9x5555 183:16414698889c 798 _dutyBands[i].TimeOffEnd -= now;
nguyenhoang9x5555 183:16414698889c 799 }
nguyenhoang9x5555 183:16414698889c 800
nguyenhoang9x5555 183:16414698889c 801 if (freq >= _dutyBands[i].FrequencyMin && freq <= _dutyBands[i].FrequencyMax) {
nguyenhoang9x5555 183:16414698889c 802 logDebug("update TOE: freq: %d i:%d toa: %d DC:%d", freq, i, time_on_air_ms, _dutyBands[i].DutyCycle);
nguyenhoang9x5555 183:16414698889c 803
nguyenhoang9x5555 183:16414698889c 804 if (freq > _minFrequency && freq < _maxFrequency && (GetSettings()->Session.TxPower + GetSettings()->Network.AntennaGain) <= 7) {
nguyenhoang9x5555 183:16414698889c 805 _dutyBands[i].TimeOffEnd = 0;
nguyenhoang9x5555 183:16414698889c 806 } else {
nguyenhoang9x5555 183:16414698889c 807 time_off_air = time_on_air_ms * _dutyBands[i].DutyCycle;
nguyenhoang9x5555 183:16414698889c 808 _dutyBands[i].TimeOffEnd = time_off_air;
nguyenhoang9x5555 183:16414698889c 809 }
nguyenhoang9x5555 183:16414698889c 810 }
nguyenhoang9x5555 183:16414698889c 811 }
nguyenhoang9x5555 183:16414698889c 812
nguyenhoang9x5555 183:16414698889c 813
nguyenhoang9x5555 183:16414698889c 814 ResetDutyCycleTimer();
nguyenhoang9x5555 183:16414698889c 815 }
nguyenhoang9x5555 183:16414698889c 816
nguyenhoang9x5555 183:16414698889c 817 std::vector<uint32_t> lora::ChannelPlan_VI919::GetChannels() {
nguyenhoang9x5555 183:16414698889c 818 std::vector < uint32_t > chans;
nguyenhoang9x5555 183:16414698889c 819
nguyenhoang9x5555 183:16414698889c 820 for (int8_t i = 0; i < (int) _channels.size(); i++) {
nguyenhoang9x5555 183:16414698889c 821 chans.push_back(_channels[i].Frequency);
nguyenhoang9x5555 183:16414698889c 822 }
nguyenhoang9x5555 183:16414698889c 823 chans.push_back(GetRxWindow(2).Frequency);
nguyenhoang9x5555 183:16414698889c 824
nguyenhoang9x5555 183:16414698889c 825 return chans;
nguyenhoang9x5555 183:16414698889c 826 }
nguyenhoang9x5555 183:16414698889c 827
nguyenhoang9x5555 183:16414698889c 828 std::vector<uint8_t> lora::ChannelPlan_VI919::GetChannelRanges() {
nguyenhoang9x5555 183:16414698889c 829 std::vector < uint8_t > ranges;
nguyenhoang9x5555 183:16414698889c 830
nguyenhoang9x5555 183:16414698889c 831 for (int8_t i = 0; i < (int) _channels.size(); i++) {
nguyenhoang9x5555 183:16414698889c 832 ranges.push_back(_channels[i].DrRange.Value);
nguyenhoang9x5555 183:16414698889c 833 }
nguyenhoang9x5555 183:16414698889c 834
nguyenhoang9x5555 183:16414698889c 835 ranges.push_back(GetRxWindow(2).DatarateIndex);
nguyenhoang9x5555 183:16414698889c 836
nguyenhoang9x5555 183:16414698889c 837 return ranges;
nguyenhoang9x5555 183:16414698889c 838
nguyenhoang9x5555 183:16414698889c 839 }
nguyenhoang9x5555 183:16414698889c 840
nguyenhoang9x5555 183:16414698889c 841 void lora::ChannelPlan_VI919::EnableDefaultChannels() {
nguyenhoang9x5555 183:16414698889c 842 _channelMask[0] |= 0x0003;
nguyenhoang9x5555 183:16414698889c 843 }
nguyenhoang9x5555 183:16414698889c 844
nguyenhoang9x5555 183:16414698889c 845 uint8_t ChannelPlan_VI919::GetNextChannel()
nguyenhoang9x5555 183:16414698889c 846 {
nguyenhoang9x5555 183:16414698889c 847 if (GetSettings()->Session.AggregatedTimeOffEnd != 0) {
nguyenhoang9x5555 183:16414698889c 848 return LORA_AGGREGATED_DUTY_CYCLE;
nguyenhoang9x5555 183:16414698889c 849 }
nguyenhoang9x5555 183:16414698889c 850
nguyenhoang9x5555 183:16414698889c 851 if (P2PEnabled() || GetSettings()->Network.TxFrequency != 0) {
nguyenhoang9x5555 183:16414698889c 852 logDebug("Using frequency %d", GetSettings()->Network.TxFrequency);
nguyenhoang9x5555 183:16414698889c 853
nguyenhoang9x5555 183:16414698889c 854 if (GetSettings()->Test.DisableDutyCycle != lora::ON) {
nguyenhoang9x5555 183:16414698889c 855 int8_t band = GetDutyBand(GetSettings()->Network.TxFrequency);
nguyenhoang9x5555 183:16414698889c 856 logDebug("band: %d freq: %d", band, GetSettings()->Network.TxFrequency);
nguyenhoang9x5555 183:16414698889c 857 if (band != -1 && _dutyBands[band].TimeOffEnd != 0) {
nguyenhoang9x5555 183:16414698889c 858 return LORA_NO_CHANS_ENABLED;
nguyenhoang9x5555 183:16414698889c 859 }
nguyenhoang9x5555 183:16414698889c 860 }
nguyenhoang9x5555 183:16414698889c 861
nguyenhoang9x5555 183:16414698889c 862 GetRadio()->SetChannel(GetSettings()->Network.TxFrequency);
nguyenhoang9x5555 183:16414698889c 863 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 864 }
nguyenhoang9x5555 183:16414698889c 865
nguyenhoang9x5555 183:16414698889c 866 uint8_t start = 0;
nguyenhoang9x5555 183:16414698889c 867 uint8_t maxChannels = _numChans125k;
nguyenhoang9x5555 183:16414698889c 868 uint8_t nbEnabledChannels = 0;
nguyenhoang9x5555 183:16414698889c 869 uint8_t *enabledChannels = new uint8_t[maxChannels];
nguyenhoang9x5555 183:16414698889c 870
nguyenhoang9x5555 183:16414698889c 871 if (GetTxDatarate().Bandwidth == BW_500) {
nguyenhoang9x5555 183:16414698889c 872 maxChannels = _numChans500k;
nguyenhoang9x5555 183:16414698889c 873 start = _numChans125k;
nguyenhoang9x5555 183:16414698889c 874 }
nguyenhoang9x5555 183:16414698889c 875
nguyenhoang9x5555 183:16414698889c 876 // Search how many channels are enabled
nguyenhoang9x5555 183:16414698889c 877 DatarateRange range;
nguyenhoang9x5555 183:16414698889c 878 uint8_t dr_index = GetSettings()->Session.TxDatarate;
nguyenhoang9x5555 183:16414698889c 879 uint32_t now = _dutyCycleTimer.read_ms();
nguyenhoang9x5555 183:16414698889c 880
nguyenhoang9x5555 183:16414698889c 881 for (size_t i = 0; i < _dutyBands.size(); i++) {
nguyenhoang9x5555 183:16414698889c 882 if (_dutyBands[i].TimeOffEnd < now || GetSettings()->Test.DisableDutyCycle == lora::ON) {
nguyenhoang9x5555 183:16414698889c 883 _dutyBands[i].TimeOffEnd = 0;
nguyenhoang9x5555 183:16414698889c 884 }
nguyenhoang9x5555 183:16414698889c 885 }
nguyenhoang9x5555 183:16414698889c 886
nguyenhoang9x5555 183:16414698889c 887 for (uint8_t i = start; i < start + maxChannels; i++) {
nguyenhoang9x5555 183:16414698889c 888 range = GetChannel(i).DrRange;
nguyenhoang9x5555 183:16414698889c 889 // logDebug("chan: %d freq: %d range:%02x", i, GetChannel(i).Frequency, range.Value);
nguyenhoang9x5555 183:16414698889c 890
nguyenhoang9x5555 183:16414698889c 891 if (IsChannelEnabled(i) && (dr_index >= range.Fields.Min && dr_index <= range.Fields.Max)) {
nguyenhoang9x5555 183:16414698889c 892 int8_t band = GetDutyBand(GetChannel(i).Frequency);
nguyenhoang9x5555 183:16414698889c 893 // logDebug("band: %d freq: %d", band, _channels[i].Frequency);
nguyenhoang9x5555 183:16414698889c 894 if (band != -1 && _dutyBands[band].TimeOffEnd == 0) {
nguyenhoang9x5555 183:16414698889c 895 enabledChannels[nbEnabledChannels++] = i;
nguyenhoang9x5555 183:16414698889c 896 }
nguyenhoang9x5555 183:16414698889c 897 }
nguyenhoang9x5555 183:16414698889c 898 }
nguyenhoang9x5555 183:16414698889c 899
nguyenhoang9x5555 183:16414698889c 900 logTrace("Number of available channels: %d", nbEnabledChannels);
nguyenhoang9x5555 183:16414698889c 901
nguyenhoang9x5555 183:16414698889c 902 uint32_t freq = 0;
nguyenhoang9x5555 183:16414698889c 903 uint8_t sf = GetTxDatarate().SpreadingFactor;
nguyenhoang9x5555 183:16414698889c 904 uint8_t bw = GetTxDatarate().Bandwidth;
nguyenhoang9x5555 183:16414698889c 905 int16_t thres = DEFAULT_FREE_CHAN_RSSI_THRESHOLD;
nguyenhoang9x5555 183:16414698889c 906
nguyenhoang9x5555 183:16414698889c 907 if (nbEnabledChannels == 0) {
nguyenhoang9x5555 183:16414698889c 908 delete [] enabledChannels;
nguyenhoang9x5555 183:16414698889c 909 return LORA_NO_CHANS_ENABLED;
nguyenhoang9x5555 183:16414698889c 910 }
nguyenhoang9x5555 183:16414698889c 911
nguyenhoang9x5555 183:16414698889c 912 if (GetSettings()->Network.CADEnabled) {
nguyenhoang9x5555 183:16414698889c 913 // Search for free channel with ms timeout
nguyenhoang9x5555 183:16414698889c 914 int16_t timeout = 10000;
nguyenhoang9x5555 183:16414698889c 915 Timer tmr;
nguyenhoang9x5555 183:16414698889c 916 tmr.start();
nguyenhoang9x5555 183:16414698889c 917
nguyenhoang9x5555 183:16414698889c 918 for (uint8_t j = rand_r(0, nbEnabledChannels - 1); tmr.read_ms() < timeout; j++) {
nguyenhoang9x5555 183:16414698889c 919 freq = GetChannel(enabledChannels[j]).Frequency;
nguyenhoang9x5555 183:16414698889c 920
nguyenhoang9x5555 183:16414698889c 921 // Listen before talk
nguyenhoang9x5555 183:16414698889c 922 if (GetRadio()->IsChannelFree(SxRadio::MODEM_LORA, freq, sf, thres, bw)) {
nguyenhoang9x5555 183:16414698889c 923 _txChannel = enabledChannels[j];
nguyenhoang9x5555 183:16414698889c 924 break;
nguyenhoang9x5555 183:16414698889c 925 }
nguyenhoang9x5555 183:16414698889c 926 }
nguyenhoang9x5555 183:16414698889c 927 } else {
nguyenhoang9x5555 183:16414698889c 928 uint8_t j = rand_r(0, nbEnabledChannels - 1);
nguyenhoang9x5555 183:16414698889c 929 _txChannel = enabledChannels[j];
nguyenhoang9x5555 183:16414698889c 930 freq = GetChannel(_txChannel).Frequency;
nguyenhoang9x5555 183:16414698889c 931 }
nguyenhoang9x5555 183:16414698889c 932
nguyenhoang9x5555 183:16414698889c 933 assert(freq != 0);
nguyenhoang9x5555 183:16414698889c 934
nguyenhoang9x5555 183:16414698889c 935 logDebug("Using channel %d : %d", _txChannel, freq);
nguyenhoang9x5555 183:16414698889c 936 GetRadio()->SetChannel(freq);
nguyenhoang9x5555 183:16414698889c 937
nguyenhoang9x5555 183:16414698889c 938 delete [] enabledChannels;
nguyenhoang9x5555 183:16414698889c 939 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 940 }
nguyenhoang9x5555 183:16414698889c 941
nguyenhoang9x5555 183:16414698889c 942
nguyenhoang9x5555 183:16414698889c 943 uint8_t lora::ChannelPlan_VI919::GetJoinDatarate() {
nguyenhoang9x5555 183:16414698889c 944 uint8_t dr = GetSettings()->Session.TxDatarate;
nguyenhoang9x5555 183:16414698889c 945
nguyenhoang9x5555 183:16414698889c 946 // Default join datarate is DR2:SF10BW125
nguyenhoang9x5555 183:16414698889c 947 dr = lora::DR_2;
nguyenhoang9x5555 183:16414698889c 948
nguyenhoang9x5555 183:16414698889c 949 return dr;
nguyenhoang9x5555 183:16414698889c 950 }
nguyenhoang9x5555 183:16414698889c 951
nguyenhoang9x5555 183:16414698889c 952 uint8_t ChannelPlan_VI919::CalculateJoinBackoff(uint8_t size) {
nguyenhoang9x5555 183:16414698889c 953
nguyenhoang9x5555 183:16414698889c 954 time_t now = time(NULL);
nguyenhoang9x5555 183:16414698889c 955 uint32_t time_on_max = 0;
nguyenhoang9x5555 183:16414698889c 956 static uint32_t time_off_max = 15;
nguyenhoang9x5555 183:16414698889c 957 uint32_t rand_time_off = 0;
nguyenhoang9x5555 183:16414698889c 958
nguyenhoang9x5555 183:16414698889c 959 // TODO: calc time-off-max based on RTC time from JoinFirstAttempt, time-off-max is lost over sleep
nguyenhoang9x5555 183:16414698889c 960
nguyenhoang9x5555 183:16414698889c 961 if ((time_t)GetSettings()->Session.JoinTimeOffEnd > now) {
nguyenhoang9x5555 183:16414698889c 962 return LORA_JOIN_BACKOFF;
nguyenhoang9x5555 183:16414698889c 963 }
nguyenhoang9x5555 183:16414698889c 964
nguyenhoang9x5555 183:16414698889c 965 uint32_t secs_since_first_attempt = (now - GetSettings()->Session.JoinFirstAttempt);
nguyenhoang9x5555 183:16414698889c 966 uint16_t hours_since_first_attempt = secs_since_first_attempt / (60 * 60);
nguyenhoang9x5555 183:16414698889c 967
nguyenhoang9x5555 183:16414698889c 968 static uint8_t join_cnt = 0;
nguyenhoang9x5555 183:16414698889c 969
nguyenhoang9x5555 183:16414698889c 970 join_cnt = (join_cnt+1) % 8;
nguyenhoang9x5555 183:16414698889c 971
nguyenhoang9x5555 183:16414698889c 972 if (GetSettings()->Session.JoinFirstAttempt == 0) {
nguyenhoang9x5555 183:16414698889c 973 /* 1 % duty-cycle for first hour
nguyenhoang9x5555 183:16414698889c 974 * 0.1 % next 10 hours
nguyenhoang9x5555 183:16414698889c 975 * 0.01 % upto 24 hours */
nguyenhoang9x5555 183:16414698889c 976 GetSettings()->Session.JoinFirstAttempt = now;
nguyenhoang9x5555 183:16414698889c 977 GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
nguyenhoang9x5555 183:16414698889c 978 GetSettings()->Session.JoinTimeOffEnd = now + (GetTimeOnAir(size) / 10);
nguyenhoang9x5555 183:16414698889c 979 } else if (join_cnt == 0) {
nguyenhoang9x5555 183:16414698889c 980 if (hours_since_first_attempt < 1) {
nguyenhoang9x5555 183:16414698889c 981 time_on_max = 36000;
nguyenhoang9x5555 183:16414698889c 982 rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
nguyenhoang9x5555 183:16414698889c 983 // time off max 1 hour
nguyenhoang9x5555 183:16414698889c 984 time_off_max = std::min < uint32_t > (time_off_max * 2, 60 * 60);
nguyenhoang9x5555 183:16414698889c 985
nguyenhoang9x5555 183:16414698889c 986 if (GetSettings()->Session.JoinTimeOnAir < time_on_max) {
nguyenhoang9x5555 183:16414698889c 987 GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
nguyenhoang9x5555 183:16414698889c 988 GetSettings()->Session.JoinTimeOffEnd = now + rand_time_off;
nguyenhoang9x5555 183:16414698889c 989 } else {
nguyenhoang9x5555 183:16414698889c 990 logWarning("Max time-on-air limit met for current join backoff period");
nguyenhoang9x5555 183:16414698889c 991 GetSettings()->Session.JoinTimeOffEnd = GetSettings()->Session.JoinFirstAttempt + 60 * 60;
nguyenhoang9x5555 183:16414698889c 992 }
nguyenhoang9x5555 183:16414698889c 993 } else if (hours_since_first_attempt < 11) {
nguyenhoang9x5555 183:16414698889c 994 if (GetSettings()->Session.JoinTimeOnAir < 36000) {
nguyenhoang9x5555 183:16414698889c 995 GetSettings()->Session.JoinTimeOnAir = 36000;
nguyenhoang9x5555 183:16414698889c 996 }
nguyenhoang9x5555 183:16414698889c 997 time_on_max = 72000;
nguyenhoang9x5555 183:16414698889c 998 rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
nguyenhoang9x5555 183:16414698889c 999 // time off max 1 hour
nguyenhoang9x5555 183:16414698889c 1000 time_off_max = std::min < uint32_t > (time_off_max * 2, 60 * 60);
nguyenhoang9x5555 183:16414698889c 1001
nguyenhoang9x5555 183:16414698889c 1002 if (GetSettings()->Session.JoinTimeOnAir < time_on_max) {
nguyenhoang9x5555 183:16414698889c 1003 GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
nguyenhoang9x5555 183:16414698889c 1004 GetSettings()->Session.JoinTimeOffEnd = now + rand_time_off;
nguyenhoang9x5555 183:16414698889c 1005 } else {
nguyenhoang9x5555 183:16414698889c 1006 logWarning("Max time-on-air limit met for current join backoff period");
nguyenhoang9x5555 183:16414698889c 1007 GetSettings()->Session.JoinTimeOffEnd = GetSettings()->Session.JoinFirstAttempt + 11 * 60 * 60;
nguyenhoang9x5555 183:16414698889c 1008 }
nguyenhoang9x5555 183:16414698889c 1009 } else {
nguyenhoang9x5555 183:16414698889c 1010 if (GetSettings()->Session.JoinTimeOnAir < 72000) {
nguyenhoang9x5555 183:16414698889c 1011 GetSettings()->Session.JoinTimeOnAir = 72000;
nguyenhoang9x5555 183:16414698889c 1012 }
nguyenhoang9x5555 183:16414698889c 1013 uint32_t join_time = 2500;
nguyenhoang9x5555 183:16414698889c 1014
nguyenhoang9x5555 183:16414698889c 1015 time_on_max = 80700;
nguyenhoang9x5555 183:16414698889c 1016 time_off_max = 1 * 60 * 60; // 1 hour
nguyenhoang9x5555 183:16414698889c 1017 rand_time_off = rand_r(time_off_max - 1, time_off_max + 1);
nguyenhoang9x5555 183:16414698889c 1018
nguyenhoang9x5555 183:16414698889c 1019 if (GetSettings()->Session.JoinTimeOnAir < time_on_max - join_time) {
nguyenhoang9x5555 183:16414698889c 1020 GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
nguyenhoang9x5555 183:16414698889c 1021 GetSettings()->Session.JoinTimeOffEnd = now + rand_time_off;
nguyenhoang9x5555 183:16414698889c 1022 } else {
nguyenhoang9x5555 183:16414698889c 1023 logWarning("Max time-on-air limit met for current join backoff period");
nguyenhoang9x5555 183:16414698889c 1024 // Reset the join time on air and set end of restriction to the next 24 hour period
nguyenhoang9x5555 183:16414698889c 1025 GetSettings()->Session.JoinTimeOnAir = 72000;
nguyenhoang9x5555 183:16414698889c 1026 uint16_t days = (now - GetSettings()->Session.JoinFirstAttempt) / (24 * 60 * 60) + 1;
nguyenhoang9x5555 183:16414698889c 1027 logWarning("days : %d", days);
nguyenhoang9x5555 183:16414698889c 1028 GetSettings()->Session.JoinTimeOffEnd = GetSettings()->Session.JoinFirstAttempt + ((days * 24) + 11) * 60 * 60;
nguyenhoang9x5555 183:16414698889c 1029 }
nguyenhoang9x5555 183:16414698889c 1030 }
nguyenhoang9x5555 183:16414698889c 1031
nguyenhoang9x5555 183:16414698889c 1032 logWarning("JoinBackoff: %lu seconds Time On Air: %lu / %lu", GetSettings()->Session.JoinTimeOffEnd - now, GetSettings()->Session.JoinTimeOnAir, time_on_max);
nguyenhoang9x5555 183:16414698889c 1033 } else {
nguyenhoang9x5555 183:16414698889c 1034 GetSettings()->Session.JoinTimeOnAir += GetTimeOnAir(size);
nguyenhoang9x5555 183:16414698889c 1035 GetSettings()->Session.JoinTimeOffEnd = now + (GetTimeOnAir(size) / 10);
nguyenhoang9x5555 183:16414698889c 1036 }
nguyenhoang9x5555 183:16414698889c 1037
nguyenhoang9x5555 183:16414698889c 1038 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 1039 }
nguyenhoang9x5555 183:16414698889c 1040
nguyenhoang9x5555 183:16414698889c 1041
nguyenhoang9x5555 183:16414698889c 1042 uint8_t ChannelPlan_VI919::HandleMacCommand(uint8_t* payload, uint8_t& index) {
nguyenhoang9x5555 183:16414698889c 1043 logDebug("VI919 Handle Mac index: %d", index);
nguyenhoang9x5555 183:16414698889c 1044
nguyenhoang9x5555 183:16414698889c 1045 switch (payload[index++]) {
nguyenhoang9x5555 183:16414698889c 1046 case SRV_MAC_TX_PARAM_SETUP_REQ: {
nguyenhoang9x5555 183:16414698889c 1047 uint8_t eirp_dwell = payload[index++];
nguyenhoang9x5555 183:16414698889c 1048
nguyenhoang9x5555 183:16414698889c 1049 GetSettings()->Session.DownlinkDwelltime = eirp_dwell >> 5 & 0x01;
nguyenhoang9x5555 183:16414698889c 1050 GetSettings()->Session.UplinkDwelltime = eirp_dwell >> 4 & 0x01;
nguyenhoang9x5555 183:16414698889c 1051 //change data rate with if dwell time changes
nguyenhoang9x5555 183:16414698889c 1052 if(GetSettings()->Session.UplinkDwelltime == 0) {
nguyenhoang9x5555 183:16414698889c 1053 _minDatarate = lora::DR_0;
nguyenhoang9x5555 183:16414698889c 1054 } else {
nguyenhoang9x5555 183:16414698889c 1055 _minDatarate = lora::DR_2;
nguyenhoang9x5555 183:16414698889c 1056 if(GetSettings()->Session.TxDatarate < lora::DR_2) {
nguyenhoang9x5555 183:16414698889c 1057 GetSettings()->Session.TxDatarate = lora::DR_2;
nguyenhoang9x5555 183:16414698889c 1058 logDebug("Datarate is now DR%d",GetSettings()->Session.TxDatarate);
nguyenhoang9x5555 183:16414698889c 1059 }
nguyenhoang9x5555 183:16414698889c 1060 }
nguyenhoang9x5555 183:16414698889c 1061
nguyenhoang9x5555 183:16414698889c 1062 GetSettings()->Session.Max_EIRP = MAX_ERP_VALUES[(eirp_dwell & 0x0F)];
nguyenhoang9x5555 183:16414698889c 1063 logDebug("buffer index %d", GetSettings()->Session.CommandBufferIndex);
nguyenhoang9x5555 183:16414698889c 1064 if (GetSettings()->Session.CommandBufferIndex < COMMANDS_BUFFER_SIZE) {
nguyenhoang9x5555 183:16414698889c 1065 logDebug("Add tx param setup mac cmd to buffer");
nguyenhoang9x5555 183:16414698889c 1066 GetSettings()->Session.CommandBuffer[GetSettings()->Session.CommandBufferIndex++] = MOTE_MAC_TX_PARAM_SETUP_ANS;
nguyenhoang9x5555 183:16414698889c 1067 }
nguyenhoang9x5555 183:16414698889c 1068
nguyenhoang9x5555 183:16414698889c 1069 logDebug("TX PARAM DWELL UL: %d DL: %d Max EIRP: %d", GetSettings()->Session.UplinkDwelltime, GetSettings()->Session.DownlinkDwelltime, GetSettings()->Session.Max_EIRP);
nguyenhoang9x5555 183:16414698889c 1070 break;
nguyenhoang9x5555 183:16414698889c 1071 }
nguyenhoang9x5555 183:16414698889c 1072 default: {
nguyenhoang9x5555 183:16414698889c 1073 return LORA_ERROR;
nguyenhoang9x5555 183:16414698889c 1074 }
nguyenhoang9x5555 183:16414698889c 1075 }
nguyenhoang9x5555 183:16414698889c 1076
nguyenhoang9x5555 183:16414698889c 1077 return LORA_OK;
nguyenhoang9x5555 183:16414698889c 1078 }
nguyenhoang9x5555 183:16414698889c 1079
nguyenhoang9x5555 183:16414698889c 1080 void ChannelPlan_VI919::DecrementDatarate() {
nguyenhoang9x5555 183:16414698889c 1081 if(GetSettings()->Session.UplinkDwelltime == 0) {
nguyenhoang9x5555 183:16414698889c 1082 _minDatarate = lora::DR_0;
nguyenhoang9x5555 183:16414698889c 1083 } else {
nguyenhoang9x5555 183:16414698889c 1084 _minDatarate = lora::DR_2;
nguyenhoang9x5555 183:16414698889c 1085 }
nguyenhoang9x5555 183:16414698889c 1086
nguyenhoang9x5555 183:16414698889c 1087 if (GetSettings()->Session.TxDatarate > _minDatarate) {
nguyenhoang9x5555 183:16414698889c 1088 GetSettings()->Session.TxDatarate--;
nguyenhoang9x5555 183:16414698889c 1089 }
nguyenhoang9x5555 183:16414698889c 1090 }
nguyenhoang9x5555 183:16414698889c 1091
nguyenhoang9x5555 183:16414698889c 1092 bool ChannelPlan_VI919::DecodeBeacon(const uint8_t* payload, size_t size, BeaconData_t& data) {
nguyenhoang9x5555 183:16414698889c 1093 uint16_t crc1, crc1_rx, crc2, crc2_rx;
nguyenhoang9x5555 183:16414698889c 1094 const BCNPayload* beacon = (const BCNPayload*)payload;
nguyenhoang9x5555 183:16414698889c 1095
nguyenhoang9x5555 183:16414698889c 1096 // First check the size of the packet
nguyenhoang9x5555 183:16414698889c 1097 if (size != sizeof(BCNPayload))
nguyenhoang9x5555 183:16414698889c 1098 return false;
nguyenhoang9x5555 183:16414698889c 1099
nguyenhoang9x5555 183:16414698889c 1100 // Next we verify the CRCs are correct
nguyenhoang9x5555 183:16414698889c 1101 crc1 = CRC16(beacon->RFU, sizeof(beacon->RFU) + sizeof(beacon->Time));
nguyenhoang9x5555 183:16414698889c 1102 memcpy((uint8_t*)&crc1_rx, beacon->CRC1, sizeof(uint16_t));
nguyenhoang9x5555 183:16414698889c 1103
nguyenhoang9x5555 183:16414698889c 1104 if (crc1 != crc1_rx)
nguyenhoang9x5555 183:16414698889c 1105 return false;
nguyenhoang9x5555 183:16414698889c 1106
nguyenhoang9x5555 183:16414698889c 1107 crc2 = CRC16(beacon->GwSpecific, sizeof(beacon->GwSpecific));
nguyenhoang9x5555 183:16414698889c 1108 memcpy((uint8_t*)&crc2_rx, beacon->CRC2, sizeof(uint16_t));
nguyenhoang9x5555 183:16414698889c 1109
nguyenhoang9x5555 183:16414698889c 1110 if (crc2 != crc2_rx)
nguyenhoang9x5555 183:16414698889c 1111 return false;
nguyenhoang9x5555 183:16414698889c 1112
nguyenhoang9x5555 183:16414698889c 1113 // Now that we have confirmed this packet is a beacon, parse and complete the output struct
nguyenhoang9x5555 183:16414698889c 1114 memcpy(&data.Time, beacon->Time, sizeof(beacon->Time));
nguyenhoang9x5555 183:16414698889c 1115 data.InfoDesc = beacon->GwSpecific[0];
nguyenhoang9x5555 183:16414698889c 1116
nguyenhoang9x5555 183:16414698889c 1117 // Update the GPS fields if we have a gps info descriptor
nguyenhoang9x5555 183:16414698889c 1118 if (data.InfoDesc == GPS_FIRST_ANTENNA ||
nguyenhoang9x5555 183:16414698889c 1119 data.InfoDesc == GPS_SECOND_ANTENNA ||
nguyenhoang9x5555 183:16414698889c 1120 data.InfoDesc == GPS_THIRD_ANTENNA) {
nguyenhoang9x5555 183:16414698889c 1121 // Latitude and Longitude 3 bytes in length
nguyenhoang9x5555 183:16414698889c 1122 memcpy(&data.Latitude, &beacon->GwSpecific[1], 3);
nguyenhoang9x5555 183:16414698889c 1123 memcpy(&data.Longitude, &beacon->GwSpecific[4], 3);
nguyenhoang9x5555 183:16414698889c 1124 }
nguyenhoang9x5555 183:16414698889c 1125
nguyenhoang9x5555 183:16414698889c 1126 return true;
nguyenhoang9x5555 183:16414698889c 1127 }