SatNOGS-COMMS  4.1.0
A COMMS subsystem for CubeSats
Loading...
Searching...
No Matches
test.cpp
Go to the documentation of this file.
1/*
2 * SatNOGS-COMMS MCU software
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 "test.hpp"
23#include "logger.hpp"
24#include "msg_arbiter.hpp"
25#include "settings.hpp"
26#include "telemetry.hpp"
27#include <zephyr/drivers/disk.h>
28#include <zephyr/kernel.h>
29#include <zephyr/random/random.h>
30#include <zephyr/storage/disk_access.h>
31#include <zephyr/task_wdt/task_wdt.h>
32
33namespace scl = satnogs::comms::lib;
34namespace satnogs::comms
35{
36
37// FIXME: This has to be defined in a more elegant way
38#define EMMC_VOLUME_NAME "SD"
39
40#ifdef CONFIG_TEST_ENABLED
41static uint8_t buffer[512];
42static uint8_t read_buff[512];
43static struct k_work_q test_workq;
44K_THREAD_STACK_DEFINE(test_workq_stack, CONFIG_TEST_WORKQUEUE_STACK_SIZE);
45#endif
46
47void
48test::init_workq()
49{
50#ifdef CONFIG_TEST_ENABLED
51 const k_work_queue_config cfg = {.name = "test_workq", .no_yield = 0};
52 k_work_queue_start(&test_workq, test_workq_stack,
53 K_THREAD_STACK_SIZEOF(test_workq_stack),
54 CONFIG_TEST_WORKQUEUE_PRIO, &cfg);
55
56 k_work_init(&m_test_internal_params.work, &test::exec);
57 return;
58#else
59 return;
60#endif
61}
62
63void
64test::exec_test(test_id id, std::initializer_list<uint32_t> params)
65{
66#ifdef CONFIG_TEST_ENABLED
67 switch (id) {
69 m_test_internal_params.test_id = static_cast<int>(id);
70 m_test_internal_params.param0 = params.begin()[0];
71 m_test_internal_params.param1 = params.begin()[1];
72 m_test_internal_params.param2 = params.begin()[2];
73 break;
75 m_test_internal_params.test_id = static_cast<int>(id);
76 m_test_internal_params.param0 = params.begin()[0];
77 m_test_internal_params.param1 = params.begin()[1];
78 m_test_internal_params.param2 = params.begin()[2];
79 break;
81 m_test_internal_params.test_id = static_cast<int>(id);
82 m_test_internal_params.param0 = params.begin()[0];
83 break;
84 default:
85 return;
86 }
87 k_work_submit_to_queue(&test_workq, &m_test_internal_params.work);
88#endif
89}
90
91void
92test::uhf_tx_simple(uint32_t nframes, uint32_t delay_us, uint32_t len)
93{
94 tx_simple(scl::radio::interface::UHF, nframes, delay_us, len);
95}
96
97void
98test::sband_tx_simple(uint32_t nframes, uint32_t delay_us, uint32_t len)
99{
100 tx_simple(scl::radio::interface::SBAND, nframes, delay_us, len);
101}
102
103static auto radio_msg = msg_arbiter::msg();
104
105void
106test::tx_simple(scl::radio::interface iface, uint32_t nframes,
107 uint32_t delay_us, uint32_t len)
108{
109 auto &arb = msg_arbiter::get_instance();
110
111 switch (iface) {
113 radio_msg.iface = msg_arbiter::subsys::RADIO_UHF;
114 break;
116 radio_msg.iface = msg_arbiter::subsys::RADIO_SBAND;
117 break;
118 default:
119 return;
120 }
121
122 /*
123 * Re-use the ping telemetry response. With this way, frames can be processed
124 * by e.g YAMCS for further usage (e.g BER tests)
125 */
126 telemetry::ccsds_tm_header tm_header;
127 tm_header.apid = static_cast<uint16_t>(telemetry::apid::PING_RESP);
128
129 /* The payload should be at least one byte */
130 len = std::max(len, telemetry::ccsds_tm_header::size + 1);
131 len =
132 etl::clamp<uint32_t>(len - telemetry::ccsds_tm_header::size, 1,
134
135 radio_msg.len = len + telemetry::ccsds_tm_header::size;
136
137 tm_header.length = len;
138 for (uint32_t i = 0; i < nframes && m_stop == false; i++) {
139
140 etl::bit_stream_writer bit_stream(
141 radio_msg.data, telemetry::ccsds_tm_header::size, etl::endian::big);
142 tm_header.count = i;
143 tm_header.serialize(bit_stream);
144
145 /* Nothing fancy on the payload. We want speed! */
146 for (size_t j = telemetry::ccsds_tm_header::size;
147 j < len + telemetry::ccsds_tm_header::size; j++) {
148 radio_msg.data[j] = j;
149 }
150
151 arb.fwd(0, 0, radio_msg, radio_msg.iface, K_FOREVER);
152 k_usleep(std::max(1U, delay_us));
153 }
154}
155
156bool
158{
159 return m_emmc_success;
160}
161
162void
163test::emmc_simple(uint32_t nbytes)
164{
165#ifdef CONFIG_TEST_ENABLED
166 auto &emmc = scl::board::get_instance().emmc();
167 emmc.enable(true);
168 emmc.reset(true);
169 k_msleep(10);
170 emmc.reset(false);
171 emmc.set_dir(scl::emmc::dir::MCU);
172
173 int ret = disk_access_ioctl(EMMC_VOLUME_NAME, DISK_IOCTL_CTRL_INIT, NULL);
174 uint32_t sec_size = 512;
175 // for now we only accept multiples of `sec_size`
176 if (nbytes % sec_size != 0) {
177 return;
178 }
179
180 int num_of_sectors = nbytes / sec_size;
181
182 int k = 0;
183 for (uint32_t i = 0; i < sec_size; i++) {
184 if (i % 4 == 0)
185 k++;
186 buffer[i] = k;
187 }
188
189 int i;
190 for (i = 0; i < num_of_sectors; i++) {
191 ret = disk_access_write(EMMC_VOLUME_NAME, buffer, i, 1);
192
193 if (ret) {
194 return;
195 }
196 ret = disk_access_read(EMMC_VOLUME_NAME, read_buff, i, 1);
197 k_yield();
198 if (ret) {
199 return;
200 }
201
202 if (memcmp(read_buff, buffer, sec_size)) {
203 return;
204 }
205 memset(read_buff, 0xff, sec_size);
206 }
207 m_emmc_success = true;
208 emmc.reset(true);
209 k_msleep(50);
210 emmc.enable(false);
211 return;
212#endif
213}
214
221void
223{
224 auto &emmc = scl::board::get_instance().emmc();
225 auto fpga = scl::board::get_instance().fpga();
226 emmc.enable(true);
227 fpga.enable();
228 emmc.set_dir(scl::emmc::dir::MCU);
229 emmc.reset(true);
230 k_msleep(1);
231 emmc.reset(false);
232 while (1) {
233 uhf_tx_simple(4, 100000, 512);
234 k_sleep(K_SECONDS(30));
235 sband_tx_simple(10, 100000, 512);
236 k_sleep(K_SECONDS(10));
237 emmc_simple(512 * 1000);
238 }
239}
240
241void
242test::exec(k_work *item)
243{
244#ifdef CONFIG_TEST_ENABLED
245
246 auto &t = test::get_instance();
247 while (t.m_running) {
248 k_sleep(K_MSEC(10));
249 }
250
251 t.m_running = true;
252 t.m_stop = false;
253
254 struct params_work_container *params =
255 CONTAINER_OF(item, struct params_work_container, work);
256 switch (static_cast<test_id>(params->test_id)) {
258 t.uhf_tx_simple(params->param0, params->param1, params->param2);
259 break;
260 }
262 t.sband_tx_simple(params->param0, params->param1, params->param2);
263 break;
264 }
266 t.emmc_simple(params->param0);
267 break;
268 }
270 t.emc_routine();
271 }
272 default:
273 break;
274 }
275
276 t.m_running = false;
277 t.m_stop = false;
278#endif
279}
280
281void
283{
284 m_stop = true;
285}
286
287bool
289{
290 return m_running;
291}
292
293test::test() : m_emmc_success(false), m_running(false), m_stop(false)
294{
295 init_workq();
296}
297
298} // namespace satnogs::comms
comms::lib::emmc & emmc()
Returns a reference to the eMMC subsystem.
Definition board.cpp:91
comms::lib::fpga & fpga()
Returns a reference to the FPGA subsystem.
Definition board.cpp:113
static board & get_instance()
Gets a reference to the single instance of the Board interface class.
Definition board.cpp:66
void enable(bool set=false)
Definition emmc.cpp:46
void enable(bool en=true)
Enable/disable the FPGA subsystem.
Definition fpga.cpp:83
interface
Radio interface identifier.
Definition radio.hpp:177
static constexpr size_t mtu
static msg_arbiter & get_instance()
Singleton access to a unique and global msg_arbiter instance.
bool running() const
Definition test.cpp:288
bool emmc_success() const
Definition test.cpp:157
test(test const &)=delete
void exec_test(test_id id, std::initializer_list< uint32_t > params)
Definition test.cpp:64
static test & get_instance()
Definition test.hpp:42
void emc_routine()
A simple routine for EMC testing.
Definition test.cpp:222
K_THREAD_STACK_DEFINE(radio_rx_thread_stack, CONFIG_RADIO_RX_THREAD_STACK_SIZE)
#define EMMC_VOLUME_NAME
Definition test.cpp:38