Line data Source code
1 : #include "LR2021.h"
2 :
3 : #include "../LR11x0/LR_common.h"
4 : #include "LR2021_registers.h"
5 :
6 : #include <math.h>
7 : #include <string.h>
8 :
9 : #if !RADIOLIB_EXCLUDE_LR2021
10 :
11 : // maximum number of allowed frontend calibration attempts
12 : #define RADIOLIB_LR2021_MAX_CAL_ATTEMPTS (10)
13 :
14 : static const LR2021PaTableEntry_t paOptTableLf[RADIOLIB_LR2021_PA_TABLE_LEN] = {
15 : { .paDutyCycle = 1, .paSlices = 1, .paVal = 8 },
16 : { .paDutyCycle = 2, .paSlices = 2, .paVal = 1 },
17 : { .paDutyCycle = 2, .paSlices = 2, .paVal = 3 },
18 : { .paDutyCycle = 2, .paSlices = 2, .paVal = 5 },
19 : { .paDutyCycle = 1, .paSlices = 2, .paVal = 13 },
20 : { .paDutyCycle = 2, .paSlices = 1, .paVal = 13 },
21 : { .paDutyCycle = 2, .paSlices = 2, .paVal = 11 },
22 : { .paDutyCycle = 2, .paSlices = 2, .paVal = 13 },
23 : { .paDutyCycle = 3, .paSlices = 1, .paVal = 12 },
24 : { .paDutyCycle = 1, .paSlices = 1, .paVal = 18 },
25 : { .paDutyCycle = 1, .paSlices = 1, .paVal = 20 },
26 : { .paDutyCycle = 1, .paSlices = 1, .paVal = 23 },
27 : { .paDutyCycle = 1, .paSlices = 1, .paVal = 27 },
28 : { .paDutyCycle = 1, .paSlices = 1, .paVal = 33 },
29 : { .paDutyCycle = 1, .paSlices = 2, .paVal = 26 },
30 : { .paDutyCycle = 1, .paSlices = 2, .paVal = 31 },
31 : { .paDutyCycle = 1, .paSlices = 3, .paVal = 27 },
32 : { .paDutyCycle = 1, .paSlices = 1, .paVal = 37 },
33 : { .paDutyCycle = 1, .paSlices = 2, .paVal = 40 },
34 : { .paDutyCycle = 2, .paSlices = 1, .paVal = 38 },
35 : { .paDutyCycle = 2, .paSlices = 2, .paVal = 39 },
36 : { .paDutyCycle = 2, .paSlices = 4, .paVal = 40 },
37 : { .paDutyCycle = 2, .paSlices = 7, .paVal = 41 },
38 : { .paDutyCycle = 3, .paSlices = 2, .paVal = 39 },
39 : { .paDutyCycle = 3, .paSlices = 3, .paVal = 39 },
40 : { .paDutyCycle = 3, .paSlices = 6, .paVal = 38 },
41 : { .paDutyCycle = 4, .paSlices = 3, .paVal = 37 },
42 : { .paDutyCycle = 4, .paSlices = 5, .paVal = 37 },
43 : { .paDutyCycle = 4, .paSlices = 7, .paVal = 38 },
44 : { .paDutyCycle = 5, .paSlices = 3, .paVal = 37 },
45 : { .paDutyCycle = 5, .paSlices = 6, .paVal = 37 },
46 : { .paDutyCycle = 6, .paSlices = 7, .paVal = 35 },
47 : };
48 :
49 : static const LR2021PaTableEntry_t paOptTableHf[RADIOLIB_LR2021_PA_TABLE_LEN] = {
50 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -38 }, // -19
51 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -36 }, // -18
52 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -34 }, // -17
53 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -32 }, // -16
54 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -30 }, // -15
55 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -28 }, // -14
56 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -26 }, // -13
57 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -24 }, // -12
58 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -22 }, // -11
59 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -20 }, // -10
60 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -18 }, // -9
61 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -16 }, // -8
62 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -14 }, // -7
63 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -12 }, // -6
64 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -10 }, // -5
65 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -8 }, // -4
66 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -6 }, // -3
67 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -4 }, // -2
68 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -2 }, // -1
69 : { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 4 }, // 0
70 : { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 6 }, // 1
71 : { .paDutyCycle = 12, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 7 }, // 2
72 : { .paDutyCycle = 9, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 8 }, // 3
73 : { .paDutyCycle = 9, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 10 }, // 4
74 : { .paDutyCycle = 15, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 15 }, // 5
75 : { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 16 }, // 6
76 : { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 18 }, // 7
77 : { .paDutyCycle = 15, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 21 }, // 8
78 : { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 22 }, // 9
79 : { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 }, // 10
80 : { .paDutyCycle = 10, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 }, // 11
81 : { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 }, // 12
82 : };
83 :
84 1 : int16_t LR2021::setFrequency(float freq) {
85 1 : return(this->setFrequency(freq, false));
86 : }
87 :
88 1 : int16_t LR2021::setFrequency(float freq, bool skipCalibration) {
89 : #if RADIOLIB_CHECK_PARAMS
90 1 : if(!(((freq >= 150.0f) && (freq <= 1090.0f)) ||
91 1 : ((freq >= 1900.0f) && (freq <= 2200.0f)) ||
92 1 : ((freq >= 2400.0f) && (freq <= 2500.0f)))) {
93 1 : return(RADIOLIB_ERR_INVALID_FREQUENCY);
94 : }
95 : #endif
96 :
97 : // check if we need to recalibrate image
98 : int16_t state;
99 0 : if(!skipCalibration && (fabsf(freq - this->freqMHz) >= RADIOLIB_LR2021_CAL_IMG_FREQ_TRIG_MHZ)) {
100 : // calibration can fail if there is a strong interfering source
101 : // run it several times until it passes
102 0 : int i = 0;
103 0 : for(; i < RADIOLIB_LR2021_MAX_CAL_ATTEMPTS; i++) {
104 : // get the nearest multiple of 4 MHz
105 0 : uint16_t frequencies[3] = { (uint16_t)((freq / 4.0f) + 0.5f), 0, 0 };
106 0 : frequencies[0] |= (freq > RADIOLIB_LR2021_LF_CUTOFF_FREQ) ? RADIOLIB_LR2021_CALIBRATE_FE_HF_PATH : RADIOLIB_LR2021_CALIBRATE_FE_LF_PATH;
107 0 : state = calibrateFrontEnd(const_cast<const uint16_t*>(frequencies));
108 :
109 : // if something failed, check the device errors
110 0 : if(state != RADIOLIB_ERR_NONE) {
111 0 : uint16_t errors = 0;
112 0 : getErrors(&errors);
113 : RADIOLIB_DEBUG_BASIC_PRINTLN("Frontend calibration #%d failed, device errors: 0x%X", i, errors);
114 :
115 : // if this is caused by something else than RSSI saturation, repeating will not help
116 0 : if((errors & RADIOLIB_LR2021_SRC_SATURATION_CALIB_ERR) == 0) {
117 0 : return(state);
118 : }
119 :
120 : // wait a little while before the next attempt
121 0 : this->mod->hal->delay(5);
122 :
123 : } else {
124 : // calibration passed
125 0 : break;
126 : }
127 :
128 : }
129 :
130 0 : if(i == RADIOLIB_LR2021_MAX_CAL_ATTEMPTS) {
131 0 : return(RADIOLIB_ERR_FRONTEND_CALIBRATION_FAILED);
132 : }
133 :
134 : }
135 :
136 : // set frequency
137 0 : state = setRfFrequency((uint32_t)(freq*1000000.0f));
138 0 : RADIOLIB_ASSERT(state);
139 0 : this->freqMHz = freq;
140 0 : this->highFreq = (freq > RADIOLIB_LR2021_LF_CUTOFF_FREQ);
141 0 : return(state);
142 : }
143 :
144 1 : int16_t LR2021::setOutputPower(int8_t power) {
145 1 : return(this->setOutputPower(power, 48));
146 : }
147 :
148 1 : int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) {
149 : // check if power value is configurable
150 1 : int16_t state = this->checkOutputPower(power, NULL);
151 1 : RADIOLIB_ASSERT(state);
152 :
153 : //! \TODO: [LR2021] how and when to configure OCP?
154 :
155 : // poitners to default tables
156 1 : LR2021PaTableEntry_t* defaultTables[] = {
157 : const_cast<LR2021PaTableEntry_t*>(paOptTableLf),
158 : const_cast<LR2021PaTableEntry_t*>(paOptTableHf),
159 : };
160 :
161 : // if the user pointers are not set, use the appropriate (LF/HF) default table
162 1 : LR2021PaTableEntry_t* table = this->paOptTable[this->highFreq] ? this->paOptTable[this->highFreq] : defaultTables[this->highFreq];
163 :
164 : // update PA config
165 1 : const LR2021PaTableEntry_t* paCfg = this->highFreq ? &table[power + 19] : &table[power + 9];
166 2 : state = setPaConfig(this->highFreq,
167 : RADIOLIB_LR2021_PA_LF_MODE_FSM,
168 1 : this->highFreq ? RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED : paCfg->paDutyCycle,
169 1 : paCfg->paSlices,
170 1 : this->highFreq ? (paCfg->paDutyCycle + RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED) : RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED);
171 1 : RADIOLIB_ASSERT(state);
172 :
173 : // set output power
174 0 : state = setTxParams(paCfg->paVal, roundRampTime(rampTimeUs));
175 0 : return(state);
176 : }
177 :
178 0 : void LR2021::setPaTable(LR2021PaTableEntry_t* table, bool highFreq) {
179 0 : this->paOptTable[highFreq] = table;
180 0 : }
181 :
182 0 : int16_t LR2021::setStandbyXOSC(bool enable) {
183 : // set the internal variable
184 0 : this->standbyXOSC = enable;
185 :
186 : // call standby to start the oscillator
187 0 : int16_t state = this->standbyXOSC ? standby(RADIOLIB_LR2021_STANDBY_XOSC) : standby(RADIOLIB_LR2021_STANDBY_RC);
188 0 : RADIOLIB_ASSERT(state);
189 :
190 : // update Rx/Tx fallback mode
191 0 : uint8_t mode = this->standbyXOSC ? RADIOLIB_LR2021_FALLBACK_MODE_STBY_XOSC : RADIOLIB_LR2021_FALLBACK_MODE_STBY_RC;
192 0 : return(setRxTxFallbackMode(mode));
193 : }
194 :
195 2 : int16_t LR2021::checkOutputPower(int8_t power, int8_t* clipped) {
196 2 : if(this->highFreq) {
197 0 : if(clipped) {
198 0 : *clipped = RADIOLIB_MAX(-19, RADIOLIB_MIN(12, power));
199 : }
200 0 : RADIOLIB_CHECK_RANGE(power, -19, 12, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
201 :
202 : } else {
203 2 : if(clipped) {
204 0 : *clipped = RADIOLIB_MAX(-9, RADIOLIB_MIN(22, power));
205 : }
206 2 : RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
207 :
208 : }
209 :
210 2 : return(RADIOLIB_ERR_NONE);
211 : }
212 :
213 0 : void LR2021::setRfSwitchTable(const uint32_t (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) {
214 : // find which pins are used
215 : // on LR2021, modes are configured per DIO (exact opposite of LR11x0)
216 0 : uint8_t dioConfigs[7] = { 0 };
217 0 : for(size_t i = 0; i < Module::RFSWITCH_MAX_PINS; i++) {
218 : // check if this pin is unused
219 0 : if(pins[i] == RADIOLIB_NC) { continue; }
220 :
221 : // only keep DIO pins, there may be some GPIOs in the switch tabke
222 0 : if((pins[i] & RFSWITCH_PIN_FLAG) == 0) { continue; }
223 :
224 : // find modes in which this pin is used
225 0 : uint32_t dioNum = RADIOLIB_LRXXXX_DIOx_VAL(pins[i]);
226 0 : size_t j = 0;
227 0 : while(table[j].mode != LR2021::MODE_END_OF_TABLE) {
228 0 : dioConfigs[dioNum] |= (table[j].values[i] == this->mod->hal->GpioLevelHigh) ? (1UL << (table[j].mode - 1)) : 0;
229 0 : j++;
230 : }
231 :
232 : // For DIO = 5, only DIO_SLEEP_PULL_UP is accepted. Otherwise the SetDioFunction returns FAIL.
233 0 : uint8_t pull = dioNum == 0 ? RADIOLIB_LR2021_DIO_SLEEP_PULL_UP : RADIOLIB_LR2021_DIO_SLEEP_PULL_AUTO;
234 : // enable RF control for this pin and set the modes in which it is active
235 0 : (void)this->setDioFunction(dioNum + 5, RADIOLIB_LR2021_DIO_FUNCTION_RF_SWITCH, pull);
236 0 : (void)this->setDioRfSwitchConfig(dioNum + 5, dioConfigs[dioNum]);
237 : }
238 0 : }
239 :
240 0 : int16_t LR2021::forceLDRO(bool enable) {
241 : // check packet type
242 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
243 0 : int16_t state = getPacketType(&type);
244 0 : RADIOLIB_ASSERT(state);
245 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
246 0 : return(RADIOLIB_ERR_WRONG_MODEM);
247 : }
248 :
249 : // update modulation parameters
250 0 : this->ldroAuto = false;
251 0 : this->ldrOptimize = (uint8_t)enable;
252 0 : return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
253 : }
254 :
255 0 : int16_t LR2021::autoLDRO() {
256 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
257 0 : int16_t state = getPacketType(&type);
258 0 : RADIOLIB_ASSERT(state);
259 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
260 0 : return(RADIOLIB_ERR_WRONG_MODEM);
261 : }
262 :
263 0 : this->ldroAuto = true;
264 0 : return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
265 : }
266 :
267 0 : int16_t LR2021::setBandwidth(float bw) {
268 : // check active modem
269 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
270 0 : int16_t state = getPacketType(&type);
271 0 : RADIOLIB_ASSERT(state);
272 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
273 0 : return(RADIOLIB_ERR_WRONG_MODEM);
274 : }
275 :
276 : // convert to int to avoid bunch of math
277 0 : int bw_div2 = bw / 2 + 0.01f;
278 :
279 : // check allowed bandwidth values
280 0 : switch (bw_div2) {
281 0 : case 15:
282 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_31;
283 0 : break;
284 0 : case 20:
285 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_41;
286 0 : break;
287 0 : case 31:
288 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_62;
289 0 : break;
290 0 : case 41:
291 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_83;
292 0 : break;
293 0 : case 50:
294 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_101;
295 0 : break;
296 0 : case 101:
297 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_203;
298 0 : break;
299 0 : case 62:
300 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_125;
301 0 : break;
302 0 : case 125:
303 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_250;
304 0 : break;
305 0 : case 203:
306 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_406;
307 0 : break;
308 0 : case 250:
309 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_500;
310 0 : break;
311 0 : case 406:
312 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_812;
313 0 : break;
314 0 : case 500:
315 0 : this->bandwidth = RADIOLIB_LR2021_LORA_BW_1000;
316 0 : break;
317 0 : default:
318 0 : return(RADIOLIB_ERR_INVALID_BANDWIDTH);
319 : }
320 :
321 : // update modulation parameters
322 0 : this->bandwidthKhz = bw;
323 0 : return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
324 : }
325 :
326 0 : int16_t LR2021::setSpreadingFactor(uint8_t sf, bool legacy) {
327 : // check active modem
328 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
329 0 : int16_t state = getPacketType(&type);
330 0 : RADIOLIB_ASSERT(state);
331 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
332 0 : return(RADIOLIB_ERR_WRONG_MODEM);
333 : }
334 :
335 0 : RADIOLIB_CHECK_RANGE(sf, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
336 :
337 0 : if(legacy && (sf == 6)) {
338 : // enable SF6 legacy mode if requested
339 0 : state = this->writeRegMemMask32(RADIOLIB_LR2021_REG_LORA_MODEM_TXRX_CFG0, (3UL << 18), (1UL << 19));
340 : } else {
341 : // disable it in all other cases
342 0 : state = this->writeRegMemMask32(RADIOLIB_LR2021_REG_LORA_MODEM_TXRX_CFG0, (3UL << 18), 0);
343 : }
344 0 : RADIOLIB_ASSERT(state);
345 :
346 : // update modulation parameters
347 0 : this->spreadingFactor = sf;
348 0 : return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
349 : }
350 :
351 0 : int16_t LR2021::setCodingRate(uint8_t cr, bool longInterleave) {
352 : // check active modem
353 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
354 0 : int16_t state = getPacketType(&type);
355 0 : RADIOLIB_ASSERT(state);
356 0 : if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
357 0 : RADIOLIB_CHECK_RANGE(cr, 4, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
358 :
359 0 : if(longInterleave) {
360 0 : switch(cr) {
361 0 : case 4:
362 0 : this->codingRate = 0;
363 0 : break;
364 0 : case 5:
365 : case 6:
366 0 : this->codingRate = cr;
367 0 : break;
368 0 : case 8:
369 0 : this->codingRate = cr - 1;
370 0 : break;
371 0 : default:
372 0 : return(RADIOLIB_ERR_INVALID_CODING_RATE);
373 : }
374 :
375 : } else {
376 0 : this->codingRate = cr - 4;
377 :
378 : }
379 :
380 : // update modulation parameters
381 0 : return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
382 :
383 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
384 0 : if(cr > RADIOLIB_LR2021_FLRC_CR_2_3) {
385 0 : return(RADIOLIB_ERR_INVALID_CODING_RATE);
386 : }
387 :
388 : // update modulation parameters
389 0 : this->codingRateFlrc = cr;
390 0 : return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
391 :
392 : }
393 :
394 0 : return(RADIOLIB_ERR_WRONG_MODEM);
395 : }
396 :
397 0 : int16_t LR2021::setSyncWord(uint8_t syncWord) {
398 : // check active modem
399 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
400 0 : int16_t state = getPacketType(&type);
401 0 : RADIOLIB_ASSERT(state);
402 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
403 0 : return(RADIOLIB_ERR_WRONG_MODEM);
404 : }
405 :
406 0 : return(setLoRaSyncword(syncWord));
407 : }
408 :
409 1 : int16_t LR2021::setPreambleLength(size_t preambleLength) {
410 : // check active modem
411 1 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
412 1 : int16_t state = getPacketType(&type);
413 1 : RADIOLIB_ASSERT(state);
414 0 : if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
415 0 : this->preambleLengthLoRa = preambleLength;
416 0 : return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType,
417 0 : (this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
418 :
419 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
420 0 : this->preambleLengthGFSK = preambleLength;
421 0 : this->preambleDetLength = (preambleLength / 8) << 3;
422 0 : return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType,
423 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
424 :
425 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
426 0 : this->preambleLengthGFSK = preambleLength;
427 0 : this->preambleDetLength = (preambleLength / 8) << 3;
428 0 : return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType,
429 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
430 :
431 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
432 0 : if((preambleLength % 4) != 0) {
433 0 : return(RADIOLIB_ERR_INVALID_PREAMBLE_LENGTH);
434 : }
435 0 : this->preambleLengthGFSK = (preambleLength / 4) - 1;
436 0 : return(setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK,
437 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH));
438 :
439 : }
440 :
441 0 : return(RADIOLIB_ERR_WRONG_MODEM);
442 : }
443 :
444 0 : int16_t LR2021::setTCXO(float voltage, uint32_t delay) {
445 : // force RC oscillator - typically this is called on startup,
446 : // so we cannot rely on what the user may have provided
447 0 : (void)standby(RADIOLIB_LR2021_STANDBY_RC);
448 :
449 : // check oscillator startup error flag and clear it
450 0 : uint16_t errors = 0;
451 0 : int16_t state = getErrors(&errors);
452 0 : RADIOLIB_ASSERT(state);
453 0 : if(errors & RADIOLIB_LR2021_HF_XOSC_START_ERR) {
454 0 : clearErrors();
455 : }
456 :
457 : // check 0 V disable
458 0 : if(fabsf(voltage - 0.0f) <= 0.001f) {
459 0 : setTcxoMode(0, 0);
460 0 : return(reset());
461 : }
462 :
463 : // check allowed voltage values
464 0 : uint8_t tune = 0;
465 0 : if(fabsf(voltage - 1.6f) <= 0.001f) {
466 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_6;
467 0 : } else if(fabsf(voltage - 1.7f) <= 0.001f) {
468 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_7;
469 0 : } else if(fabsf(voltage - 1.8f) <= 0.001f) {
470 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_8;
471 0 : } else if(fabsf(voltage - 2.2f) <= 0.001f) {
472 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_2;
473 0 : } else if(fabsf(voltage - 2.4f) <= 0.001f) {
474 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_4;
475 0 : } else if(fabsf(voltage - 2.7f) <= 0.001f) {
476 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_7;
477 0 : } else if(fabsf(voltage - 3.0f) <= 0.001f) {
478 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_3_0;
479 0 : } else if(fabsf(voltage - 3.3f) <= 0.001f) {
480 0 : tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_3_3;
481 : } else {
482 0 : return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
483 : }
484 :
485 : // calculate delay value
486 0 : uint32_t delayValue = (uint32_t)((float)delay / 30.52f);
487 0 : if(delayValue == 0) {
488 0 : delayValue = 1;
489 : }
490 :
491 : // enable TCXO control
492 0 : return(setTcxoMode(tune, delayValue));
493 : }
494 :
495 0 : int16_t LR2021::setCRC(uint8_t len, uint32_t initial, uint32_t polynomial, bool inverted) {
496 : // check active modem
497 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
498 0 : int16_t state = getPacketType(&type);
499 0 : RADIOLIB_ASSERT(state);
500 0 : if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
501 : // LoRa CRC doesn't allow to set CRC polynomial, initial value, or inversion
502 0 : this->crcTypeLoRa = len > 0;
503 0 : return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType,
504 0 : (this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
505 :
506 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
507 0 : if(len > 4) {
508 0 : return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
509 : }
510 :
511 0 : this->crcTypeGFSK = len;
512 0 : if((this->crcTypeGFSK != RADIOLIB_LR2021_GFSK_OOK_CRC_OFF) && inverted) {
513 0 : this->crcTypeGFSK += 0x08;
514 : }
515 :
516 0 : state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType,
517 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
518 0 : RADIOLIB_ASSERT(state);
519 :
520 0 : return(setGfskCrcParams(polynomial, initial));
521 :
522 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
523 0 : if(len > 4) {
524 0 : return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
525 : }
526 :
527 0 : this->crcTypeGFSK = len;
528 0 : if((this->crcTypeGFSK != RADIOLIB_LR2021_GFSK_OOK_CRC_OFF) && inverted) {
529 0 : this->crcTypeGFSK += 0x08;
530 : }
531 :
532 0 : state = setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType,
533 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
534 0 : RADIOLIB_ASSERT(state);
535 :
536 0 : return(setOokCrcParams(polynomial, initial));
537 :
538 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
539 0 : if((len == 1) || (len > 4)) {
540 0 : return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
541 : }
542 :
543 0 : this->crcLenGFSK = len ? len - 1 : 0;
544 0 : return(setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK,
545 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH));
546 :
547 : }
548 :
549 0 : return(RADIOLIB_ERR_WRONG_MODEM);
550 : }
551 :
552 1 : int16_t LR2021::invertIQ(bool enable) {
553 : // check active modem
554 1 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
555 1 : int16_t state = getPacketType(&type);
556 1 : RADIOLIB_ASSERT(state);
557 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
558 0 : return(RADIOLIB_ERR_WRONG_MODEM);
559 : }
560 :
561 0 : this->invertIQEnabled = enable;
562 0 : return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType,
563 0 : (this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
564 : }
565 :
566 1 : int16_t LR2021::setBitRate(float br) {
567 : // check active modem
568 1 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
569 1 : int16_t state = getPacketType(&type);
570 1 : RADIOLIB_ASSERT(state);
571 0 : if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
572 0 : RADIOLIB_CHECK_RANGE(br, 0.5f, 2000.0f, RADIOLIB_ERR_INVALID_BIT_RATE);
573 : //! \TODO: [LR2021] implement fractional bit rate configuration
574 0 : this->bitRate = br * 1000.0f;
575 0 : state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
576 0 : return(state);
577 :
578 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
579 0 : RADIOLIB_CHECK_RANGE(br, 0.5f, 2000.0f, RADIOLIB_ERR_INVALID_BIT_RATE);
580 : //! \TODO: [LR2021] implement fractional bit rate configuration
581 0 : this->bitRate = br * 1000.0f;
582 : //! \TODO: [LR2021] implement OOK magnitude depth configuration
583 0 : state = setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL);
584 0 : return(state);
585 :
586 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
587 0 : if((uint16_t)br == 260) {
588 0 : this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_260;
589 0 : } else if((uint16_t)br == 325) {
590 0 : this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_325;
591 0 : } else if((uint16_t)br == 520) {
592 0 : this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_520;
593 0 : } else if((uint16_t)br == 650) {
594 0 : this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_650;
595 0 : } else if((uint16_t)br == 1040) {
596 0 : this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_1040;
597 0 : } else if((uint16_t)br == 1300) {
598 0 : this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_1300;
599 0 : } else if((uint16_t)br == 2080) {
600 0 : this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_2080;
601 0 : } else if((uint16_t)br == 2600) {
602 0 : this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_2600;
603 : } else {
604 0 : return(RADIOLIB_ERR_INVALID_BIT_RATE);
605 : }
606 :
607 : // it is slightly weird to reuse the GFSK bitrate variable in this way
608 : // but if GFSK gets enabled it should get reset anyway ... I think
609 0 : this->bitRate = br;
610 :
611 : // update modulation parameters
612 0 : return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
613 : }
614 :
615 0 : return(RADIOLIB_ERR_WRONG_MODEM);
616 : }
617 :
618 1 : int16_t LR2021::setFrequencyDeviation(float freqDev) {
619 : // check active modem
620 1 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
621 1 : int16_t state = getPacketType(&type);
622 1 : RADIOLIB_ASSERT(state);
623 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
624 0 : return(RADIOLIB_ERR_WRONG_MODEM);
625 : }
626 :
627 : // set frequency deviation to lowest available setting (required for digimodes)
628 0 : float newFreqDev = freqDev;
629 0 : if(freqDev < 0.6f) {
630 0 : newFreqDev = 0.6f;
631 : }
632 :
633 0 : RADIOLIB_CHECK_RANGE(newFreqDev, 0.6f, 500.0f, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
634 0 : this->frequencyDev = newFreqDev * 1000.0f;
635 0 : state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
636 0 : return(state);
637 : }
638 :
639 0 : int16_t LR2021::setRxBandwidth(float rxBw) {
640 : // check active modem
641 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
642 0 : int16_t state = getPacketType(&type);
643 0 : RADIOLIB_ASSERT(state);
644 0 : if(!((type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) ||
645 0 : (type == RADIOLIB_LR2021_PACKET_TYPE_OOK))) {
646 0 : return(RADIOLIB_ERR_WRONG_MODEM);
647 : }
648 :
649 0 : const uint8_t rxBwLut[] = {
650 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_4_8,
651 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_5_8,
652 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_7_4,
653 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_9_7,
654 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_12_0,
655 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_14_9,
656 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_19_2,
657 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_23_1,
658 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_29_8,
659 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_38_5,
660 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_46_3,
661 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_59_5,
662 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_76_9,
663 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_92_6,
664 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_119_0,
665 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_153_8,
666 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_185_2,
667 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_238_1,
668 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_307_7,
669 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_370_4,
670 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_476_2,
671 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_555_6,
672 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_666_7,
673 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_769_2,
674 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_1111,
675 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_2222,
676 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_2666,
677 : RADIOLIB_LR2021_GFSK_OOK_RX_BW_3076,
678 : };
679 :
680 0 : state = findRxBw(rxBw, rxBwLut, sizeof(rxBwLut)/sizeof(rxBwLut[0]), 3076.0f, &this->rxBandwidth);
681 0 : RADIOLIB_ASSERT(state);
682 :
683 : // update modulation parameters
684 0 : if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
685 0 : state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
686 : } else {
687 0 : state = setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL);
688 : }
689 0 : return(state);
690 : }
691 :
692 1 : int16_t LR2021::setSyncWord(uint8_t* syncWord, size_t len) {
693 1 : if(len > RADIOLIB_LR2021_GFSK_SYNC_WORD_LEN) {
694 0 : return(RADIOLIB_ERR_INVALID_SYNC_WORD);
695 : }
696 :
697 : // check active modem
698 1 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
699 1 : int16_t state = getPacketType(&type);
700 1 : RADIOLIB_ASSERT(state);
701 :
702 0 : uint32_t sync = 0;
703 0 : switch(type) {
704 0 : case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
705 : // default to MSB-first
706 0 : return(setGfskSyncword(const_cast<const uint8_t*>(syncWord), len, true));
707 :
708 0 : case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
709 : // default to MSB-first
710 0 : return(setOokSyncword(const_cast<const uint8_t*>(syncWord), len, true));
711 :
712 0 : case(RADIOLIB_LR2021_PACKET_TYPE_LORA):
713 : // with length set to 1 and LoRa modem active, assume it is the LoRa sync word
714 0 : if(len > 1) {
715 0 : return(RADIOLIB_ERR_INVALID_SYNC_WORD);
716 : }
717 0 : return(setSyncWord(syncWord[0]));
718 :
719 0 : case(RADIOLIB_LR2021_PACKET_TYPE_LR_FHSS):
720 : // with length set to 4 and LR-FHSS modem active, assume it is the LR-FHSS sync word
721 0 : if(len != sizeof(uint32_t)) {
722 0 : return(RADIOLIB_ERR_INVALID_SYNC_WORD);
723 : }
724 0 : memcpy(&sync, syncWord, sizeof(uint32_t));
725 0 : return(lrFhssSetSyncword(sync));
726 :
727 0 : case(RADIOLIB_LR2021_PACKET_TYPE_FLRC):
728 : // FLRC requires 16 or 32-bit sync word
729 0 : if(!((len == 0) || (len == 2) || (len == 4))) {
730 0 : return(RADIOLIB_ERR_INVALID_SYNC_WORD);
731 : }
732 :
733 : // update sync word length
734 0 : this->syncWordLength = len;
735 0 : state = setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK,
736 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH);
737 0 : RADIOLIB_ASSERT(state);
738 :
739 0 : sync |= (uint32_t)syncWord[0] << 24;
740 0 : sync |= (uint32_t)syncWord[1] << 16;
741 0 : sync |= (uint32_t)syncWord[2] << 8;
742 0 : sync |= (uint32_t)syncWord[3];
743 0 : return(setFlrcSyncWord(1, sync));
744 : }
745 :
746 0 : return(RADIOLIB_ERR_WRONG_MODEM);
747 : }
748 :
749 1 : int16_t LR2021::setDataShaping(uint8_t sh) {
750 : // check active modem
751 1 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
752 1 : int16_t state = getPacketType(&type);
753 1 : RADIOLIB_ASSERT(state);
754 :
755 : // set data shaping
756 0 : switch(sh) {
757 0 : case RADIOLIB_SHAPING_NONE:
758 0 : this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_NONE;
759 0 : break;
760 0 : case RADIOLIB_SHAPING_0_3:
761 0 : this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_3;
762 0 : break;
763 0 : case RADIOLIB_SHAPING_0_5:
764 0 : this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_5;
765 0 : break;
766 0 : case RADIOLIB_SHAPING_0_7:
767 0 : this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_7;
768 0 : break;
769 0 : case RADIOLIB_SHAPING_1_0:
770 0 : this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_1_0;
771 0 : break;
772 0 : default:
773 0 : return(RADIOLIB_ERR_INVALID_DATA_SHAPING);
774 : }
775 :
776 : // update modulation parameters
777 0 : if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
778 0 : return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
779 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
780 0 : return(setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev));
781 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
782 0 : return(setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL));
783 : }
784 0 : return(RADIOLIB_ERR_WRONG_MODEM);
785 : }
786 :
787 1 : int16_t LR2021::setEncoding(uint8_t encoding) {
788 : // check active modem
789 1 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
790 1 : int16_t state = getPacketType(&type);
791 1 : RADIOLIB_ASSERT(state);
792 :
793 0 : switch(type) {
794 0 : case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
795 0 : return(setWhitening(encoding == RADIOLIB_ENCODING_WHITENING));
796 :
797 0 : case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
798 0 : switch(encoding) {
799 0 : case(RADIOLIB_ENCODING_NRZ):
800 : case(RADIOLIB_ENCODING_WHITENING):
801 0 : return(setWhitening(encoding == RADIOLIB_ENCODING_WHITENING));
802 :
803 0 : case(RADIOLIB_ENCODING_MANCHESTER):
804 0 : state = setWhitening(false);
805 0 : RADIOLIB_ASSERT(state);
806 0 : this->whitening = RADIOLIB_LR2021_OOK_MANCHESTER_ON;
807 0 : return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType,
808 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
809 :
810 0 : case(RADIOLIB_ENCODING_MANCHESTER_INV):
811 0 : state = setWhitening(false);
812 0 : RADIOLIB_ASSERT(state);
813 0 : this->whitening = RADIOLIB_LR2021_OOK_MANCHESTER_ON_INV;
814 0 : return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType,
815 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
816 :
817 0 : default:
818 0 : return(RADIOLIB_ERR_INVALID_ENCODING);
819 : }
820 :
821 0 : default:
822 0 : return(RADIOLIB_ERR_WRONG_MODEM);
823 : }
824 :
825 : return(state);
826 : }
827 :
828 0 : int16_t LR2021::fixedPacketLengthMode(uint8_t len) {
829 0 : return(setPacketMode(RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, len));
830 : }
831 :
832 0 : int16_t LR2021::variablePacketLengthMode(uint8_t maxLen) {
833 0 : return(setPacketMode(RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_VARIABLE_8BIT, maxLen));
834 : }
835 :
836 0 : int16_t LR2021::setWhitening(bool enabled, uint16_t initial) {
837 : // check active modem
838 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
839 0 : int16_t state = getPacketType(&type);
840 0 : RADIOLIB_ASSERT(state);
841 :
842 0 : switch(type) {
843 0 : case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
844 : //! \TODO: [LR2021] Implement SX128x-compatible whitening
845 0 : if(enabled) {
846 0 : state = setGfskWhiteningParams(RADIOLIB_LR2021_GFSK_WHITENING_TYPE_SX126X_LR11XX, initial);
847 0 : RADIOLIB_ASSERT(state);
848 : }
849 0 : this->whitening = enabled;
850 0 : return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType,
851 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
852 :
853 0 : case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
854 0 : this->whitening = enabled;
855 0 : if(enabled) {
856 : //! \TODO: [LR2021] Implement configurable index and polynomial
857 0 : state = setOokWhiteningParams(12, 0x01FF, initial);
858 : } else {
859 0 : state = setOokWhiteningParams(0, 0, 0);
860 : }
861 0 : RADIOLIB_ASSERT(state);
862 0 : return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType,
863 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
864 : }
865 :
866 0 : return(RADIOLIB_ERR_WRONG_MODEM);
867 : }
868 :
869 1 : int16_t LR2021::setDataRate(DataRate_t dr, ModemType_t modem ) {
870 : // get the current modem
871 : ModemType_t currentModem;
872 1 : int16_t state = this->getModem(¤tModem);
873 1 : RADIOLIB_ASSERT(state);
874 :
875 : // switch over if the requested modem is different
876 0 : if(modem != RADIOLIB_MODEM_NONE && modem != currentModem) {
877 0 : state = this->standby();
878 0 : RADIOLIB_ASSERT(state);
879 0 : state = this->setModem(modem);
880 0 : RADIOLIB_ASSERT(state);
881 : }
882 :
883 0 : if(modem == RADIOLIB_MODEM_NONE) {
884 0 : modem = currentModem;
885 : }
886 :
887 : // select interpretation based on modem
888 0 : if(modem == RADIOLIB_MODEM_FSK) {
889 : // set the bit rate
890 0 : state = this->setBitRate(dr.fsk.bitRate);
891 0 : RADIOLIB_ASSERT(state);
892 :
893 : // set the frequency deviation
894 0 : state = this->setFrequencyDeviation(dr.fsk.freqDev);
895 :
896 0 : } else if(modem == RADIOLIB_MODEM_LORA) {
897 : // set the spreading factor
898 0 : state = this->setSpreadingFactor(dr.lora.spreadingFactor);
899 0 : RADIOLIB_ASSERT(state);
900 :
901 : // set the bandwidth
902 0 : state = this->setBandwidth(dr.lora.bandwidth);
903 0 : RADIOLIB_ASSERT(state);
904 :
905 : // set the coding rate
906 0 : state = this->setCodingRate(dr.lora.codingRate);
907 :
908 0 : } else if(modem == RADIOLIB_MODEM_LRFHSS) {
909 : // set the basic config
910 0 : state = this->setLrFhssConfig(dr.lrFhss.bw, dr.lrFhss.cr);
911 0 : RADIOLIB_ASSERT(state);
912 :
913 : // set hopping grid
914 0 : this->lrFhssGrid = dr.lrFhss.narrowGrid ? RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_NON_FCC : RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_FCC;
915 :
916 : }
917 :
918 0 : return(state);
919 : }
920 :
921 1 : int16_t LR2021::checkDataRate(DataRate_t dr, ModemType_t modem) {
922 1 : int16_t state = RADIOLIB_ERR_UNKNOWN;
923 :
924 : // retrieve modem if not supplied
925 1 : if(modem == RADIOLIB_MODEM_NONE) {
926 1 : state = this->getModem(&modem);
927 1 : RADIOLIB_ASSERT(state);
928 : }
929 :
930 : // select interpretation based on modem
931 0 : if(modem == RADIOLIB_MODEM_FSK) {
932 0 : RADIOLIB_CHECK_RANGE(dr.fsk.bitRate, 0.5f, 2000.0f, RADIOLIB_ERR_INVALID_BIT_RATE);
933 0 : RADIOLIB_CHECK_RANGE(dr.fsk.freqDev, 0.6f, 500.0f, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
934 0 : return(RADIOLIB_ERR_NONE);
935 :
936 0 : } else if(modem == RADIOLIB_MODEM_LORA) {
937 0 : RADIOLIB_CHECK_RANGE(dr.lora.spreadingFactor, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
938 0 : RADIOLIB_CHECK_RANGE(dr.lora.bandwidth, 0.0f, 510.0f, RADIOLIB_ERR_INVALID_BANDWIDTH);
939 0 : RADIOLIB_CHECK_RANGE(dr.lora.codingRate, 4, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
940 0 : return(RADIOLIB_ERR_NONE);
941 :
942 : }
943 :
944 0 : return(state);
945 : }
946 :
947 0 : int16_t LR2021::setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount, uint16_t hopSeed) {
948 : // check active modem
949 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
950 0 : int16_t state = getPacketType(&type);
951 0 : RADIOLIB_ASSERT(state);
952 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_LR_FHSS) {
953 0 : return(RADIOLIB_ERR_WRONG_MODEM);
954 : }
955 :
956 : // check and cache all parameters
957 0 : RADIOLIB_CHECK_RANGE((int8_t)cr, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_CR_5_6, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_CR_1_3, RADIOLIB_ERR_INVALID_CODING_RATE);
958 0 : this->lrFhssCr = cr;
959 0 : RADIOLIB_CHECK_RANGE((int8_t)bw, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_BW_39_06, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_BW_1574_2, RADIOLIB_ERR_INVALID_BANDWIDTH);
960 0 : this->lrFhssBw = bw;
961 0 : RADIOLIB_CHECK_RANGE(hdrCount, 1, 4, RADIOLIB_ERR_INVALID_BIT_RANGE);
962 0 : this->lrFhssHdrCount = hdrCount;
963 0 : RADIOLIB_CHECK_RANGE((int16_t)hopSeed, (int16_t)0x000, (int16_t)0x1FF, RADIOLIB_ERR_INVALID_DATA_SHAPING);
964 0 : this->lrFhssHopSeq = hopSeed;
965 0 : return(RADIOLIB_ERR_NONE);
966 : }
967 :
968 0 : int16_t LR2021::setRxBoostedGainMode(uint8_t level) {
969 0 : if(this->highFreq) {
970 0 : this->gainModeHf = level;
971 : } else {
972 0 : this->gainModeLf = level;
973 : }
974 0 : return(this->setRxPath(this->highFreq ? RADIOLIB_LR2021_RX_PATH_HF : RADIOLIB_LR2021_RX_PATH_LF, this->highFreq ? this->gainModeHf : this->gainModeLf));
975 : }
976 :
977 0 : int16_t LR2021::setPacketMode(uint8_t mode, uint8_t len) {
978 : // check active modem
979 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
980 0 : int16_t state = getPacketType(&type);
981 0 : RADIOLIB_ASSERT(state);
982 0 : if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
983 : // set requested packet mode
984 0 : state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, mode, len, this->crcTypeGFSK, this->whitening);
985 0 : RADIOLIB_ASSERT(state);
986 :
987 : // update cached value
988 0 : this->packetType = mode;
989 0 : this->implicitLen = len;
990 0 : return(state);
991 :
992 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
993 : // set requested packet mode
994 0 : state = setOokPacketParams(this->preambleLengthGFSK, this->addrComp, mode, len, this->crcTypeGFSK, this->whitening);
995 0 : RADIOLIB_ASSERT(state);
996 :
997 : // update cached value
998 0 : this->packetType = mode;
999 0 : this->implicitLen = len;
1000 0 : return(state);
1001 :
1002 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
1003 0 : state = setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, mode == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, len);
1004 0 : RADIOLIB_ASSERT(state);
1005 :
1006 0 : this->packetType = mode;
1007 0 : this->implicitLen = len;
1008 0 : return(state);
1009 :
1010 0 : } else if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
1011 : // LoRa mode does not have fixed/variable packet length mode, but it does have implicit/explicit header
1012 : // in terms of packet length, that is equivalent with fixed/variable packets, so let's assume that's what the user meant
1013 0 : if(mode == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) {
1014 0 : return(this->implicitHeader(len));
1015 : }
1016 0 : return(this->explicitHeader());
1017 :
1018 : }
1019 :
1020 :
1021 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1022 : }
1023 :
1024 0 : int16_t LR2021::setLoRaHeaderType(uint8_t hdrType, size_t len) {
1025 0 : uint8_t modem = RADIOLIB_LR2021_PACKET_TYPE_NONE;
1026 0 : int16_t state = getPacketType(&modem);
1027 0 : RADIOLIB_ASSERT(state);
1028 0 : if(modem != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
1029 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1030 : }
1031 :
1032 : // set requested packet mode
1033 0 : state = setLoRaPacketParams(this->preambleLengthLoRa, hdrType, len, this->crcTypeLoRa, this->invertIQEnabled);
1034 0 : RADIOLIB_ASSERT(state);
1035 :
1036 : // update cached value
1037 0 : this->headerType = hdrType;
1038 0 : this->implicitLen = len;
1039 :
1040 0 : return(state);
1041 : }
1042 :
1043 0 : int16_t LR2021::implicitHeader(size_t len) {
1044 0 : return(this->setLoRaHeaderType(RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT, len));
1045 : }
1046 :
1047 0 : int16_t LR2021::explicitHeader() {
1048 0 : return(this->setLoRaHeaderType(RADIOLIB_LRXXXX_LORA_HEADER_EXPLICIT));
1049 : }
1050 :
1051 0 : int16_t LR2021::setNodeAddress(uint8_t nodeAddr) {
1052 : // check active modem
1053 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
1054 0 : int16_t state = getPacketType(&type);
1055 0 : RADIOLIB_ASSERT(state);
1056 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
1057 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1058 : }
1059 :
1060 : // enable address filtering (node only)
1061 0 : this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_NODE;
1062 0 : state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType,
1063 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
1064 0 : RADIOLIB_ASSERT(state);
1065 :
1066 : // set node address
1067 0 : this->node = nodeAddr;
1068 0 : return(setGfskAddress(this->node, 0));
1069 : }
1070 :
1071 0 : int16_t LR2021::setBroadcastAddress(uint8_t broadAddr) {
1072 : // check active modem
1073 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
1074 0 : int16_t state = getPacketType(&type);
1075 0 : RADIOLIB_ASSERT(state);
1076 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
1077 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1078 : }
1079 :
1080 : // enable address filtering (node and broadcast)
1081 0 : this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_NODE_BROADCAST;
1082 0 : state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType,
1083 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
1084 0 : RADIOLIB_ASSERT(state);
1085 :
1086 : // set node and broadcast address
1087 0 : return(setGfskAddress(this->node, broadAddr));
1088 : }
1089 :
1090 0 : int16_t LR2021::disableAddressFiltering() {
1091 : // check active modem
1092 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
1093 0 : int16_t state = getPacketType(&type);
1094 0 : RADIOLIB_ASSERT(state);
1095 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
1096 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1097 : }
1098 :
1099 : // disable address filtering
1100 0 : this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_DISABLED;
1101 0 : return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType,
1102 0 : (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
1103 : }
1104 :
1105 0 : int16_t LR2021::ookDetector(uint16_t pattern, uint8_t len, uint8_t repeats, bool syncRaw, bool rising, uint8_t sofLen) {
1106 : // check active modem
1107 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
1108 0 : int16_t state = getPacketType(&type);
1109 0 : RADIOLIB_ASSERT(state);
1110 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_OOK) {
1111 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1112 : }
1113 :
1114 0 : return(setOokDetector(pattern, len - 1, repeats, syncRaw, rising, sofLen));
1115 : }
1116 :
1117 0 : int16_t LR2021::setOokDetectionThreshold(int16_t level) {
1118 0 : int16_t levelRaw = 64 + level;
1119 0 : return(this->writeRegMemMask32(RADIOLIB_LR2021_REG_OOK_DETECTION_THRESHOLD, (0x7FUL << 20), (uint32_t)levelRaw << 20));
1120 : }
1121 :
1122 0 : int16_t LR2021::setSideDetector(const LR2021LoRaSideDetector_t* cfg, size_t numDetectors) {
1123 : // some basic sanity checks
1124 0 : if((cfg == nullptr) || (numDetectors == 0)) {
1125 0 : return(RADIOLIB_ERR_NONE);
1126 : }
1127 :
1128 0 : if(numDetectors > 3) {
1129 0 : return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
1130 : }
1131 :
1132 : // if bandwidth is higher than 500 kHz, at most 2 side detectors are allowed
1133 0 : if((this->bandwidthKhz > 500.0f) && (numDetectors > 2)) {
1134 0 : return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
1135 : }
1136 :
1137 : // if the primary spreading factor is 10, 11 or 12, at most 2 side detectors are allowed
1138 0 : if((this->spreadingFactor >= 10) && (numDetectors > 2)) {
1139 0 : return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
1140 : }
1141 :
1142 : // condition of the primary spreading factor being the smallest/largest is not checked
1143 : // this is intentional, because it depends on whether the user wants to start Rx or CAD
1144 :
1145 0 : uint8_t detectors[3] = { 0 };
1146 0 : uint8_t syncWords[3] = { 0 };
1147 0 : uint8_t minSf = this->spreadingFactor;
1148 0 : uint32_t sumDetFactors = 0;
1149 0 : for(size_t i = 0; i < numDetectors; i++) {
1150 : // all side-detector spreading factors must be higher than the primary one
1151 : //! \todo [LR2021] implement multi-SF for CAD (main SF must be smallest!)
1152 0 : if(this->spreadingFactor >= cfg[i].sf) {
1153 0 : return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
1154 : }
1155 :
1156 : // the difference between maximum and minimum spreading factor used must be less than or equal to 4
1157 0 : if(cfg[i].sf - minSf > 4) {
1158 0 : return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
1159 : }
1160 :
1161 0 : if(cfg[i].sf < minSf) { minSf = cfg[i].sf; }
1162 :
1163 0 : detectors[i] = cfg[i].sf << 4 | cfg[i].ldro << 2 | cfg[i].invertIQ;
1164 0 : syncWords[i] = cfg[i].syncWord;
1165 0 : sumDetFactors += 10 + (((cfg[i].sf - 5) >> 1) << 1);
1166 : }
1167 :
1168 : // sum of detection factors multiplied by BW must be smaller than 32E6
1169 0 : if(sumDetFactors*(uint32_t)this->bandwidthKhz >= 32000UL) {
1170 0 : return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
1171 : }
1172 :
1173 : // all spreading factors must be different
1174 0 : if(numDetectors >= 2) {
1175 0 : if(cfg[0].sf == cfg[1].sf) {
1176 0 : return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
1177 : }
1178 : }
1179 :
1180 0 : if(numDetectors == 3) {
1181 0 : if((cfg[1].sf == cfg[2].sf) || (cfg[0].sf == cfg[2].sf)) {
1182 0 : return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
1183 : }
1184 : }
1185 :
1186 : // check active modem
1187 0 : uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
1188 0 : int16_t state = getPacketType(&type);
1189 0 : RADIOLIB_ASSERT(state);
1190 0 : if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
1191 0 : return(RADIOLIB_ERR_WRONG_MODEM);
1192 : }
1193 :
1194 0 : state = setLoRaSideDetConfig(detectors, numDetectors);
1195 0 : RADIOLIB_ASSERT(state);
1196 :
1197 0 : return(setLoRaSideDetSyncword(syncWords, numDetectors));
1198 : }
1199 :
1200 0 : int16_t LR2021::setGain(uint8_t gain) {
1201 0 : if(gain > 13) {
1202 0 : return(RADIOLIB_ERR_INVALID_GAIN);
1203 : }
1204 0 : return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_AGC_GAIN_MANUAL, true, &gain, sizeof(gain)));
1205 : }
1206 :
1207 : #endif
|