SatNOGS-COMMS  4.1.0
A COMMS subsystem for CubeSats
Loading...
Searching...
No Matches
iface_ctrl.cpp
Go to the documentation of this file.
1#include "iface_ctrl.hpp"
2
3#include "io_wdg.hpp"
4#include <dsp/ccsds.hpp>
5#include <error_handler.hpp>
6#include <etl/algorithm.h>
7#include <etl/crc.h>
8#include <logger.hpp>
9#include <scoped_lock.hpp>
10#include <settings.hpp>
11#include <startup.hpp>
12
13namespace sc = satnogs::comms;
14namespace scl = satnogs::comms::lib;
15
17{
18
20 : fsm(unique_message_router_id::get()), m_iface(iface), m_pending(false)
21{
22 k_work_init(&m_work, &iface_ctrl::timer_work_handler);
23 k_timer_init(&m_timer, &iface_ctrl::timer_handler, nullptr);
24 k_timer_user_data_set(&m_timer, this);
25 k_mutex_init(&m_mtx);
26 k_condvar_init(&m_cond_var);
27
28 m_state_list[0] = &m_disabled_state;
29 m_state_list[1] = &m_tx_state;
30 m_state_list[2] = &m_rx_state;
31 m_state_list[3] = &m_idle_state;
32
33 set_states(m_state_list, 4);
34 start();
35}
36
41void
43{
44 receive(rx_msg());
45}
46
47void
49{
50 if (yes) {
51 enable();
52 } else {
53 disable();
54 }
55}
56
61void
63{
64 receive(disable_msg());
65}
66
73void
75{
76 disable();
77 enable();
78}
79
84void
86{
87 m_stats.reset();
88}
89
97{
98 return m_stats;
99}
100
101void
102iface_ctrl::tx(const msg_arbiter::msg &msg, bool more)
103{
104 /* Check if the TX inhibit is activated */
105 auto &s = settings::get_instance();
106 bool tx_en = m_iface == scl::radio::interface::UHF
109 if (!tx_en) {
110 throw tx_inhibit_exception(__FILE__, __LINE__);
111 }
112
113 wait_pending_operation();
114 m_tx_msg = msg;
115 m_tx_wait = more;
116 receive(tx_frame_msg());
117}
118
126{
127 return static_cast<iface_ctrl::state>(get_state_id());
128}
129
130void
131iface_ctrl::wait_pending_operation()
132{
133 scoped_lock lock(&m_mtx);
134 while (m_pending) {
135 k_condvar_wait(&m_cond_var, &m_mtx, K_FOREVER);
136 }
137}
138
145void
146iface_ctrl::frame_sent(bool success)
147{
148 if (success) {
149 if (m_stats.tx_frames < std::numeric_limits<size_t>::max()) {
150 m_stats.tx_frames++;
151 }
152 } else {
153 if (m_stats.tx_frames_fail < std::numeric_limits<size_t>::max()) {
154 m_stats.tx_frames_fail++;
155 }
156 }
157}
158
163void
164iface_ctrl::tx_frame_dropped()
165{
166 if (m_stats.tx_frames_drop < std::numeric_limits<size_t>::max()) {
167 m_stats.tx_frames_drop++;
168 }
169}
170
179bool
180iface_ctrl::duty_cycle_enabled()
181{
182 auto &s = settings::get_instance();
183 uint32_t on_secs = m_iface == scl::radio::interface::UHF
185 : s.get<settings::param::SBAND_RX_ON_SECS>();
186 uint32_t off_secs = m_iface == scl::radio::interface::UHF
189 return on_secs > 0 && off_secs > 0;
190}
191
198void
199iface_ctrl::frame_received(bool valid, float rssi)
200{
201 m_stats.last_rssi = rssi;
202 m_stats.last_rx_ts = k_uptime_get();
203 if (valid) {
204 /* In case of a valid frame reset the IO/GS watchdog */
206 if (m_stats.rx_frames < std::numeric_limits<size_t>::max()) {
207 m_stats.rx_frames++;
208 }
209 m_stats.last_valid_rssi = rssi;
210 m_stats.last_valid_rx_ts = k_uptime_get();
211 } else {
212 if (m_stats.rx_frames_inval < std::numeric_limits<size_t>::max()) {
213 m_stats.rx_frames_inval++;
214 }
215 }
216}
217
222void
224{
225 if (m_stats.rx_frames_drop < std::numeric_limits<size_t>::max()) {
226 m_stats.rx_frames_drop++;
227 }
228}
229
230void
231iface_ctrl::timer_handler(k_timer *t)
232{
233 iface_ctrl *ctx = static_cast<iface_ctrl *>(k_timer_user_data_get(t));
234 if (!ctx) {
235 return;
236 }
237
238 if (k_work_is_pending(&ctx->m_work)) {
239 return;
240 }
241 k_work_submit(&ctx->m_work);
242}
243
244void
245iface_ctrl::timer_work_handler(k_work *item)
246{
247 auto ctx = CONTAINER_OF(item, iface_ctrl, m_work);
248
249 scoped_lock lock(&ctx->m_mtx);
250 while (ctx->m_pending) {
251 k_condvar_wait(&ctx->m_cond_var, &ctx->m_mtx, K_FOREVER);
252 }
253
254 /* Waiting for the next TX timed out. Go again in RX */
255 if (ctx->get_state() == iface_ctrl::state::TX) {
256 ctx->receive(rx_msg());
257 }
258 /* Idle timer has expired, go again on the RX state */
259 else if (ctx->get_state() == iface_ctrl::state::IDLE) {
260 ctx->receive(rx_msg());
261 } else if (ctx->get_state() == iface_ctrl::state::RX) {
262 ctx->receive(idle_msg());
263 }
264}
265
266/*****************************************************************************
267 *****************************************************************************
268 ************************** Disabled state handling **************************
269 *****************************************************************************
270 *****************************************************************************/
271
272etl::fsm_state_id_t
273iface_ctrl::disabled_state::on_enter_state()
274{
275 auto &ctx = get_fsm_context();
276 auto &log = sc::logger::get_instance();
277 log.log({sc::logger::target::RTT},
278 ctx.m_iface == scl::radio::interface::UHF
279 ? "iface_ctrl<UHF>: disabled iface. enter disabled state"
280 : "iface_ctrl<S-Band>: disabled iface. enter disabled state");
282 k_timer_stop(&ctx.m_timer);
283 radio.enable(ctx.m_iface, false);
284 return No_State_Change;
285}
286
287etl::fsm_state_id_t
288iface_ctrl::disabled_state::on_event(const rx_msg &msg)
289{
290 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::RX);
291}
292
293etl::fsm_state_id_t
294iface_ctrl::disabled_state::on_event(const tx_frame_msg &msg)
295{
296 auto &log = sc::logger::get_instance();
297 auto &ctx = get_fsm_context();
298 log.log({sc::logger::target::RTT},
299 ctx.m_iface == scl::radio::interface::UHF
300 ? "iface_ctrl<UHF>: disabled iface. Dropping TX msg"
301 : "iface_ctrl<S-Band>: disabled iface. Dropping TX msg");
302 ctx.tx_frame_dropped();
303 return No_State_Change;
304}
305
306etl::fsm_state_id_t
307iface_ctrl::disabled_state::on_event_unknown(const etl::imessage &msg)
308{
309 auto &ctx = get_fsm_context();
310 auto &log = sc::logger::get_instance();
311 log.log({sc::logger::target::RTT},
312 ctx.m_iface == scl::radio::interface::UHF
313 ? "iface_ctrl<UHF>: disabled state unknown msg"
314 : "iface_ctrl<S-Band>: disabled state unknown msg");
315 return No_State_Change;
316}
317
318/*****************************************************************************
319 *****************************************************************************
320 ***************************** TX state handling *****************************
321 *****************************************************************************
322 *****************************************************************************/
323
324etl::fsm_state_id_t
325iface_ctrl::tx_state::on_enter_state()
326{
327 auto &ctx = get_fsm_context();
328 auto &log = sc::logger::get_instance();
329 try {
330 scoped_lock lock(&ctx.m_mtx);
331 ctx.m_pending = true;
332 log.log({sc::logger::target::RTT},
333 ctx.m_iface == scl::radio::interface::UHF
334 ? "iface_ctrl<UHF>: enter TX state"
335 : "iface_ctrl<S-Band>: enter TX state");
337 auto &s = settings::get_instance();
338
339 /*
340 * Force the interface to stop. This will close all the analog chain of the
341 * AT86RF215. Note that if the radio in general is not enabled, this does
342 * not make any sense.
343 */
344 if (radio.enabled()) {
345 radio.stop(ctx.m_iface);
346 }
347 /*
348 * If this is a transition from another state, apply the TX settings and
349 * perform the TX of the frame
350 */
351 radio.enable(ctx.m_iface, true);
352
353 /* Get the TX configuration */
354 lib::radio::tx_conf cnf;
355 s.get_tx_conf(ctx.m_iface, cnf);
356 radio.set_tx_conf(ctx.m_iface, cnf);
357
358 /* Do DSP and send it! */
359 auto crc = etl::crc32_c(ctx.m_tx_msg.data, ctx.m_tx_msg.data +
360 radio.tx_len(ctx.m_iface) -
361 sizeof(uint32_t))
362 .value();
363
364 ctx.m_tx_msg.data[radio.tx_len(ctx.m_iface) - 4] = crc >> 24;
365 ctx.m_tx_msg.data[radio.tx_len(ctx.m_iface) - 3] = crc >> 16;
366 ctx.m_tx_msg.data[radio.tx_len(ctx.m_iface) - 2] = crc >> 8;
367 ctx.m_tx_msg.data[radio.tx_len(ctx.m_iface) - 1] = crc;
368 dsp::ccsds::scrambler(ctx.m_tx_msg.data, radio.tx_len(ctx.m_iface));
369
370 radio.tx(ctx.m_iface, ctx.m_tx_msg.data);
371 ctx.frame_sent(true);
372
373 /*
374 * Fireup the timer that allows the interface to stay in TX mode for the
375 * configured amount of time to increase throughput
376 */
377 if (ctx.m_tx_wait) {
378 uint32_t wait_ms = ctx.m_iface == scl::radio::interface::UHF
380 : s.get<settings::param::SBAND_TX_WAIT_MS>();
381 k_timer_start(&ctx.m_timer, K_MSEC(wait_ms), K_NO_WAIT);
382
383 ctx.m_pending = false;
384 k_condvar_broadcast(&ctx.m_cond_var);
385 return No_State_Change;
386 } else {
387 ctx.m_pending = false;
388 k_condvar_broadcast(&ctx.m_cond_var);
389 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::RX);
390 }
391 } catch (const scl::exception &e) {
392 ctx.frame_sent(false);
393 log.log({sc::logger::target::RTT},
394 ctx.m_iface == scl::radio::interface::UHF
395 ? "iface_ctrl<UHF>: Exception occurred"
396 : "iface_ctrl<S-Band>: Exception occurred");
397 auto &err = error_handler::get_instance();
398 err.handle(e);
399 ctx.m_pending = false;
400 k_condvar_broadcast(&ctx.m_cond_var);
401 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::IDLE);
402 }
403}
404
405void
406iface_ctrl::tx_state::on_exit_state()
407{
408 auto &ctx = get_fsm_context();
409 k_timer_stop(&ctx.m_timer);
410 ctx.wait_pending_operation();
411}
412
413etl::fsm_state_id_t
414iface_ctrl::tx_state::on_event(const disable_msg &msg)
415{
416 auto &ctx = get_fsm_context();
417 /*
418 * NOTE: Disable first the timer to avoid accidental fireup while waiting for
419 * the operation to finish
420 */
421 k_timer_stop(&ctx.m_timer);
422 /* Let the final frame finish */
423 ctx.wait_pending_operation();
424 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::DISABLED);
425}
426
427etl::fsm_state_id_t
428iface_ctrl::tx_state::on_event(const tx_frame_msg &msg)
429{
430 auto &ctx = get_fsm_context();
431 auto &log = sc::logger::get_instance();
432 auto &s = settings::get_instance();
433 try {
434 /* Stop the timer we do not want to expire in the middle of a TX */
435 k_timer_stop(&ctx.m_timer);
436
437 scoped_lock lock(&ctx.m_mtx);
438 ctx.m_pending = true;
439
441 /* Do DSP and send it! */
442 auto crc = etl::crc32_c(ctx.m_tx_msg.data, ctx.m_tx_msg.data +
443 radio.tx_len(ctx.m_iface) -
444 sizeof(uint32_t))
445 .value();
446 ctx.m_tx_msg.data[radio.tx_len(ctx.m_iface) - 4] = crc >> 24;
447 ctx.m_tx_msg.data[radio.tx_len(ctx.m_iface) - 3] = crc >> 16;
448 ctx.m_tx_msg.data[radio.tx_len(ctx.m_iface) - 2] = crc >> 8;
449 ctx.m_tx_msg.data[radio.tx_len(ctx.m_iface) - 1] = crc;
450
451 dsp::ccsds::scrambler(ctx.m_tx_msg.data, radio.tx_len(ctx.m_iface));
452
453 radio.tx(ctx.m_iface, ctx.m_tx_msg.data);
454 ctx.frame_sent(true);
455
456 /*
457 * Update the timer that allows the interface to stay in TX mode for the
458 * configured amount of time to increase throughput
459 */
460 if (ctx.m_tx_wait) {
461 uint32_t wait_ms = ctx.m_iface == scl::radio::interface::UHF
463 : s.get<settings::param::SBAND_TX_WAIT_MS>();
464
465 k_timer_start(&ctx.m_timer, K_MSEC(wait_ms), K_NO_WAIT);
466
467 ctx.m_pending = false;
468 k_condvar_broadcast(&ctx.m_cond_var);
469 return No_State_Change;
470 } else {
471 k_timer_stop(&ctx.m_timer);
472 ctx.m_pending = false;
473 k_condvar_broadcast(&ctx.m_cond_var);
474 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::RX);
475 }
476 }
477 /* We can easily recover from the timeout exception */
478 catch (const scl::timeout_exception &e) {
479 ctx.frame_sent(false);
480 auto &err = error_handler::get_instance();
481 log.log({sc::logger::target::RTT},
482 ctx.m_iface == scl::radio::interface::UHF
483 ? "iface_ctrl<UHF>: Exception occurred"
484 : "iface_ctrl<S-Band>: Exception occurred");
485 err.handle(e);
486 ctx.m_pending = false;
487 k_condvar_broadcast(&ctx.m_cond_var);
488 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::RX);
489 } catch (const scl::exception &e) {
490 ctx.frame_sent(false);
491 auto &err = error_handler::get_instance();
492 log.log({sc::logger::target::RTT},
493 ctx.m_iface == scl::radio::interface::UHF
494 ? "iface_ctrl<UHF>: Exception occurred"
495 : "iface_ctrl<S-Band>: Exception occurred");
496 err.handle(e);
497 ctx.m_pending = false;
498 k_condvar_broadcast(&ctx.m_cond_var);
499 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::IDLE);
500 }
501}
502
503etl::fsm_state_id_t
504iface_ctrl::tx_state::on_event(const rx_msg &msg)
505{
506 auto &ctx = get_fsm_context();
507 /*
508 * NOTE: Disable first the timer to avoid accidental fireup while waiting for
509 * the operation to finish
510 */
511 k_timer_stop(&ctx.m_timer);
512 /* Let the final frame finish */
513 ctx.wait_pending_operation();
514 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::RX);
515}
516
517etl::fsm_state_id_t
518iface_ctrl::tx_state::on_event_unknown(const etl::imessage &msg)
519{
520 auto &ctx = get_fsm_context();
521 auto &log = sc::logger::get_instance();
522 log.log({sc::logger::target::RTT},
523 ctx.m_iface == scl::radio::interface::UHF
524 ? "iface_ctrl<UHF>:TX state unknown msg"
525 : "iface_ctrl<S-Band>: TX state unknown msg");
526 return No_State_Change;
527}
528
529/*****************************************************************************
530 *****************************************************************************
531 ***************************** RX state handling *****************************
532 *****************************************************************************
533 *****************************************************************************/
534
535etl::fsm_state_id_t
536iface_ctrl::rx_state::on_enter_state()
537{
538 auto &ctx = get_fsm_context();
539 auto &log = sc::logger::get_instance();
540 try {
541 /* Stop the timer we do not want to expire in the middle of an RX */
542 k_timer_stop(&ctx.m_timer);
543
544 log.log({sc::logger::target::RTT},
545 ctx.m_iface == scl::radio::interface::UHF
546 ? "iface_ctrl<UHF>: enter RX state"
547 : "iface_ctrl<S-Band>: enter RX state");
549 auto &s = settings::get_instance();
550
551 /*
552 * Force the interface to stop. This will close all the analog chain of the
553 * AT86RF215. Note that if the radio in general is not enabled, this does
554 * not make any sense.
555 */
556 if (radio.enabled()) {
557 radio.stop(ctx.m_iface);
558 }
559
560 /*
561 * If this is a transition from another state, apply the RX settings
562 */
563 radio.enable(ctx.m_iface);
564
565 /* Get the RX configuration */
566 lib::radio::rx_conf cnf;
567 s.get_rx_conf(ctx.m_iface, cnf);
568
569 radio.rx_async(ctx.m_iface, cnf);
570
571 /*
572 * Fireup the timer to enter IDLE mode. If any of the on or off periods are
573 * set to 0, this mechanism is not activated and the transceiver will stay
574 * at RX mode continuously or until a TX or disable event is requested
575 */
576 if (ctx.duty_cycle_enabled()) {
577 uint32_t on_secs = ctx.m_iface == scl::radio::interface::UHF
579 : s.get<settings::param::SBAND_RX_ON_SECS>();
580 k_timer_start(&ctx.m_timer, K_SECONDS(on_secs), K_NO_WAIT);
581 }
582
583 return No_State_Change;
584 /*
585 * In case of a known error, we go to the disabled state. From there we may
586 * recover
587 */
588 } catch (const scl::exception &e) {
589 auto &err = error_handler::get_instance();
590 log.log({sc::logger::target::RTT},
591 ctx.m_iface == scl::radio::interface::UHF
592 ? "iface_ctrl<UHF>: Exception occurred"
593 : "iface_ctrl<S-Band>: Exception occurred");
594 err.handle(e);
595 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::IDLE);
596 }
597}
598
599etl::fsm_state_id_t
600iface_ctrl::rx_state::on_event(const disable_msg &msg)
601{
602 auto &ctx = get_fsm_context();
603 /*
604 * NOTE: Disable first the timer to avoid accidental fireup while waiting for
605 * the operation to finish
606 */
607 k_timer_stop(&ctx.m_timer);
608 /* Let the final frame finish */
609 ctx.wait_pending_operation();
610 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::DISABLED);
611}
612
613etl::fsm_state_id_t
614iface_ctrl::rx_state::on_event(const tx_frame_msg &msg)
615{
616 auto &ctx = get_fsm_context();
617 auto &log = sc::logger::get_instance();
618 try {
619 /* Stop the timer we do not want to expire while we are waiting */
620 k_timer_stop(&ctx.m_timer);
621
622 /*
623 * Implement the GS TRX turnaround time taking also into account
624 * the time from the last frame received and go to TX state
625 */
626 auto &s = settings::get_instance();
627 uint32_t trx_time = ctx.m_iface == scl::radio::interface::UHF
629 : s.get<settings::param::SBAND_TRX_TURNAROUND_MS>();
630 int64_t now = k_uptime_get();
631 if (now - ctx.m_stats.last_rx_ts < trx_time) {
632 k_msleep(etl::clamp<uint64_t>(trx_time - (now - ctx.m_stats.last_rx_ts),
633 1U, MAX_TRX_MS));
634 }
635 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::TX);
636 } catch (const scl::exception &e) {
637 log.log({sc::logger::target::RTT},
638 ctx.m_iface == scl::radio::interface::UHF
639 ? "iface_ctrl<UHF>: Exception occurred"
640 : "iface_ctrl<S-Band>: Exception occurred");
641 auto &err = error_handler::get_instance();
642 err.handle(e);
643 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::IDLE);
644 }
645}
646
647etl::fsm_state_id_t
648iface_ctrl::rx_state::on_event(const idle_msg &msg)
649{
650 auto &ctx = get_fsm_context();
651 k_timer_stop(&ctx.m_timer);
652 /* Let the final frame finish */
653 ctx.wait_pending_operation();
654 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::IDLE);
655}
656
657etl::fsm_state_id_t
658iface_ctrl::rx_state::on_event(const frame_received_msg &msg)
659{
660 auto &ctx = get_fsm_context();
661 auto &s = settings::get_instance();
662 /* Just extend the ON period */
663 if (ctx.duty_cycle_enabled()) {
664 uint32_t on_secs = ctx.m_iface == scl::radio::interface::UHF
666 : s.get<settings::param::SBAND_RX_ON_SECS>();
667 k_timer_start(&ctx.m_timer, K_SECONDS(on_secs), K_NO_WAIT);
668 }
669 return No_State_Change;
670}
671
672etl::fsm_state_id_t
673iface_ctrl::rx_state::on_event_unknown(const etl::imessage &msg)
674{
675 auto &ctx = get_fsm_context();
676 auto &log = sc::logger::get_instance();
677 log.log({sc::logger::target::RTT},
678 ctx.m_iface == scl::radio::interface::UHF
679 ? "iface_ctrl<UHF>: RX state unknown msg"
680 : "iface_ctrl<S-Band>: RX state unknown msg");
681 return No_State_Change;
682}
683
684/*****************************************************************************
685 *****************************************************************************
686 ***************************** IDLE state handling ***************************
687 *****************************************************************************
688 *****************************************************************************/
689
690etl::fsm_state_id_t
691iface_ctrl::idle_state::on_enter_state()
692{
693 auto &ctx = get_fsm_context();
694 auto &log = sc::logger::get_instance();
695 auto &s = settings::get_instance();
696 k_timer_stop(&ctx.m_timer);
697
698 try {
699 log.log({sc::logger::target::RTT},
700 ctx.m_iface == scl::radio::interface::UHF
701 ? "iface_ctrl<UHF>: enter IDLE state"
702 : "iface_ctrl<S-Band>: enter IDLE state");
704 radio.stop(ctx.m_iface);
705
706 /*
707 * Make sure that the timer will fireup, even in the case somehow (race
708 * condition?) the OFF period was erroneously set to 0. In addtion, we use
709 * the IDLE state to recover from errors, so even if the OFF period is set
710 * to 0 by the user, the recovery will be attempted after 1 second
711 */
712 uint32_t off_secs = ctx.m_iface == scl::radio::interface::UHF
714 : s.get<settings::param::SBAND_RX_OFF_SECS>();
715 k_timer_start(&ctx.m_timer, K_SECONDS(std::max(1U, off_secs)), K_NO_WAIT);
716
717 /*
718 * In case of a known error, we stay at the same state. The re-enable timer
719 * may allow to recover
720 */
721 } catch (const scl::exception &e) {
722 log.log({sc::logger::target::RTT},
723 ctx.m_iface == scl::radio::interface::UHF
724 ? "iface_ctrl<UHF>: Exception occurred"
725 : "iface_ctrl<S-Band>: Exception occurred");
726 auto &err = error_handler::get_instance();
727 err.handle(e);
728 /* Similar approach as above */
729 uint32_t off_secs = ctx.m_iface == scl::radio::interface::UHF
731 : s.get<settings::param::SBAND_RX_OFF_SECS>();
732 k_timer_start(&ctx.m_timer, K_SECONDS(std::max(1U, off_secs)), K_NO_WAIT);
733 }
734 return No_State_Change;
735}
736
737void
738iface_ctrl::idle_state::on_exit_state()
739{
740 /* Too pedantic but just in case! */
741 auto &ctx = get_fsm_context();
742 k_timer_stop(&ctx.m_timer);
743}
744
745etl::fsm_state_id_t
746iface_ctrl::idle_state::on_event(const disable_msg &msg)
747{
748 auto &ctx = get_fsm_context();
749 /*
750 * NOTE: Disable first the timer to avoid accidental fireup while waiting for
751 * the operation to finish
752 */
753 k_timer_stop(&ctx.m_timer);
754 /* Let the final frame finish */
755 ctx.wait_pending_operation();
756 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::DISABLED);
757}
758
759etl::fsm_state_id_t
760iface_ctrl::idle_state::on_event(const tx_frame_msg &msg)
761{
762 /* While idling a TX may requested. Go directly to TX state */
763 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::TX);
764}
765
766etl::fsm_state_id_t
767iface_ctrl::idle_state::on_event(const rx_msg &msg)
768{
769 return static_cast<etl::fsm_state_id_t>(iface_ctrl::state::RX);
770}
771
772etl::fsm_state_id_t
773iface_ctrl::idle_state::on_event_unknown(const etl::imessage &msg)
774{
775 auto &ctx = get_fsm_context();
776 auto &log = sc::logger::get_instance();
777 log.log({sc::logger::target::RTT},
778 ctx.m_iface == scl::radio::interface::UHF
779 ? "iface_ctrl<UHF>: RX state unknown msg"
780 : "iface_ctrl<S-Band>: RX state unknown msg");
781 return No_State_Change;
782}
783
784} // namespace satnogs::comms::utils
static void scrambler(uint8_t *data, size_t len)
Definition ccsds.cpp:58
static error_handler & get_instance()
Singleton access to the error_handler subsystem.
static io_wdg & get_instance()
Singleton access to the io_wdg subsystem.
Definition io_wdg.hpp:62
void reset()
Resets the watchdog timer.
Definition io_wdg.cpp:160
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
void rx_async(interface iface, const rx_conf &conf)
Sets the radio in reception mode asynchronously.
Definition radio.cpp:845
void tx(interface iface, const uint8_t *b)
Performs a TX in blocking mode.
Definition radio.cpp:817
interface
Radio interface identifier.
Definition radio.hpp:177
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
void enable(bool yes=true)
Enable/disable the radio chain.
Definition radio.cpp:247
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
@ RTT
Real-Time Transfer logging (Segger RTT).
Definition logger.hpp:77
static logger & get_instance()
Singleton access to the logger subsystem.
Definition logger.hpp:102
uint8_t data[mtu]
Buffer to hold the data.
Implements a scoped lock utilizing the Zephyr mutex.
static settings & get_instance()
Get a singleton access to the settings subsystem.
Definition settings.hpp:210
Radio interface statistics.
uint64_t last_rx_ts
The timestamp of the RX frame.
size_t tx_frames
TX frames successfully sent.
void restart()
Restarts the interface by disabling and enabling it again.
void frame_received(bool valid, float rssi)
Updates the RX stats.
const stats & get_stats() const
Returns the statistics class of the interface.
void reset_stats()
Resets the TX/RX statistics of the interface.
void rx_frame_dropped()
Increments the RX drop counter.
@ TX
Interface is either TX a frame or wait to TX the next frame.
void tx(const msg_arbiter::msg &msg, bool more=false)
void enable()
Enables the interface and set it automatically to RX state.
static constexpr uint32_t MAX_TRX_MS
iface_ctrl(satnogs::comms::lib::radio::interface iface)
void disable()
Disables the interface.
iface_ctrl::state get_state() const
Returns the current state of the interface.
scl::radio radio
Definition settings.cpp:30