Updated and Validated
[betaflight.git] / src / test / unit / rx_spi_expresslrs_unittest.cc
bloba5ac4880ffe317e3e3581ea2fc36adb37cb57939
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
19 * Based on https://github.com/ExpressLRS/ExpressLRS
20 * Thanks to AlessandroAU, original creator of the ExpressLRS project.
23 #include <stdint.h>
24 #include <stdbool.h>
26 #include <limits.h>
28 extern "C" {
29 #include "platform.h"
31 #include "build/atomic.h"
33 #include "drivers/io.h"
34 #include "common/filter.h"
36 #include "pg/pg.h"
37 #include "pg/pg_ids.h"
38 #include "pg/rx_spi.h"
39 #include "pg/rx_spi_expresslrs.h"
41 #include "rx/rx_spi.h"
42 #include "rx/expresslrs.h"
43 #include "rx/expresslrs_impl.h"
45 #include "drivers/rx/rx_sx127x.h"
46 #include "drivers/rx/rx_sx1280.h"
48 extern uint8_t fhssSequence[ELRS_NR_SEQUENCE_ENTRIES];
49 extern uint16_t crc14tab[ELRS_CRC_LEN];
51 extern elrsReceiver_t receiver;
52 static const elrsReceiver_t empty = elrsReceiver_t();
54 static rxRuntimeState_t config = rxRuntimeState_t();
55 static rxSpiExtiConfig_t extiConfig;
56 static const rxSpiConfig_t injectedConfig = {
57 .extiIoTag = IO_TAG(PA0),
61 #include "unittest_macros.h"
62 #include "gtest/gtest.h"
64 //make clean test_rx_spi_expresslrs_unittest
65 TEST(RxSpiExpressLrsUnitTest, TestCrc14)
67 uint16_t expectedCrc14Tab[ELRS_CRC_LEN] = {
68 0,28247,62201,40110,52133,42482,14684,22283,
69 38730,63773,26035,3044,23791,12984,44566,49217,
70 11924,16579,56429,45626,58673,35686,6088,31135,
71 47582,55177,19239,9584,29307,7212,32898,61141,
72 29567,7464,33158,61393,47322,54925,18979,9332,
73 58421,35426,5836,30875,12176,16839,56681,45886,
74 24043,13244,44818,49477,38478,63513,25783,2784,
75 51873,42230,14424,22031,260,28499,62461,40362,
76 51369,42750,14928,21511,780,27995,61941,40866,
77 24547,12724,44314,49997,37958,64017,26303,2280,
78 58941,34922,5316,31379,11672,17359,57185,45366,
79 29047,7968,33678,60889,47826,54405,18475,9852,
80 48086,54657,18735,10104,28787,7716,33418,60637,
81 11420,17099,56933,45106,59193,35182,5568,31639,
82 38210,64277,26555,2540,24295,12464,44062,49737,
83 520,27743,61681,40614,51629,43002,15188,21763,
84 37202,65285,25515,3580,23287,13472,43022,50777,
85 1560,26703,62689,39606,52669,41962,16196,20755,
86 49094,53649,19775,9064,29795,6708,34458,59597,
87 10380,18139,55925,46114,58153,36222,4560,32647,
88 57901,35962,4308,32387,10632,18399,56177,46374,
89 30055,6960,34718,59849,48834,53397,19515,8812,
90 52409,41710,15936,20503,1820,26955,62949,39858,
91 23539,13732,43274,51037,36950,65025,25263,3320,
92 23035,14252,43778,50517,37470,64521,24743,3824,
93 52913,41190,15432,21023,1300,27459,63469,39354,
94 30575,6456,34198,60353,48330,53917,20019,8292,
95 57381,36466,4828,31883,11136,17879,55673,46894,
96 10884,17619,55421,46634,57633,36726,5080,32143,
97 48590,54169,20279,8544,30315,6204,33938,60101,
98 1040,27207,63209,39102,53173,41442,15692,21275,
99 37722,64781,24995,4084,22783,13992,43526,50257
102 generateCrc14Table();
103 for (int i = 0; i < ELRS_CRC_LEN; i++) {
104 EXPECT_EQ(expectedCrc14Tab[i], crc14tab[i]);
108 TEST(RxSpiExpressLrsUnitTest, TestFHSSTable)
110 const uint8_t UID[6] = {1, 2, 3, 4, 5, 6};
112 const uint8_t expectedSequence[2][ELRS_NR_SEQUENCE_ENTRIES] = {
114 40, 43, 39, 18, 52, 19, 36, 7, 1, 12,
115 71, 5, 42, 46, 50, 28, 49, 33, 76, 51,
116 60, 70, 47, 61, 0, 55, 72, 37, 53, 25,
117 3, 11, 41, 13, 35, 27, 9, 75, 48, 77,
118 73, 74, 69, 58, 14, 31, 10, 59, 66, 4,
119 78, 17, 44, 54, 29, 57, 21, 64, 22, 67,
120 62, 56, 15, 79, 6, 34, 23, 30, 32, 2,
121 68, 8, 63, 65, 45, 20, 24, 26, 16, 38,
122 40, 8, 52, 29, 57, 10, 6, 26, 19, 75,
123 21, 24, 1, 9, 50, 32, 69, 67, 2, 59,
124 28, 48, 77, 60, 41, 49, 68, 4, 5, 3,
125 44, 78, 58, 31, 16, 62, 35, 45, 73, 11,
126 33, 46, 42, 36, 64, 7, 34, 53, 17, 25,
127 37, 38, 54, 55, 15, 76, 18, 43, 23, 12,
128 39, 51, 22, 79, 74, 63, 27, 66, 65, 47,
129 70, 0, 30, 61, 13, 56, 14, 72, 71, 20,
130 40, 71, 68, 12, 57, 45, 10, 53, 21, 15,
131 69, 26, 54, 55, 73, 47, 35, 77, 1, 31,
132 20, 0, 38, 76, 5, 60, 6, 79, 3, 16,
133 50, 17, 52, 62, 18, 46, 28, 39, 29, 51,
134 43, 34, 49, 56, 32, 61, 74, 58, 25, 44,
135 2, 19, 65, 4, 13, 67, 11, 30, 66, 64,
136 36, 24, 75, 33, 59, 7, 41, 70, 48, 14,
137 42, 37, 8, 23, 78, 63, 22, 9, 72, 27
140 20, 37, 1, 3, 7, 26, 36, 29, 15, 35,
141 33, 24, 10, 34, 13, 31, 22, 9, 28, 23,
142 17, 38, 6, 27, 0, 32, 11, 5, 18, 25,
143 2, 4, 12, 19, 16, 8, 30, 14, 21, 39,
144 20, 2, 14, 7, 13, 33, 32, 28, 21, 11,
145 25, 17, 22, 9, 3, 4, 0, 31, 35, 38,
146 10, 34, 26, 39, 36, 6, 19, 16, 30, 27,
147 15, 24, 18, 1, 23, 37, 29, 8, 12, 5,
148 20, 19, 24, 29, 27, 2, 22, 14, 0, 3,
149 23, 13, 12, 35, 4, 25, 38, 18, 33, 36,
150 21, 16, 5, 31, 9, 32, 11, 1, 6, 7,
151 10, 15, 26, 34, 39, 37, 28, 17, 30, 8,
152 20, 7, 4, 24, 19, 16, 8, 13, 15, 10,
153 14, 36, 34, 0, 17, 12, 28, 21, 39, 22,
154 3, 2, 32, 33, 27, 6, 37, 18, 31, 38,
155 23, 25, 26, 30, 9, 1, 35, 5, 11, 29,
156 20, 1, 35, 22, 0, 10, 11, 27, 18, 37,
157 21, 31, 9, 19, 30, 17, 5, 38, 29, 36,
158 3, 2, 25, 34, 23, 6, 15, 4, 16, 26,
159 12, 24, 14, 13, 39, 8, 32, 7, 28, 33,
160 20, 36, 13, 5, 39, 37, 15, 8, 9, 4,
161 22, 12, 1, 6, 32, 25, 17, 18, 27, 28,
162 23, 19, 26, 3, 38, 16, 2, 34, 14, 30,
163 10, 11, 7, 0, 35, 24, 21, 33, 31, 29
167 fhssGenSequence(UID, ISM2400);
168 for (int i = 0; i < ELRS_NR_SEQUENCE_ENTRIES; i++) {
169 EXPECT_EQ(expectedSequence[0][i], fhssSequence[i]);
172 fhssGenSequence(UID, FCC915);
173 for (int i = 0; i < ELRS_NR_SEQUENCE_ENTRIES; i++) {
174 EXPECT_EQ(expectedSequence[1][i], fhssSequence[i]);
178 TEST(RxSpiExpressLrsUnitTest, TestInitUnbound)
180 const uint8_t bindUID[6] = {0, 1, 2, 3, 4, 5};
182 receiver = empty;
183 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
185 //check initialization of elrsReceiver_t
186 EXPECT_TRUE(receiver.inBindingMode);
187 EXPECT_EQ(IO_NONE, receiver.resetPin);
188 EXPECT_EQ(IO_NONE, receiver.busyPin);
189 for (int i = 0; i < 6; i++) {
190 EXPECT_EQ(bindUID[i], receiver.UID[i]);
192 EXPECT_EQ(0, receiver.nonceRX);
193 EXPECT_EQ(0, receiver.freqOffset);
194 EXPECT_EQ(0, receiver.lastValidPacketMs);
196 const uint32_t initialFrequencies[7] = {
197 FREQ_HZ_TO_REG_VAL_900(433920000),
198 FREQ_HZ_TO_REG_VAL_900(921500000),
199 FREQ_HZ_TO_REG_VAL_900(433925000),
200 FREQ_HZ_TO_REG_VAL_900(866425000),
201 FREQ_HZ_TO_REG_VAL_900(866425000),
202 FREQ_HZ_TO_REG_VAL_900(915500000),
203 FREQ_HZ_TO_REG_VAL_24(2440400000)
206 for (int i = 0; i < 7; i++) {
207 receiver = empty;
208 rxExpressLrsSpiConfigMutable()->domain = i;
209 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
210 EXPECT_EQ(initialFrequencies[i], receiver.currentFreq);
213 // for unbound we need to initialize in 50HZ mode
214 receiver = empty;
215 rxExpressLrsSpiConfigMutable()->rateIndex = 1;
216 rxExpressLrsSpiConfigMutable()->domain = FCC915;
217 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
218 EXPECT_EQ(airRateConfig[0][2].index, receiver.modParams->index);
219 EXPECT_EQ(airRateConfig[0][2].enumRate, receiver.modParams->enumRate);
220 EXPECT_EQ(airRateConfig[0][2].bw, receiver.modParams->bw);
221 EXPECT_EQ(airRateConfig[0][2].sf, receiver.modParams->sf);
222 EXPECT_EQ(airRateConfig[0][2].cr, receiver.modParams->cr);
223 EXPECT_EQ(airRateConfig[0][2].interval, receiver.modParams->interval);
224 EXPECT_EQ(airRateConfig[0][2].tlmInterval, receiver.modParams->tlmInterval);
225 EXPECT_EQ(airRateConfig[0][2].fhssHopInterval, receiver.modParams->fhssHopInterval);
226 EXPECT_EQ(airRateConfig[0][2].preambleLen, receiver.modParams->preambleLen);
228 receiver = empty;
229 rxExpressLrsSpiConfigMutable()->rateIndex = 1;
230 rxExpressLrsSpiConfigMutable()->domain = ISM2400;
231 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
232 EXPECT_EQ(airRateConfig[1][3].index, receiver.modParams->index);
233 EXPECT_EQ(airRateConfig[1][3].enumRate, receiver.modParams->enumRate);
234 EXPECT_EQ(airRateConfig[1][3].bw, receiver.modParams->bw);
235 EXPECT_EQ(airRateConfig[1][3].sf, receiver.modParams->sf);
236 EXPECT_EQ(airRateConfig[1][3].cr, receiver.modParams->cr);
237 EXPECT_EQ(airRateConfig[1][3].interval, receiver.modParams->interval);
238 EXPECT_EQ(airRateConfig[1][3].tlmInterval, receiver.modParams->tlmInterval);
239 EXPECT_EQ(airRateConfig[1][3].fhssHopInterval, receiver.modParams->fhssHopInterval);
240 EXPECT_EQ(airRateConfig[1][3].preambleLen, receiver.modParams->preambleLen);
242 //check switch mode
243 receiver = empty;
244 rxExpressLrsSpiConfigMutable()->switchMode = SM_HYBRID;
245 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
246 EXPECT_EQ(16, config.channelCount);
247 receiver = empty;
248 rxExpressLrsSpiConfigMutable()->switchMode = SM_HYBRID_WIDE;
249 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
250 EXPECT_EQ(16, config.channelCount);
253 TEST(RxSpiExpressLrsUnitTest, TestInitBound)
255 const uint8_t validUID[6] = {0, 0, 1, 2, 3, 4};
256 receiver = empty;
257 memcpy(rxExpressLrsSpiConfigMutable()->UID, validUID, 6);
259 // check mod params
260 for (int i = 0; i < ELRS_RATE_MAX; i++) {
261 receiver = empty;
262 rxExpressLrsSpiConfigMutable()->rateIndex = i;
263 rxExpressLrsSpiConfigMutable()->domain = FCC915;
264 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
265 EXPECT_EQ(airRateConfig[0][i].index, receiver.modParams->index);
266 EXPECT_EQ(airRateConfig[0][i].enumRate, receiver.modParams->enumRate);
267 EXPECT_EQ(airRateConfig[0][i].bw, receiver.modParams->bw);
268 EXPECT_EQ(airRateConfig[0][i].sf, receiver.modParams->sf);
269 EXPECT_EQ(airRateConfig[0][i].cr, receiver.modParams->cr);
270 EXPECT_EQ(airRateConfig[0][i].interval, receiver.modParams->interval);
271 EXPECT_EQ(airRateConfig[0][i].tlmInterval, receiver.modParams->tlmInterval);
272 EXPECT_EQ(airRateConfig[0][i].fhssHopInterval, receiver.modParams->fhssHopInterval);
273 EXPECT_EQ(airRateConfig[0][i].preambleLen, receiver.modParams->preambleLen);
275 receiver = empty;
276 rxExpressLrsSpiConfigMutable()->rateIndex = i;
277 rxExpressLrsSpiConfigMutable()->domain = ISM2400;
278 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
279 EXPECT_EQ(airRateConfig[1][i].index, receiver.modParams->index);
280 EXPECT_EQ(airRateConfig[1][i].enumRate, receiver.modParams->enumRate);
281 EXPECT_EQ(airRateConfig[1][i].bw, receiver.modParams->bw);
282 EXPECT_EQ(airRateConfig[1][i].sf, receiver.modParams->sf);
283 EXPECT_EQ(airRateConfig[1][i].cr, receiver.modParams->cr);
284 EXPECT_EQ(airRateConfig[1][i].interval, receiver.modParams->interval);
285 EXPECT_EQ(airRateConfig[1][i].tlmInterval, receiver.modParams->tlmInterval);
286 EXPECT_EQ(airRateConfig[1][i].fhssHopInterval, receiver.modParams->fhssHopInterval);
287 EXPECT_EQ(airRateConfig[1][i].preambleLen, receiver.modParams->preambleLen);
290 expressLrsSpiInit(&injectedConfig, &config, &extiConfig);
291 EXPECT_FALSE(receiver.inBindingMode);
292 for (int i = 0; i < 6; i++) {
293 EXPECT_EQ(validUID[i], receiver.UID[i]);
297 TEST(RxSpiExpressLrsUnitTest, TestLQCalc)
299 lqReset();
300 for (int i = 1; i <= 100; i++) {
301 lqNewPeriod();
302 lqIncrease();
303 EXPECT_EQ(i, lqGet());
305 lqNewPeriod();
306 lqIncrease();
307 EXPECT_EQ(100, lqGet());
308 for (int i = 99; i >= 0; i--) {
309 lqNewPeriod();
310 EXPECT_EQ(i, lqGet());
312 lqNewPeriod();
313 EXPECT_EQ(0, lqGet());
314 lqReset();
315 lqNewPeriod();
316 EXPECT_EQ(0, lqGet());
317 lqIncrease();
318 EXPECT_EQ(1, lqGet());
321 TEST(RxSpiExpressLrsUnitTest, Test1bSwitchDecode)
323 EXPECT_EQ(1000, convertSwitch1b(0));
324 EXPECT_EQ(2000, convertSwitch1b(1));
325 EXPECT_EQ(2000, convertSwitch1b(2));
326 EXPECT_EQ(2000, convertSwitch1b(255));
329 TEST(RxSpiExpressLrsUnitTest, Test3bSwitchDecode)
331 EXPECT_EQ(1000, convertSwitch3b(0));
332 EXPECT_EQ(1275, convertSwitch3b(1));
333 EXPECT_EQ(1425, convertSwitch3b(2));
334 EXPECT_EQ(1575, convertSwitch3b(3));
335 EXPECT_EQ(1725, convertSwitch3b(4));
336 EXPECT_EQ(2000, convertSwitch3b(5));
337 EXPECT_EQ(1500, convertSwitch3b(6));
338 EXPECT_EQ(1500, convertSwitch3b(7));
339 EXPECT_EQ(1500, convertSwitch3b(8));
340 EXPECT_EQ(1500, convertSwitch3b(123));
341 EXPECT_EQ(1500, convertSwitch3b(255));
344 TEST(RxSpiExpressLrsUnitTest, Test4bSwitchDecode)
346 EXPECT_EQ(1000, convertSwitchNb(0, 15));
347 EXPECT_EQ(1066, convertSwitchNb(1, 15));
348 EXPECT_EQ(1133, convertSwitchNb(2, 15));
349 EXPECT_EQ(1200, convertSwitchNb(3, 15));
350 EXPECT_EQ(1266, convertSwitchNb(4, 15));
351 EXPECT_EQ(1333, convertSwitchNb(5, 15));
352 EXPECT_EQ(1400, convertSwitchNb(6, 15));
353 EXPECT_EQ(1466, convertSwitchNb(7, 15));
354 EXPECT_EQ(1533, convertSwitchNb(8, 15));
355 EXPECT_EQ(1600, convertSwitchNb(9, 15));
356 EXPECT_EQ(1666, convertSwitchNb(10, 15));
357 EXPECT_EQ(1733, convertSwitchNb(11, 15));
358 EXPECT_EQ(1800, convertSwitchNb(12, 15));
359 EXPECT_EQ(1866, convertSwitchNb(13, 15));
360 EXPECT_EQ(1933, convertSwitchNb(14, 15));
361 EXPECT_EQ(2000, convertSwitchNb(15, 15));
362 EXPECT_EQ(1500, convertSwitchNb(16, 15));
363 EXPECT_EQ(1500, convertSwitchNb(255, 15));
366 TEST(RxSpiExpressLrsUnitTest, TestAnalogDecode)
368 EXPECT_EQ(988, convertAnalog(172));
369 EXPECT_EQ(1500, convertAnalog(992));
370 EXPECT_EQ(2012, convertAnalog(1811));
373 // STUBS
375 extern "C" {
377 uint8_t systemState;
378 int16_t *debug;
379 uint8_t debugMode;
381 rssiSource_e rssiSource;
382 linkQualitySource_e linkQualitySource;
383 void setRssi(uint16_t , rssiSource_e ) {}
384 void setRssiDirect(uint16_t , rssiSource_e ) {}
386 uint32_t micros(void) { return 0; }
387 uint32_t millis(void) { return 0; }
389 bool IORead(IO_t ) { return true; }
390 IO_t IOGetByTag(ioTag_t ) { return (IO_t)1; }
391 void IOHi(IO_t ) {}
392 void IOLo(IO_t ) {}
394 void saveConfigAndNotify(void) {}
396 void rxSpiCommonIOInit(const rxSpiConfig_t *) {}
397 void rxSpiLedBlinkRxLoss(rx_spi_received_e ) {}
398 void rxSpiLedBlinkBind(void) {}
399 bool rxSpiCheckBindRequested(bool)
401 return false;
403 bool rxSpiExtiConfigured(void) { return true; }
405 bool sx1280IsBusy(void) { return false; }
406 void sx1280Config(const sx1280LoraBandwidths_e , const sx1280LoraSpreadingFactors_e , const sx1280LoraCodingRates_e , const uint32_t , const uint8_t , const bool ) {}
407 void sx1280StartReceiving(void) {}
408 void sx1280ISR(void) {}
409 bool rxSpiGetExtiState(void) { return false; }
410 void sx1280HandleFromTock(void) {}
411 bool sx1280HandleFromTick(void) { return false; }
412 void sx1280TransmitData(const uint8_t *, const uint8_t ) {}
413 void sx1280ReceiveData(uint8_t *, const uint8_t ) {}
414 void sx1280SetFrequencyReg(const uint32_t ) {}
415 void sx1280GetLastPacketStats(int8_t *rssi, int8_t *snr)
417 *rssi = 0;
418 *snr = 0;
420 void sx1280AdjustFrequency(int32_t , const uint32_t ) {}
421 bool sx1280Init(IO_t , IO_t ) { return true; }
423 void sx127xConfig(const sx127xBandwidth_e , const sx127xSpreadingFactor_e , const sx127xCodingRate_e , const uint32_t , const uint8_t , const bool ) {}
424 void sx127xStartReceiving(void) {}
425 uint8_t sx127xISR(uint32_t *timestamp)
427 *timestamp = 0;
428 return 0;
430 void sx127xTransmitData(const uint8_t *, const uint8_t ) {}
431 void sx127xReceiveData(uint8_t *, const uint8_t ) {}
432 void sx127xSetFrequencyReg(const uint32_t ) {}
433 void sx127xGetLastPacketStats(int8_t *rssi, int8_t *snr)
435 *rssi = 0;
436 *snr = 0;
438 void sx127xAdjustFrequency(int32_t , const uint32_t ) {}
439 bool sx127xInit(IO_t , IO_t ) { return true; }
441 int scaleRange(int x, int srcFrom, int srcTo, int destFrom, int destTo) {
442 long int a = ((long int) destTo - (long int) destFrom) * ((long int) x - (long int) srcFrom);
443 long int b = (long int) srcTo - (long int) srcFrom;
444 return (a / b) + destFrom;
447 void expressLrsInitialiseTimer(TIM_TypeDef *, elrsReceiver_t *) {}
448 void expressLrsTimerEnableIRQs(void) {}
449 void expressLrsUpdateTimerInterval(uint16_t ) {}
450 void expressLrsUpdatePhaseShift(int32_t ) {}
451 void expressLrsTimerIncreaseFrequencyOffset(void) {}
452 void expressLrsTimerDecreaseFrequencyOffset(void) {}
453 void expressLrsTimerResetFrequencyOffset(void) {}
454 void expressLrsTimerStop(void) {}
455 void expressLrsTimerResume(void) {}
456 bool expressLrsTimerIsRunning(void) { return true; }
457 void expressLrsTimerDebug(void) {}
459 int32_t simpleLPFilterUpdate(simpleLowpassFilter_t *, int32_t ) { return 0; }
460 void simpleLPFilterInit(simpleLowpassFilter_t *, int32_t , int32_t ) {}
461 void dbgPinHi(int ) {}
462 void dbgPinLo(int ) {}
464 void initTelemetry(void) {}
465 bool getNextTelemetryPayload(uint8_t *, uint8_t **) { return false; }
467 void setTelemetryDataToTransmit(const uint8_t , uint8_t* , const uint8_t ) {}
468 bool isTelemetrySenderActive(void) { return false; }
469 void getCurrentTelemetryPayload(uint8_t *, uint8_t *, uint8_t **) {}
470 void confirmCurrentTelemetryPayload(const bool ) {}
471 void updateTelemetryRate(const uint16_t , const uint8_t , const uint8_t ) {}