STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_main.cpp
Go to the documentation of this file.
1// SX1262 start-up and transmit, against a pretend chip that keeps the
2// registers, IRQ flags and device errors the way the datasheet describes.
3#include <string.h>
4
7#include "unity.h"
8
9using namespace radio_link;
10
11namespace {
12
13class TxChip : public SpiBus {
14public:
15 static constexpr uint8_t kStatus = 0x24; // STBY_RC (2), no error
16 static constexpr unsigned kMaxCmds = 64;
17
18 void reset() {
19 never_ready = false;
21 device_errors = 0;
23 raise_timeout = false;
24 irq = 0;
25 tx_clamp = 0xC8; // reset values, register table p.66
26 tx_modulation = 0x01;
27 sync[0] = sync[1] = 0;
28 memset(buffer, 0, sizeof buffer);
29 transmitting = false;
30 n_cmds = 0;
31 }
32 // Test knobs.
34 uint8_t status;
35 uint16_t device_errors;
36 int polls_until_done; // GetIrqStatus polls after SetTx; < 0: never
37 bool raise_timeout; // the chip's own TX timeout fires instead
38 // What the chip saw.
39 uint16_t irq;
41 uint8_t buffer[256];
43 unsigned n_cmds;
44 uint8_t cmds[kMaxCmds][12]; // first 12 bytes of each command
45 uint16_t lens[kMaxCmds];
46
47 int find(uint8_t opcode, int from = 0) const {
48 for (unsigned i = static_cast<unsigned>(from); i < n_cmds; ++i) {
49 if (cmds[i][0] == opcode)
50 return static_cast<int>(i);
51 }
52 return -1;
53 }
54
55 bool wait_busy() override {
56 return !never_ready;
57 }
58 void transfer(const uint8_t* tx, uint8_t* rx, uint16_t len) override {
59 if (n_cmds < kMaxCmds) {
60 memcpy(cmds[n_cmds], tx, len < 12 ? len : 12);
61 lens[n_cmds++] = len;
62 }
63 memset(rx, 0xEE, len);
64 rx[1] = status;
65 const uint16_t addr =
66 len >= 3 ? static_cast<uint16_t>(tx[1] << 8 | tx[2]) : 0;
67 switch (tx[0]) {
68 case 0x1D: // ReadRegister
69 rx[4] = addr == 0x08D8 ? tx_clamp
70 : addr == 0x0889 ? tx_modulation
71 : 0;
72 break;
73 case 0x0D: // WriteRegister
74 if (addr == 0x08D8)
75 tx_clamp = tx[3];
76 if (addr == 0x0889)
77 tx_modulation = tx[3];
78 if (addr == 0x0740)
79 memcpy(sync, tx + 3, 2);
80 break;
81 case 0x17: // GetDeviceErrors
82 rx[2] = static_cast<uint8_t>(device_errors >> 8);
83 rx[3] = static_cast<uint8_t>(device_errors);
84 break;
85 case 0x07: // ClearDeviceErrors
86 break;
87 case 0x0E: // WriteBuffer
88 for (unsigned i = 2; i < len; ++i)
89 buffer[(tx[1] + i - 2) & 0xFF] = tx[i];
90 break;
91 case 0x83: // SetTx
92 transmitting = true;
93 break;
94 case 0x12: // GetIrqStatus
96 irq |= 1u << 9;
97 transmitting = false;
98 } else if (transmitting && polls_until_done >= 0 &&
99 polls_until_done-- == 0) {
100 irq |= 1u << 0;
101 transmitting = false;
102 }
103 rx[2] = static_cast<uint8_t>(irq >> 8);
104 rx[3] = static_cast<uint8_t>(irq);
105 break;
106 case 0x02: // ClearIrqStatus
107 irq = static_cast<uint16_t>(irq & ~(tx[1] << 8 | tx[2]));
108 break;
109 default:
110 break;
111 }
112 }
113};
114
115class Control : public RadioControl {
116public:
117 bool rx_switch = false;
119 unsigned delays = 0;
120 TxChip* chip = nullptr;
121 void set_rx_switch(bool receive) override {
122 rx_switch = receive;
123 }
124 void delay_ms(uint32_t ms) override {
125 delays += ms;
126 if (chip && chip->transmitting && rx_switch)
127 rx_switch_during_tx = true;
128 }
129};
130
131TxChip chip;
132Control control;
133
134} // namespace
135
136void setUp(void) {
137 chip.reset();
138 control = Control();
139 control.chip = &chip;
140}
141void tearDown(void) {
142}
143
145 // Independent Python: 915e6 << 25 / 32e6; p.41 formula.
146 TEST_ASSERT_EQUAL_HEX32(0x39300000, sx1262_frequency_word(915000000));
147 TEST_ASSERT_EQUAL_UINT32(164864, lora_time_on_air_us(kDefaultLora, 14));
148 TEST_ASSERT_EQUAL_UINT32(103424, lora_time_on_air_us(kDefaultLora, 1));
150 sf7.sf = 7;
151 TEST_ASSERT_EQUAL_UINT32(399616, lora_time_on_air_us(sf7, 255));
153 sf12.sf = 12; // 32.8 ms symbols: LDRO on
154 TEST_ASSERT_EQUAL_UINT32(991232, lora_time_on_air_us(sf12, 10));
155}
156
158 Sx1262Setup radio(chip, control, kDefaultLora);
159 TEST_ASSERT_EQUAL(SetupStep::Ok, radio.configure());
160 // 14.2/14.4 (p.99-100): standby, regulator, TCXO, calibrate, then packet
161 // type before modulation before packet params; ends listening.
162 const uint8_t order[] = {0x80, 0x96, 0x97, 0x89, 0x07, 0x9D, 0x8A,
163 0x86, 0x98, 0x95, 0x8E, 0x8F, 0x8B, 0x8C,
164 0x08, 0x1D, 0x0D, 0x0D, 0x17, 0x8C, 0x82};
165 TEST_ASSERT_EQUAL_UINT(sizeof order, chip.n_cmds);
166 for (unsigned i = 0; i < sizeof order; ++i) {
167 TEST_ASSERT_EQUAL_HEX8(order[i], chip.cmds[i][0]);
168 }
169 const uint8_t tcxo[] = {0x97, 0x02, 0x00, 0x01, 0x40}; // 1.8 V, 5 ms
170 TEST_ASSERT_EQUAL_HEX8_ARRAY(tcxo, chip.cmds[2], sizeof tcxo);
171 TEST_ASSERT_EQUAL_HEX8(0x7F, chip.cmds[3][1]); // calibrate all blocks
172 TEST_ASSERT_EQUAL_HEX8(0x01, chip.cmds[5][1]); // DIO2 drives RF switch
173 const uint8_t freq[] = {0x86, 0x39, 0x30, 0x00, 0x00};
174 TEST_ASSERT_EQUAL_HEX8_ARRAY(freq, chip.cmds[7], sizeof freq);
175 const uint8_t image[] = {0x98, 0xE1, 0xE9};
176 TEST_ASSERT_EQUAL_HEX8_ARRAY(image, chip.cmds[8], sizeof image);
177 const uint8_t pa[] = {0x95, 0x04, 0x07, 0x00, 0x01}; // +22 dBm, p.77
178 TEST_ASSERT_EQUAL_HEX8_ARRAY(pa, chip.cmds[9], sizeof pa);
179 const uint8_t power[] = {0x8E, 0x16, 0x04}; // +22 dBm, 200 us ramp
180 TEST_ASSERT_EQUAL_HEX8_ARRAY(power, chip.cmds[10], sizeof power);
181 const uint8_t mod[] = {0x8B, 0x09, 0x04, 0x01, 0x00}; // SF9 125k 4/5
182 TEST_ASSERT_EQUAL_HEX8_ARRAY(mod, chip.cmds[12], sizeof mod);
183 const uint8_t pkt[] = {0x8C, 0x00, 0x08, 0x00, 0xFF, 0x01, 0x00};
184 TEST_ASSERT_EQUAL_HEX8_ARRAY(pkt, chip.cmds[13], sizeof pkt); // CRC on
185 const uint8_t irq[] = {0x08, 0x02, 0x63, 0x02, 0x63, 0, 0, 0, 0};
186 TEST_ASSERT_EQUAL_HEX8_ARRAY(irq, chip.cmds[14], sizeof irq);
187 TEST_ASSERT_EQUAL_HEX8(0xDE, chip.tx_clamp); // 0xC8 | 0x1E, errata 15.2
188 TEST_ASSERT_EQUAL_HEX8(0x14, chip.sync[0]);
189 TEST_ASSERT_EQUAL_HEX8(0x24, chip.sync[1]);
190 const uint8_t rx[] = {0x82, 0xFF, 0xFF, 0xFF}; // continuous RX
191 TEST_ASSERT_EQUAL_HEX8_ARRAY(rx, chip.cmds[chip.n_cmds - 1], sizeof rx);
192 TEST_ASSERT_TRUE(control.rx_switch);
193}
194
196 chip.never_ready = true;
197 Sx1262Setup a(chip, control, kDefaultLora);
198 TEST_ASSERT_EQUAL(SetupStep::Standby, a.configure());
199
200 setUp();
201 chip.device_errors = 1u << 6; // PLL_LOCK_ERR
202 Sx1262Setup b(chip, control, kDefaultLora);
203 TEST_ASSERT_EQUAL(SetupStep::DeviceErrors, b.configure());
204 TEST_ASSERT_EQUAL_HEX16(0x0040, b.device_errors());
205 TEST_ASSERT_EQUAL(-1, chip.find(0x82)); // not left listening as if ok
206
207 setUp();
208 chip.status = 0x00; // no chip: MISO low
209 Sx1262Setup c(chip, control, kDefaultLora);
210 TEST_ASSERT_EQUAL(SetupStep::TxClamp, c.configure()); // first read
211}
212
214 Sx1262Setup radio(chip, control, kDefaultLora);
215 TEST_ASSERT_EQUAL(SetupStep::Ok, radio.configure());
216 chip.n_cmds = 0;
217 const uint8_t frame[14] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
218 TEST_ASSERT_EQUAL(RadioStatus::Ok, radio.send(frame, sizeof frame));
219 TEST_ASSERT_EQUAL_HEX8_ARRAY(frame, chip.buffer, sizeof frame);
220 TEST_ASSERT_EQUAL_HEX8(0x05, chip.tx_modulation); // errata 15.1 bit 2
221 int pp = chip.find(0x8C);
222 TEST_ASSERT_TRUE(pp >= 0);
223 TEST_ASSERT_EQUAL_HEX8(14, chip.cmds[pp][4]); // payload length for TX
224 int tx = chip.find(0x83);
225 TEST_ASSERT_TRUE(tx > pp);
226 // Timeout = 2 x 164864 us in 15.625 us steps = 21103 (0x00526F).
227 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[tx][1]);
228 TEST_ASSERT_EQUAL_HEX8(0x52, chip.cmds[tx][2]);
229 TEST_ASSERT_EQUAL_HEX8(0x6F, chip.cmds[tx][3]);
230 TEST_ASSERT_EQUAL_UINT(3, control.delays); // polled until TxDone
231 TEST_ASSERT_FALSE(control.rx_switch_during_tx);
232 // Back to listening for any length.
233 int rx = chip.find(0x82, tx);
234 TEST_ASSERT_TRUE(rx > tx);
235 TEST_ASSERT_EQUAL_HEX8(0xFF, chip.cmds[chip.find(0x8C, tx)][4]);
236 TEST_ASSERT_TRUE(control.rx_switch);
237 TEST_ASSERT_EQUAL_UINT32(1, radio.tx_ok());
238}
239
241 Sx1262Setup radio(chip, control, kDefaultLora);
242 radio.configure();
243 chip.irq = kIrqRxDone; // an Arm frame landed, not read yet
244 const uint8_t frame[3] = {1, 2, 3};
245 TEST_ASSERT_EQUAL(RadioStatus::Ok, radio.send(frame, sizeof frame));
246 TEST_ASSERT_TRUE(chip.irq & kIrqRxDone);
247}
248
249static void test_send_times_out(void) {
250 Sx1262Setup radio(chip, control, kDefaultLora);
251 radio.configure();
252 chip.polls_until_done = -1; // TxDone never comes
253 const uint8_t frame[14] = {0};
254 TEST_ASSERT_EQUAL(RadioStatus::ErrTimeout, radio.send(frame, sizeof frame));
255 // Host limit: 2 x ToA + 50 ms = 379 ms of 1 ms polls, then give up.
256 TEST_ASSERT_EQUAL_UINT(379, control.delays);
257 TEST_ASSERT_EQUAL_UINT32(1, radio.tx_timeouts());
258 TEST_ASSERT_TRUE(control.rx_switch); // still returned to RX
259
260 setUp();
261 Sx1262Setup b(chip, control, kDefaultLora);
262 b.configure();
263 chip.raise_timeout = true; // the chip's own timeout
264 TEST_ASSERT_EQUAL(RadioStatus::ErrTimeout, b.send(frame, sizeof frame));
265 TEST_ASSERT_EQUAL_UINT(0, control.delays);
266}
267
269 Sx1262Setup radio(chip, control, kDefaultLora);
270 radio.configure();
271 const uint8_t frame[1] = {1};
272 TEST_ASSERT_EQUAL(RadioStatus::ErrTooLong, radio.send(frame, 0));
273 TEST_ASSERT_EQUAL(RadioStatus::ErrTooLong, radio.send(nullptr, 1));
274 chip.status = 0xFF; // chip gone
275 TEST_ASSERT_EQUAL(RadioStatus::ErrNotFound, radio.send(frame, 1));
276}
277
278// --- Sniff mode (SetRxDutyCycle) tests ---
279
281 Sx1262Setup radio(chip, control, kDefaultLora);
282 radio.configure();
283 chip.n_cmds = 0;
284
285 TEST_ASSERT_TRUE(radio.start_rx_duty_cycle(33, 1000));
286
287 // SetRxDutyCycle (0x94): rxPeriod=33*64=2112=0x000840,
288 // sleepPeriod=1000*64=64000=0x00FA00
289 int dc = chip.find(0x94);
290 TEST_ASSERT_TRUE(dc >= 0);
291 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[dc][1]);
292 TEST_ASSERT_EQUAL_HEX8(0x08, chip.cmds[dc][2]);
293 TEST_ASSERT_EQUAL_HEX8(0x40, chip.cmds[dc][3]);
294 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[dc][4]);
295 TEST_ASSERT_EQUAL_HEX8(0xFA, chip.cmds[dc][5]);
296 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[dc][6]);
297 TEST_ASSERT_EQUAL_UINT(7, chip.lens[dc]);
298 // RF switch set for receive
299 TEST_ASSERT_TRUE(control.rx_switch);
300 // SetPacketParams (0x8C) with max length before duty cycle
301 int pp = chip.find(0x8C);
302 TEST_ASSERT_TRUE(pp >= 0 && pp < dc);
303 TEST_ASSERT_EQUAL_HEX8(0xFF, chip.cmds[pp][4]);
304 // SetStandby (0x80) is first to put chip in STDBY_RC
305 TEST_ASSERT_EQUAL_HEX8(0x80, chip.cmds[0][0]);
306 // No continuous SetRx (0x82) after the duty cycle command
307 TEST_ASSERT_EQUAL(-1, chip.find(0x82, dc));
308}
309
311 // The DS defines 1 step = 15.625 µs, so 1 ms = 64 steps exactly (p.71).
312 Sx1262Setup radio(chip, control, kDefaultLora);
313 radio.configure();
314 chip.n_cmds = 0;
315
316 radio.start_rx_duty_cycle(1, 1);
317
318 int dc = chip.find(0x94);
319 TEST_ASSERT_TRUE(dc >= 0);
320 // 1 ms * 64 = 64 = 0x000040
321 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[dc][1]);
322 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[dc][2]);
323 TEST_ASSERT_EQUAL_HEX8(0x40, chip.cmds[dc][3]);
324 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[dc][4]);
325 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[dc][5]);
326 TEST_ASSERT_EQUAL_HEX8(0x40, chip.cmds[dc][6]);
327}
328
330 Sx1262Setup radio(chip, control, kDefaultLora);
331 radio.configure();
332 radio.start_rx_duty_cycle(33, 1000);
333 chip.n_cmds = 0;
334
335 // After RxDone the chip is in STDBY_RC; re-arm must restart the duty cycle.
336 TEST_ASSERT_TRUE(radio.rearm_rx());
337
338 int dc = chip.find(0x94);
339 TEST_ASSERT_TRUE(dc >= 0);
340 // Same params as before
341 TEST_ASSERT_EQUAL_HEX8(0x00, chip.cmds[dc][1]);
342 TEST_ASSERT_EQUAL_HEX8(0x08, chip.cmds[dc][2]);
343 TEST_ASSERT_EQUAL_HEX8(0x40, chip.cmds[dc][3]);
344}
345
347 Sx1262Setup radio(chip, control, kDefaultLora);
348 radio.configure(); // leaves chip in continuous RX
349 chip.n_cmds = 0;
350
351 TEST_ASSERT_TRUE(radio.rearm_rx());
352
353 // In continuous mode the chip stays in RX on its own; nothing to send.
354 TEST_ASSERT_EQUAL_UINT(0, chip.n_cmds);
355}
356
358 Sx1262Setup radio(chip, control, kDefaultLora);
359 radio.configure();
360 radio.start_rx_duty_cycle(33, 1000);
361 chip.n_cmds = 0;
362
363 const uint8_t frame[14] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
364 TEST_ASSERT_EQUAL(RadioStatus::Ok, radio.send(frame, sizeof frame));
365
366 // After TxDone send() must return to duty cycle, NOT continuous RX.
367 int tx = chip.find(0x83);
368 TEST_ASSERT_TRUE(tx >= 0);
369 TEST_ASSERT_EQUAL(-1, chip.find(0x82, tx)); // no SetRx
370 int dc = chip.find(0x94, tx);
371 TEST_ASSERT_TRUE(dc > tx);
372}
373
375 Sx1262Setup radio(chip, control, kDefaultLora);
376 radio.configure();
377 radio.start_rx_duty_cycle(33, 1000);
378 chip.n_cmds = 0;
379
380 TEST_ASSERT_TRUE(radio.start_rx_continuous());
381
382 // Must end with SetRx 0xFFFFFF, not SetRxDutyCycle.
383 int rx = chip.find(0x82);
384 TEST_ASSERT_TRUE(rx >= 0);
385 const uint8_t expected[4] = {0x82, 0xFF, 0xFF, 0xFF};
386 TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, chip.cmds[rx], sizeof expected);
387 // No duty cycle command after the continuous SetRx.
388 TEST_ASSERT_EQUAL(-1, chip.find(0x94, rx + 1));
389}
390
void set_rx_switch(bool receive) override
void transfer(const uint8_t *tx, uint8_t *rx, uint16_t len) override
Definition test_main.cpp:58
int find(uint8_t opcode, int from=0) const
Definition test_main.cpp:47
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
Definition json.hpp:13878
*brief create JSON pointer *sa reference_tokens const string_t & a
Definition json.hpp:13877
int main()
static std::vector< uint8_t > frame(uint8_t hi, uint8_t lo, size_t payload_len, uint8_t fill)
One wire packet: len(4, LE) then len bytes of body, where the first 4 body bytes are ty/packet_id(2)/...
static FakeChip chip
Definition test_main.cpp:12
static void test_duty_cycle_1ms_is_64_steps(void)
static void test_send_keeps_a_packet_that_just_arrived(void)
static void test_send_waits_for_tx_done_and_returns_to_rx(void)
static void test_send_returns_to_duty_cycle_not_continuous(void)
static void test_rearm_restarts_duty_cycle_after_rxdone(void)
static void test_send_times_out(void)
static void test_configure_names_the_failing_step(void)
static void test_start_continuous_after_duty_cycle(void)
void setUp(void)
void tearDown(void)
static void test_configure_follows_the_datasheet_order(void)
static void test_duty_cycle_sends_correct_opcode_and_periods(void)
static void test_frequency_word_and_time_on_air(void)
static void test_rearm_is_noop_in_continuous_mode(void)
static void test_send_refuses_bad_input_and_dead_chip(void)