SatNOGS-COMMS  4.1.0
A COMMS subsystem for CubeSats
Loading...
Searching...
No Matches
radio.cpp
Go to the documentation of this file.
1/*
2 * SatNOGS-COMMS control library
3 *
4 * Copyright (C) 2023, Libre Space Foundation <http://libre.space>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * SPDX-License-Identifier: GNU General Public License v3.0 or later
20 */
21
22#include "settings.hpp"
23#include <etl/binary.h>
24#include <etl/vector.h>
29
30namespace satnogs::comms::lib
31{
32
33static radio::rx_msg rx_msg;
34static uint8_t tx_bb09_pdu[AT86RF215_MAX_PDU];
35static uint8_t tx_bb24_pdu[AT86RF215_MAX_PDU];
36
42static constexpr std::array<float, 8> fsk_mod_idxs = {
43 0.375f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f};
44
50static constexpr std::array<float, 4> fsk_bt = {0.5f, 1.0f, 1.5f, 2.0f};
51
52/* Re-implementation of the AT86RF215 weak functions */
53extern "C" {
54int
55at86rf215_set_rstn(struct at86rf215 *h, uint8_t enable)
56{
57 if (h->rst_gpio_dev) {
58 bsp::gpio *rst_gpio_dev = static_cast<bsp::gpio *>(h->rst_gpio_dev);
59 rst_gpio_dev->set(enable);
60 }
61 return AT86RF215_OK;
62}
63
64int
65at86rf215_set_seln(struct at86rf215 *h, uint8_t enable)
66{
67 if (h->cs_gpio_dev) {
68 bsp::gpio *cs_gpio_dev = static_cast<bsp::gpio *>(h->cs_gpio_dev);
69 /*
70 * NOTE: AT86RF215 uses physical pin level logic for the CS
71 */
72 cs_gpio_dev->set_raw(enable);
73 }
74 return AT86RF215_OK;
75}
76
77void
78at86rf215_delay_us(struct at86rf215 *h, uint32_t us)
79{
80 bsp::chrono *chrono_dev = static_cast<bsp::chrono *>(h->user_dev0);
81 chrono_dev->delay_us(us);
82}
83
84size_t
85at86rf215_get_time_ms(struct at86rf215 *h)
86{
87 bsp::chrono *chrono_dev = static_cast<bsp::chrono *>(h->user_dev0);
88 return chrono_dev->time_ms();
89}
90
91int
92at86rf215_spi_read(struct at86rf215 *h, uint8_t *out, const uint8_t *in,
93 size_t tx_len, size_t rx_len)
94{
95 bsp::spi *spi_dev = static_cast<bsp::spi *>(h->spi_dev);
96 spi_dev->read(out, in, tx_len, rx_len);
97
98 return AT86RF215_OK;
99}
100
101int
102at86rf215_spi_write(struct at86rf215 *h, const uint8_t *in, size_t len)
103{
104 bsp::spi *spi_dev = static_cast<bsp::spi *>(h->spi_dev);
105 spi_dev->write(in, len);
106 return AT86RF215_OK;
107}
108
109int
110at86rf215_irq_user_callback(struct at86rf215 *h, uint8_t rf09_irqs,
111 uint8_t rf24_irqs, uint8_t bbc0_irqs,
112 uint8_t bbc1_irqs)
113{
114 radio *r = reinterpret_cast<radio *>(h->user_dev1);
115 /* Check if a frame has been received */
116 if (bbc0_irqs & bit<uint8_t, 1>()) {
117 at86rf215_rx_frame(h, AT86RF215_RF09, rx_msg.pdu,
121 /*
122 * Get the RSSI of the received frame. According to the datasheet this
123 * should be the EDV register
124 */
125 int ret = at86rf215_get_edv(h, AT86RF215_RF09, &rx_msg.info.rssi);
126 if (ret) {
127 rx_msg.info.rssi = std::nanf("nan");
128 }
129
130 ret = r->m_rxq.put_isr(rx_msg);
131 if (ret) {
132 /* FIFO is full probably */
133 r->m_rx_drop.call_if(radio::interface::UHF);
134 }
135
136 /* After the RXFE the AT86 does not enter RX mode */
137 at86rf215_rx(h, AT86RF215_RF09, 1000);
138 }
139
140 /* Check if a frame has been received */
141 if (bbc1_irqs & bit<uint8_t, 1>()) {
142 at86rf215_rx_frame(h, AT86RF215_RF24, rx_msg.pdu,
146 /*
147 * Get the RSSI of the received frame. According to the datasheet this
148 * should be the EDV register
149 */
150 int ret = at86rf215_get_edv(h, AT86RF215_RF24, &rx_msg.info.rssi);
151 if (ret) {
152 rx_msg.info.rssi = std::nanf("nan");
153 }
154
155 ret = r->m_rxq.put_isr(rx_msg);
156 if (ret) {
157 /* FIFO is full probably */
158 r->m_rx_drop.call_if(radio::interface::SBAND);
159 }
160
161 /* After the RXFE the AT86 does not enter RX mode */
162 at86rf215_rx(h, AT86RF215_RF24, 1000);
163 }
164 return AT86RF215_OK;
165}
166}
167
174static at86rf215_radio_t
175iface_to_at86_radio(radio::interface iface)
176{
177 switch (iface) {
179 return AT86RF215_RF09;
181 return AT86RF215_RF24;
182 default:
183 throw inval_arg_exception(__FILE__, __LINE__);
184 }
185}
186
197radio::radio(const params &p, const io_conf &cnf, power &pwr,
198 bsp::imsgq<rx_msg> &rx_msgq)
199 : m_frame_len{p.uhf_len, p.sband_len},
200 m_spi(cnf.spi_ctrl),
201 m_nrst(cnf.nreset),
202 m_en_ext_clk(cnf.en_ext_clk),
203 m_tx_mixer_params(p.tx_mixer_params),
204 m_rx_mixer_params(p.rx_mixer_params),
205 m_iface_enabled{false, false},
206 m_rffe09(p.rffe09_params, cnf.rffe09_io, pwr),
207 m_rffe24(p.rffe24_params, cnf.rffe24_io, pwr),
208 m_chrono(cnf.chrono),
209 m_pwr(pwr),
210 m_rxq(rx_msgq),
211 m_req_clk_src(p.clk),
212 m_clk_src(clk_src::INTERNAL)
213{
214 bsp_setup();
215 enable(false);
216}
217
227int
229{
230 auto &r = board::get_instance().radio();
231 return at86rf215_irq_callback(&r.m_at86);
232}
233
246void
248{
249 if (yes) {
250 m_pwr.enable(power::subsys::RF_5V, true);
251 at86rf215_enable(true);
252 } else {
253 /*
254 * Disabling the whole chain is critical. Set explicitly the RF front-ends
255 * as disabled and disable the corresponding power supplies
256 */
257 enable_uhf(false);
258 enable_sband(false);
259 m_pwr.enable(power::subsys::RF_5V, false);
260 }
261}
262
263void
264radio::enable_uhf(bool yes)
265{
266 /*
267 * If the main radio is disabled and a disable request for the interface is
268 * requested we cannot do much..
269 */
270 if (enabled() == false && yes == false) {
271 m_rffe09.enable(false);
272 m_iface_enabled[static_cast<uint8_t>(interface::UHF)] = false;
273 return;
274 }
275
276 try {
277 int ret = 0;
278 if (yes) {
279 ret = at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, AT86RF215_RF09);
280 } else {
281 ret = at86rf215_set_trxoff(&m_at86, AT86RF215_RF09, 20);
282 }
283 ret += at86rf215_radio_irq_clear(&m_at86, AT86RF215_RF09);
284 if (ret) {
285 throw radio_exception(__FILE__, __LINE__);
286 }
287 m_rffe09.enable(yes);
288 m_iface_enabled[static_cast<uint8_t>(interface::UHF)] = yes;
289 } catch (...) {
290 /* In case of error the interface is considered disabled */
291 m_rffe09.enable(false);
292 m_iface_enabled[static_cast<uint8_t>(interface::UHF)] = false;
293 std::rethrow_exception(std::current_exception());
294 }
295}
296
297void
298radio::enable_sband(bool yes)
299{
300 /*
301 * If the main radio is disabled and a disable request for the interface is
302 * requested we cannot do much..
303 */
304 if (enabled() == false && yes == false) {
305 m_rffe24.enable(false);
306 m_iface_enabled[static_cast<uint8_t>(interface::SBAND)] = false;
307 return;
308 }
309
310 try {
311 int ret = 0;
312 if (yes) {
313 ret = at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, AT86RF215_RF24);
314 } else {
315 ret = at86rf215_set_trxoff(&m_at86, AT86RF215_RF24, 20);
316 }
317 ret += at86rf215_radio_irq_clear(&m_at86, AT86RF215_RF24);
318 if (ret) {
319 throw radio_exception(__FILE__, __LINE__);
320 }
321 m_rffe24.enable(yes);
322 m_iface_enabled[static_cast<uint8_t>(interface::SBAND)] = yes;
323 } catch (...) {
324 /* In case of error the interface is considered disabled */
325 m_rffe24.enable(false);
326 m_iface_enabled[static_cast<uint8_t>(interface::SBAND)] = false;
327 std::rethrow_exception(std::current_exception());
328 }
329}
330
341void
342radio::enable(interface iface, bool yes)
343{
344 /* Enable the radio if it is not already enabled */
345 if (yes && enabled() == false) {
346 enable();
347 }
348
349 switch (iface) {
350 case interface::UHF:
351 enable_uhf(yes);
352 break;
353 case interface::SBAND:
354 enable_sband(yes);
355 break;
356 default:
357 throw inval_arg_exception(__FILE__, __LINE__);
358 }
359}
360
371bool
373{
374 return m_pwr.enabled(power::subsys::RF_5V) &&
375 at86rf215_conn_check(&m_at86) == AT86RF215_OK;
376}
377
392void
393radio::stop(interface iface, size_t timeout_ms)
394{
395 const at86rf215_radio_t r = iface_to_at86_radio(iface);
396
397 switch (iface) {
398 case interface::UHF:
399 return m_rffe09.enable(false);
400 break;
401 case interface::SBAND:
402 return m_rffe24.enable(false);
403 break;
404 default:
405 throw inval_arg_exception(__FILE__, __LINE__);
406 }
407
408 /* [Errata reference 4840] Transition to TRXOFF may fail */
409 at86rf215_set_trxoff(&m_at86, r, 20);
410}
411
421bool
423{
424 return enabled() && m_iface_enabled[static_cast<uint8_t>(iface)];
425}
426
436{
437 switch (iface) {
438 case interface::UHF:
439 return m_rffe09.direction();
440 break;
441 case interface::SBAND:
442 return m_rffe24.direction();
443 break;
444 default:
445 throw inval_arg_exception(__FILE__, __LINE__);
446 }
447}
448
456void
458{
459 int ret = 0;
460 switch (iface) {
461 case interface::UHF: {
462 if (m_rffe09.frequency_valid(d, freq) == false) {
463 throw unsupported_freq_exception(__FILE__, __LINE__);
464 }
465
466 m_rffe09.set_direction(d);
467 struct at86rf215_radio_conf cnf = {.cm = AT86RF215_CM_FINE_RES_04,
468 .lbw = AT86RF215_PLL_LBW_DEFAULT};
469 ret = at86rf215_radio_conf(&m_at86, AT86RF215_RF09, &cnf);
470 if (ret) {
471 throw radio_exception(__FILE__, __LINE__);
472 }
473
474 ret = at86rf215_set_freq(&m_at86, AT86RF215_RF09, freq);
475 if (ret) {
476 throw radio_exception(__FILE__, __LINE__);
477 }
478 } break;
479 case interface::SBAND: {
480 if (m_rffe24.frequency_valid(d, freq) == false) {
481 throw unsupported_freq_exception(__FILE__, __LINE__);
482 }
483
484 uint64_t lo = lo_freq(freq, d);
485 m_rffe24.set_direction(d, lo);
486 struct at86rf215_radio_conf cnf = {.cm = AT86RF215_CM_FINE_RES_24,
487 .lbw = AT86RF215_PLL_LBW_DEFAULT};
488
489 ret = at86rf215_radio_conf(&m_at86, AT86RF215_RF24, &cnf);
490 if (ret) {
491 throw radio_exception(__FILE__, __LINE__);
492 }
493
494 ret = at86rf215_set_freq(&m_at86, AT86RF215_RF24, if_freq(freq, d));
495 if (ret) {
496 throw radio_exception(__FILE__, __LINE__);
497 }
498 } break;
499 }
500}
501
515uint32_t
517{
518 // TODO
519 return 0.0;
520}
521
531void
533{
534 const at86rf215_radio_t r = iface_to_at86_radio(iface);
535 gain = etl::clamp(gain, 0.0f, 31.0f);
536 at86rf215_set_pac(&m_at86, r, AT86RF215_PACUR_NO_RED, std::round(gain));
537}
538
552void
553radio::set_rx_gain(interface iface, const rf_frontend::rx_gain_params &gain)
554{
555 const at86rf215_radio_t r = iface_to_at86_radio(iface);
556
557 at86rf215_agc_conf agc_cnf = {.enable = 0,
558 .freeze = 0,
559 .reset = 0,
560 .avgs = AT86RF215_AVGS_8,
561 .input = 0,
562 .gcw = 23,
563 .tgt = AT86RF215_TGT_M21};
564
565 /* Manage gain settings for the AT86RF215 */
567 agc_cnf = gain.gain1.agc;
568 agc_cnf.enable = 1;
569 agc_cnf.freeze = 0;
570 } else {
571 agc_cnf.enable = 0;
572 agc_cnf.gcw =
573 etl::clamp<uint8_t>(std::lroundf(gain.gain1.gain / 3.0f), 0U, 23U);
574 }
575 int ret = at86rf215_set_agc(&m_at86, r, &agc_cnf);
576 if (ret) {
577 throw radio_exception(__FILE__, __LINE__);
578 }
579
580 /* Now set the gain for the RF frontend */
581 switch (iface) {
582 case interface::UHF:
583 m_rffe09.set_rx_gain(gain);
584 break;
585 case interface::SBAND:
586 m_rffe24.set_rx_gain(gain);
587 break;
588 default:
589 throw radio_exception(__FILE__, __LINE__);
590 }
591}
592
602static bool
603freq_in_range(const radio::mixer_param &p, uint32_t x)
604{
605 return x >= p.freq.first && x <= p.freq.second;
606}
607
618uint64_t
619radio::lo_freq(uint32_t rf_freq, rf_frontend::dir d) const
620{
621 switch (d) {
623
624 for (const auto &i : m_rx_mixer_params) {
625 if (freq_in_range(i, rf_freq)) {
626 return i.lo;
627 }
628 }
629 if (rf_freq < m_rx_mixer_params[0].freq.first) {
630 return m_rx_mixer_params[0].lo;
631 }
632 return m_rx_mixer_params[1].lo;
633 }
635 for (const auto &i : m_tx_mixer_params) {
636 if (freq_in_range(i, rf_freq)) {
637 return i.lo;
638 }
639 }
640 if (rf_freq < m_tx_mixer_params[0].freq.first) {
641 return m_tx_mixer_params[0].lo;
642 }
643 return m_tx_mixer_params[1].lo;
644 }
645 default:
646 throw radio_exception(__FILE__, __LINE__);
647 }
648}
649
660uint32_t
661radio::if_freq(uint32_t rf_freq, rf_frontend::dir d) const
662{
663 uint64_t res = rf_freq + lo_freq(rf_freq, d);
664 if (res > 0xFFFFFFFF) {
665 throw radio_exception(__FILE__, __LINE__);
666 }
667 return res;
668}
669
680static std::pair<at86rf215_fsk_midx_t, at86rf215_fsk_midxs_t>
681tune_fsk_mod_idx(float mod_idx)
682{
683 float diff = std::fabs(mod_idx - fsk_mod_idxs[0] * 7.0f / 8.0f);
684 uint8_t mod = 0;
685 uint8_t s = 0;
686 for (uint8_t i = 0; i < fsk_mod_idxs.size(); i++) {
687 float scaling = 7.0f / 8.0f;
688 for (uint8_t j = 0; j < 4; j++) {
689 const auto x = std::fabs(mod_idx - fsk_mod_idxs[i] * scaling);
690 if (x < diff) {
691 mod = i;
692 s = j;
693 diff = x;
694 }
695 scaling += 1.0f / 8.0f;
696 }
697 }
698 return {static_cast<at86rf215_fsk_midx_t>(mod),
699 static_cast<at86rf215_fsk_midxs_t>(s)};
700}
701
709static at86rf215_fsk_bt_t
710tune_fsk_bt(float bt)
711{
712 float diff = std::fabs(bt - fsk_bt[0]);
713 uint8_t idx = 0;
714 for (uint8_t i = 1; i < fsk_bt.size(); i++) {
715 const float x = std::fabs(bt - fsk_bt[i]);
716 if (x < diff) {
717 diff = x;
718 idx = i;
719 }
720 }
721 return static_cast<at86rf215_fsk_bt_t>(idx);
722}
723
730void
732{
733 const at86rf215_radio_t r = iface_to_at86_radio(iface);
734
735 /* For best performance the transceiver should be at TRXOFF state */
736 at86rf215_set_trxoff(&m_at86, r, 20);
738 set_tx_gain(iface, conf.gain);
739 at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, r);
740
741 /* At this point the PLL should be locked! */
742 m_chrono.delay_us(pll_ch_sw_delay_us);
743 bool locked = get_pll_ls(iface);
744 if (!locked) {
745 throw pll_ls_exception(__FILE__, __LINE__);
746 }
747
748 int ret = at86rf215_set_radio_irq_mask(&m_at86, r, TX_RF_IRQM);
749 ret += at86rf215_set_bbc_irq_mask(&m_at86, r, TX_BB_IRQM);
750 if (ret) {
751 throw radio_exception(__FILE__, __LINE__);
752 }
753
754 /* Fetch the best settings that match the user defined modulation index */
755 auto mod_idx = tune_fsk_mod_idx(conf.fsk.mod_idx);
756
757 /* FSK-2 50kHz */
758 struct at86rf215_bb_conf at86_bb;
759 at86_bb.ctx = 0;
760 at86_bb.fcsfe = 0;
761 at86_bb.txafcs = 1;
762 at86_bb.fcst = AT86RF215_FCS_16;
763 at86_bb.pt = AT86RF215_BB_MRFSK;
764 at86_bb.fsk.mord = AT86RF215_2FSK;
765 at86_bb.fsk.midx = mod_idx.first;
766 at86_bb.fsk.midxs = mod_idx.second;
767 at86_bb.fsk.bt = tune_fsk_bt(conf.fsk.excess_bw);
768 at86_bb.fsk.srate = conf.fsk.rate;
769 at86_bb.fsk.fi = 0;
770 at86_bb.fsk.rxo = AT86RF215_FSK_RXO_DISABLED;
771 at86_bb.fsk.pdtm = 0;
772 at86_bb.fsk.rxpto = 1;
773 at86_bb.fsk.mse = 0;
774 at86_bb.fsk.pri = 0;
775 at86_bb.fsk.fecs = AT86RF215_FSK_FEC_RSC;
776 at86_bb.fsk.fecie = 0;
777 at86_bb.fsk.sfd_threshold = 15;
778 at86_bb.fsk.preamble_threshold = 8;
779 at86_bb.fsk.sfdq = 1;
780 at86_bb.fsk.sfd32 = 1;
781 at86_bb.fsk.rawrbit = 1;
782 at86_bb.fsk.csfd1 = AT86RF215_SFD_UNCODED_RAW;
783 at86_bb.fsk.csfd0 = AT86RF215_SFD_UNCODED_RAW;
784 at86_bb.fsk.preamble_length = conf.preamble_len;
785 /* SFD operates in a LSB order*/
786 at86_bb.fsk.sfd0 = static_cast<uint16_t>(etl::reverse_bits(conf.sync));
787 at86_bb.fsk.sfd1 = static_cast<uint16_t>(etl::reverse_bits(conf.sync) >> 16);
788 at86_bb.fsk.sfd = 0;
789 at86_bb.fsk.dw = 0;
790 at86_bb.fsk.preemphasis = 0;
791 at86_bb.fsk.dm = 1;
792
793 ret = at86rf215_bb_conf(&m_at86, r, &at86_bb);
794 ret += at86rf215_set_mode(&m_at86, AT86RF215_RF_MODE_BBRF);
795 ret += at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, r);
796 ret += at86rf215_radio_irq_clear(&m_at86, r);
797 ret += at86rf215_bb_enable(&m_at86, r, 1);
798 if (ret) {
799 throw radio_exception(__FILE__, __LINE__);
800 }
801}
802
816void
817radio::tx(interface iface, const uint8_t *b)
818{
819
820 const at86rf215_radio_t r = iface_to_at86_radio(iface);
821 uint8_t *tx_pdu = iface == interface::UHF ? tx_bb09_pdu : tx_bb24_pdu;
822
823 memcpy(tx_pdu, b, tx_len(iface));
824
825 int ret = at86rf215_tx_frame(&m_at86, r, tx_pdu, tx_len(iface), 2000);
826 if (ret) {
827 if (ret == -AT86RF215_TIMEOUT) {
828 throw timeout_exception(__FILE__, __LINE__);
829 }
830 throw radio_exception(__FILE__, __LINE__, ret);
831 }
832}
833
844void
846{
847 const at86rf215_radio_t r = iface_to_at86_radio(iface);
848 enable(iface);
849 /* For best performance the transceiver should be at TRXOFF state */
850 at86rf215_set_trxoff(&m_at86, r, 20);
852 at86rf215_set_cmd(&m_at86, AT86RF215_CMD_RF_TXPREP, r);
853
854 /* At this point the PLL should be locked! */
855 m_chrono.delay_us(pll_ch_sw_delay_us);
856 bool locked = get_pll_ls(iface);
857 if (!locked) {
858 throw pll_ls_exception(__FILE__, __LINE__);
859 }
860
861 int ret = at86rf215_set_radio_irq_mask(&m_at86, r, RX_RF_IRQM);
862 ret += at86rf215_set_bbc_irq_mask(&m_at86, r, RX_BB_IRQM);
863 if (ret) {
864 throw radio_exception(__FILE__, __LINE__);
865 }
866
867 set_rx_gain(iface, conf.gain);
868
869 /* Apply the hardware filter path */
870 if (iface == interface::UHF) {
871 uhf().set_filter(conf.filter);
872 } else {
873 sband().set_filter(conf.filter);
874 }
875
876 /* Fetch the best settings that match the user defined modulation index */
877 auto mod_idx = tune_fsk_mod_idx(conf.fsk.mod_idx);
878
879 /* FSK-2 50kHz */
880 struct at86rf215_bb_conf at86_bb;
881 at86_bb.ctx = 0;
882 at86_bb.fcsfe = 0;
883 at86_bb.txafcs = 1;
884 at86_bb.fcst = AT86RF215_FCS_16;
885 at86_bb.pt = AT86RF215_BB_MRFSK;
886 at86_bb.fsk.mord = AT86RF215_2FSK;
887 at86_bb.fsk.midx = mod_idx.first;
888 at86_bb.fsk.midxs = mod_idx.second;
889 at86_bb.fsk.bt = tune_fsk_bt(conf.fsk.excess_bw);
890 at86_bb.fsk.srate = conf.fsk.rate;
891 at86_bb.fsk.fi = 0;
892 at86_bb.fsk.rxo = AT86RF215_FSK_RXO_DISABLED;
893 at86_bb.fsk.pdtm = 0;
894 at86_bb.fsk.rxpto = 1;
895 at86_bb.fsk.mse = 0;
896 at86_bb.fsk.pri = 0;
897 at86_bb.fsk.fecs = AT86RF215_FSK_FEC_RSC;
898 at86_bb.fsk.fecie = 0;
899 at86_bb.fsk.sfd_threshold = 12;
900 at86_bb.fsk.preamble_threshold = 4;
901 at86_bb.fsk.sfdq = 0;
902 at86_bb.fsk.sfd32 = 1;
903 at86_bb.fsk.rawrbit = 1; // Most of the encoders transmit MSB first
904 at86_bb.fsk.csfd1 = AT86RF215_SFD_UNCODED_RAW;
905 at86_bb.fsk.csfd0 = AT86RF215_SFD_UNCODED_RAW;
906 at86_bb.fsk.preamble_length = conf.preamble_len;
907 /* SFD operates in a LSB order*/
908 at86_bb.fsk.sfd0 = static_cast<uint16_t>(etl::reverse_bits(conf.sync));
909 at86_bb.fsk.sfd1 = static_cast<uint16_t>(etl::reverse_bits(conf.sync) >> 16);
910 at86_bb.fsk.sfd = 0;
911 at86_bb.fsk.dw = 0;
912 at86_bb.fsk.preemphasis = 0;
913 at86_bb.fsk.dm = 1;
914
915 /* Apply the fixed size frame length */
916 at86_bb.fsk.fskrrxf = rx_len(iface);
917
918 ret = at86rf215_bb_conf(&m_at86, r, &at86_bb);
919 ret += at86rf215_set_mode(&m_at86, AT86RF215_RF_MODE_BBRF);
920 ret += at86rf215_radio_irq_clear(&m_at86, r);
921 ret += at86rf215_bb_enable(&m_at86, r, 1);
922 ret += at86rf215_rx(&m_at86, r, 1000);
923 if (ret) {
924 throw radio_exception(__FILE__, __LINE__);
925 }
926}
927
934bool
936{
937 return m_rffe24.mixer_lock();
938}
939
946{
947 return m_rffe09;
948}
949
956{
957 return m_rffe24;
958}
959
967int
968radio::recv_msg(rx_msg &msg, size_t timeout_ms)
969{
970 return m_rxq.get(&msg, timeout_ms);
971}
972
979uint32_t
981{
982 return m_frame_len[iface_idx(iface)].tx;
983}
984
991uint32_t
993{
994 return m_frame_len[iface_idx(iface)].rx;
995}
996
1003void
1004radio::bsp_setup()
1005{
1006 m_at86.spi_dev = &m_spi;
1007 m_at86.cs_gpio_dev = &m_spi.cs();
1008 m_at86.rst_gpio_dev = &m_nrst;
1009 /* We use the user0 field for the chrono related stuff */
1010 m_at86.user_dev0 = &m_chrono;
1011 /*
1012 * user1 has the class itself for handling the user defined IRQ handler of
1013 * the AT86RF215 driver
1014 */
1015 m_at86.user_dev1 = this;
1016}
1017
1023void
1024radio::at86rf215_enable(bool enable)
1025{
1026 /* In case the user needs to disable the IC, just de-assert the RSTN pin */
1027 if (enable == false) {
1028 at86rf215_set_rstn(&m_at86, 0);
1029 return;
1030 }
1031
1032 m_at86.clk_drv = AT86RF215_RF_DRVCLKO4;
1033 m_at86.clko_os = AT86RF215_RF_CLKO_26_MHZ;
1034 m_at86.xo_trim = 0;
1035 m_at86.xo_fs = 0;
1036 m_at86.irqp = 0;
1037 m_at86.irqmm = 0;
1038 m_at86.pad_drv = AT86RF215_RF_DRV8;
1039 m_at86.rf_femode_09 = AT86RF215_RF_FEMODE2;
1040 m_at86.rf_femode_24 = AT86RF215_RF_FEMODE2;
1041
1042 /*
1043 * If the clock reference is not present, the at86rf215_init() fails.
1044 * So this is a good time to set the clock source. In case of failure
1045 * the radio fallback to the internal one
1046 */
1047 int ret = 0;
1048 if (m_req_clk_src == clk_src::EXTERNAL) {
1049 m_en_ext_clk.set(m_req_clk_src == clk_src::EXTERNAL);
1050 m_chrono.delay_us(clk_rfsw_delay_us);
1051 ret = at86rf215_init(&m_at86);
1052 /* In case of failure, fallback to the internal clock reference*/
1053 if (ret) {
1054 m_en_ext_clk.set(false);
1055 m_chrono.delay_us(clk_rfsw_delay_us);
1056 ret = at86rf215_init(&m_at86);
1057 m_clk_src = clk_src::INTERNAL;
1058 } else {
1059 m_clk_src = clk_src::EXTERNAL;
1060 }
1061 } else {
1062 /* Explicitly set it to internal in case any abnormal previous state */
1063 m_en_ext_clk.set(false);
1064 m_chrono.delay_us(clk_rfsw_delay_us);
1065 m_clk_src = clk_src::INTERNAL;
1066 ret = at86rf215_init(&m_at86);
1067 }
1068
1069 /*
1070 * init() makes several checks so it will be good to raise an exception
1071 * specifically for it
1072 */
1073 if (ret) {
1074 throw radio_exception(__FILE__, __LINE__);
1075 }
1076
1077 at86rf215_set_trxoff(&m_at86, AT86RF215_RF09, 20);
1078 at86rf215_set_trxoff(&m_at86, AT86RF215_RF24, 20);
1079 at86rf215_irq_clear(&m_at86);
1080}
1081
1093void
1095{
1096 m_req_clk_src = src;
1097}
1098
1105{
1106 return m_clk_src;
1107}
1108
1117bool
1119{
1120 at86rf215_pll_ls_t status = AT86RF215_PLL_UNLOCKED;
1121 switch (iface) {
1122 case interface::UHF: {
1123 at86rf215_get_pll_ls(&m_at86, &status, iface_to_at86_radio(interface::UHF));
1124 return status == AT86RF215_PLL_LOCKED;
1125 }
1126 case interface::SBAND: {
1127 at86rf215_get_pll_ls(&m_at86, &status,
1128 iface_to_at86_radio(interface::SBAND));
1129 return status == AT86RF215_PLL_LOCKED;
1130 }
1131 default:
1132 return AT86RF215_PLL_UNLOCKED;
1133 }
1134}
1135
1146void
1147radio::register_rx_drop_callback(etl::delegate<void(interface)> callback)
1148{
1149 m_rx_drop = callback;
1150}
1151
1152uint32_t
1153radio::iface_idx(interface iface) const
1154{
1155 switch (iface) {
1156 case interface::UHF:
1157 case interface::SBAND:
1158 return static_cast<uint32_t>(iface);
1159 default:
1160 throw inval_arg_exception(__FILE__, __LINE__);
1161 }
1162}
1163
1164} // namespace satnogs::comms::lib
comms::lib::radio & radio()
Returns a reference to the radio subsystem.
Definition board.cpp:80
static board & get_instance()
Gets a reference to the single instance of the Board interface class.
Definition board.cpp:66
Chrono device abstraction.
Definition chrono.hpp:41
virtual int64_t time_ms()=0
Retrieves the current time in milliseconds.
virtual void delay_us(size_t us)=0
Delays the execution of the active task for at least us microseconds.
GPIO device abstraction.
Definition gpio.hpp:38
virtual void set_raw(bool s)=0
Sets the physical output of the pin.
virtual void set(bool s)=0
Sets the logical output of the pin. For example, if the pin has been configured as active low,...
Message queue device abstraction.
Definition msgq.hpp:42
SPI device abstraction.
Definition spi.hpp:41
virtual gpio & cs()
Returns a GPIO control handle for the CS of the SPI. If this is performed automatically by the hardwa...
Definition spi.hpp:79
virtual void read(uint8_t *rx, const uint8_t *tx, size_t tx_len, size_t rx_len)=0
Performs an SPI read operation.
virtual void write(const uint8_t *tx, size_t len)=0
Performs an SPI write operation.
Generic exception indicating an invalid argument.
Exception indicating PLL lock status issue.
Definition radio.hpp:49
Manages power supplies and monitors subsystem status.
Definition power.hpp:43
at86rf215_fsk_srate_t rate
FSK data rate.
Definition radio.hpp:261
float excess_bw
FSK 3-dB bandwidth of the shaping filter.
Definition radio.hpp:265
IO configuration that is necessary for the radio to operate.
Definition radio.hpp:207
Configuration parameters for the RF mixer.
Definition radio.hpp:146
std::pair< uint32_t, uint32_t > freq
Definition radio.hpp:148
Initialization parameters of the radio class.
Definition radio.hpp:240
RX configuration parameters.
Definition radio.hpp:299
fsk_conf fsk
FSK parameters.
Definition radio.hpp:304
uint32_t sync
The synchronization word.
Definition radio.hpp:303
rf_frontend::filter filter
Hardware filter selection.
Definition radio.hpp:306
uint32_t freq
The desired RX frequency to set.
Definition radio.hpp:301
rf_frontend::rx_gain_params gain
Gain settings to set.
Definition radio.hpp:305
uint8_t preamble_len
The length of the preamble in bytes.
Definition radio.hpp:302
uint32_t len
The frame length.
Definition radio.hpp:278
The RX message accompanied by its metadata.
Definition radio.hpp:289
uint8_t pdu[AT86RF215_MAX_PDU]
Buffer to hold the received frame.
Definition radio.hpp:291
uint32_t sync
The synchronization word.
Definition radio.hpp:314
fsk_conf fsk
FSK parameters.
Definition radio.hpp:315
uint32_t freq
The desired RX frequency to set.
Definition radio.hpp:312
uint8_t preamble_len
The length of the preamble in bytes.
Definition radio.hpp:313
Exception indicating a generic exception of the radio subsystem.
Definition radio.hpp:64
void rx_async(interface iface, const rx_conf &conf)
Sets the radio in reception mode asynchronously.
Definition radio.cpp:845
rf_frontend::dir direction(interface iface) const
Fetches the current direction (RX/TX) of the specific interface.
Definition radio.cpp:435
static int trx_irq_handler()
The IRQ handler of the AT86RF215 IRQ.
Definition radio.cpp:228
void tx(interface iface, const uint8_t *b)
Performs a TX in blocking mode.
Definition radio.cpp:817
rf_frontend09 & uhf()
Definition radio.cpp:945
void register_rx_drop_callback(etl::delegate< void(interface)> callback)
Registers a callback for the RX drop message event.
Definition radio.cpp:1147
void set_frequency(interface iface, rf_frontend::dir d, uint32_t freq)
Sets the frequency and the direction.
Definition radio.cpp:457
interface
Radio interface identifier.
Definition radio.hpp:177
bool mixer_lock()
Gets the status of the RF mixer lock.
Definition radio.cpp:935
clk_src
PLL Reference Clock identifier.
Definition radio.hpp:167
@ INTERNAL
Internal Crystal Oscillator.
Definition radio.hpp:168
@ EXTERNAL
External Reference Clock (TCXO).
Definition radio.hpp:169
uint32_t frequency(interface iface, rf_frontend::dir d) const
Returns the actual frequency that the hardware reports.
Definition radio.cpp:516
uint32_t rx_len(interface iface) const
Retrieves the configured RX frame size for a specific interface.
Definition radio.cpp:992
radio(const params &p, const io_conf &cnf, power &pwr, bsp::imsgq< rx_msg > &rx_msgq)
Construct a new radio::radio object.
Definition radio.cpp:197
void set_tx_conf(interface iface, const tx_conf &conf)
Performs the configuration of a specific interface for TX.
Definition radio.cpp:731
void stop(interface iface, size_t timeout_ms=1000)
Sets the FSM of the AT86RF215 interface to TRXOFF.
Definition radio.cpp:393
rf_frontend24 & sband()
Definition radio.cpp:955
bool get_pll_ls(interface iface)
Checks the PLL lock status of the AT86RF215 transceiver for the specified radio interface.
Definition radio.cpp:1118
void enable(bool yes=true)
Enable/disable the radio chain.
Definition radio.cpp:247
clk_src get_clk_src() const
Retrieves the current clock source of the AT86RF215 PLL.
Definition radio.cpp:1104
void set_tx_gain(interface iface, float gain)
Sets the TX gain of the AT86RF215.
Definition radio.cpp:532
int recv_msg(rx_msg &msg, size_t timeout_ms)
Gets an available message from the receive message queue.
Definition radio.cpp:968
uint32_t tx_len(interface iface) const
Retrieves the configured TX frame size for a specific interface.
Definition radio.cpp:980
bool enabled() const
Checks if the radio transceiver is in an operational state. This means that their powesr sources are ...
Definition radio.cpp:372
void set_clk_src(clk_src src)
Sets the clock source for the AT86RF215 PLL.
Definition radio.cpp:1094
RF-frontend for the UHF interface.
RF-frontend for the S-Band interface.
RX gain settings for the two different gain stages. Gain0 stage corresponds to the first stage (close...
union satnogs::comms::lib::rf_frontend::rx_gain_params::@215011014006014333052114215072331261330071377362 gain1
float gain
Fixed gain value in dB [-6, 29.5].
virtual void set_filter(filter f)
Generic timeout exception.
Exception indicating an invalid frequency.
Definition radio.hpp:85
int at86rf215_set_seln(struct at86rf215 *h, uint8_t enable)
Definition radio.cpp:65
int at86rf215_irq_user_callback(struct at86rf215 *h, uint8_t rf09_irqs, uint8_t rf24_irqs, uint8_t bbc0_irqs, uint8_t bbc1_irqs)
Definition radio.cpp:110
void at86rf215_delay_us(struct at86rf215 *h, uint32_t us)
Definition radio.cpp:78
int at86rf215_spi_read(struct at86rf215 *h, uint8_t *out, const uint8_t *in, size_t tx_len, size_t rx_len)
Definition radio.cpp:92
size_t at86rf215_get_time_ms(struct at86rf215 *h)
Definition radio.cpp:85
int at86rf215_spi_write(struct at86rf215 *h, const uint8_t *in, size_t len)
Definition radio.cpp:102
int at86rf215_set_rstn(struct at86rf215 *h, uint8_t enable)
Definition radio.cpp:55
scl::rf_frontend09::io_conf rffe09_io
Definition startup.cpp:321
scl::rf_frontend24::io_conf rffe24_io
Definition startup.cpp:328