blob: 3b97d1bae0c8553a125e9095c2026c7f9662a1df [file] [log] [blame]
Ericb7253c62022-11-28 19:21:08 +01001#pragma once
2/*
3 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * Author: Eric Wild <ewild@sysmocom.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <atomic>
24#include <cassert>
25#include <complex>
26#include <cstdint>
27#include <mutex>
28#include <iostream>
29#include <thread>
30
31#if defined(BUILDBLADE)
32#include "bladerf_specific.h"
33#define BASET blade_hw<ms_trx>
34#elif defined(BUILDUHD)
35#include "uhd_specific.h"
36#define BASET uhd_hw<ms_trx>
37#else
38#error wat? no device..
39#endif
40
Eric Wild621a49e2023-01-12 16:21:58 +010041#include "Complex.h"
Ericb7253c62022-11-28 19:21:08 +010042#include "GSMCommon.h"
43#include "itrq.h"
Eric3e7f4b02023-05-23 12:50:25 +020044#include "threadpool.h"
Ericb7253c62022-11-28 19:21:08 +010045
46const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
47const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
48const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
49
Eric Wild621a49e2023-01-12 16:21:58 +010050template <typename T>
51void clamp_array(T *start2, unsigned int len, T max)
Ericb7253c62022-11-28 19:21:08 +010052{
53 for (int i = 0; i < len; i++) {
54 const T t1 = start2[i] < -max ? -max : start2[i];
55 const T t2 = t1 > max ? max : t1;
56 start2[i] = t2;
57 }
58}
Eric Wild621a49e2023-01-12 16:21:58 +010059
60namespace cvt_internal
61{
62
63template <typename SRC_T, typename ST>
64void convert_and_scale_i(float *dst, const SRC_T *src, unsigned int src_len, ST scale)
Ericb7253c62022-11-28 19:21:08 +010065{
66 for (unsigned int i = 0; i < src_len; i++)
Eric Wild621a49e2023-01-12 16:21:58 +010067 dst[i] = static_cast<float>(src[i]) * scale;
Ericb7253c62022-11-28 19:21:08 +010068}
Eric Wild621a49e2023-01-12 16:21:58 +010069
70template <typename DST_T, typename ST>
71void convert_and_scale_i(DST_T *dst, const float *src, unsigned int src_len, ST scale)
Ericb7253c62022-11-28 19:21:08 +010072{
Eric Wild621a49e2023-01-12 16:21:58 +010073 for (unsigned int i = 0; i < src_len; i++)
74 dst[i] = static_cast<DST_T>(src[i] * scale);
75}
76
77template <typename ST>
78void convert_and_scale_i(float *dst, const float *src, unsigned int src_len, ST scale)
79{
80 for (unsigned int i = 0; i < src_len; i++)
81 dst[i] = src[i] * scale;
82}
83
84template <typename T>
85struct is_complex : std::false_type {
86 using baset = T;
87};
88
89template <typename T>
90struct is_complex<std::complex<T>> : std::true_type {
91 using baset = typename std::complex<T>::value_type;
92};
93
94template <typename T>
95struct is_complex<Complex<T>> : std::true_type {
96 using baset = typename Complex<T>::value_type;
97};
98
99} // namespace cvt_internal
100
101template <typename DST_T, typename SRC_T, typename ST>
102void convert_and_scale(DST_T *dst, const SRC_T *src, unsigned int src_len, ST scale)
103{
104 using vd = typename cvt_internal::is_complex<DST_T>::baset;
105 using vs = typename cvt_internal::is_complex<SRC_T>::baset;
106 return cvt_internal::convert_and_scale_i((vd *)dst, (vs *)src, src_len, scale);
Ericb7253c62022-11-28 19:21:08 +0100107}
108
109struct one_burst {
110 one_burst()
111 {
112 }
113 GSM::Time gsmts;
114 union {
115 blade_sample_type burst[ONE_TS_BURST_LEN];
116 char sch_bits[148];
117 };
118};
119
Eric4080cd02023-05-23 11:32:49 +0200120using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, false>;
Ericb7253c62022-11-28 19:21:08 +0100121
122enum class SCH_STATE { SEARCHING, FOUND };
123
124class dummylog : private std::streambuf {
125 std::ostream null_stream;
126
127 public:
128 dummylog() : null_stream(this){};
129 ~dummylog() override{};
130 std::ostream &operator()()
131 {
132 return null_stream;
133 }
134 int overflow(int c) override
135 {
136 return c;
137 }
138};
139
140// keeps relationship between gsm time and (continuously adjusted) ts
141class time_keeper {
142 GSM::Time global_time_keeper;
143 int64_t global_ts_keeper;
144 std::mutex m;
145
146 public:
147 time_keeper() : global_time_keeper(0), global_ts_keeper(0)
148 {
149 }
150
151 void set(GSM::Time t, int64_t ts)
152 {
153 std::lock_guard<std::mutex> g(m);
154 global_time_keeper = t;
155 global_ts_keeper = ts;
156 }
157 void inc_both()
158 {
159 std::lock_guard<std::mutex> g(m);
160 global_time_keeper.incTN(1);
161 global_ts_keeper += ONE_TS_BURST_LEN;
162 }
163 void inc_and_update(int64_t new_ts)
164 {
165 std::lock_guard<std::mutex> g(m);
166 global_time_keeper.incTN(1);
167 global_ts_keeper = new_ts;
168 // std::cerr << "u " << new_ts << std::endl;
169 }
170 void inc_and_update_safe(int64_t new_ts)
171 {
172 std::lock_guard<std::mutex> g(m);
173 auto diff = new_ts - global_ts_keeper;
174 assert(diff < 1.5 * ONE_TS_BURST_LEN);
175 assert(diff > 0.5 * ONE_TS_BURST_LEN);
176 global_time_keeper.incTN(1);
177 global_ts_keeper = new_ts;
178 // std::cerr << "s " << new_ts << std::endl;
179 }
180 void dec_by_one()
181 {
182 std::lock_guard<std::mutex> g(m);
183 global_time_keeper.decTN(1);
184 global_ts_keeper -= ONE_TS_BURST_LEN;
185 }
186 auto get_ts()
187 {
188 std::lock_guard<std::mutex> g(m);
189 return global_ts_keeper;
190 }
191 auto gsmtime()
192 {
193 std::lock_guard<std::mutex> g(m);
194 return global_time_keeper;
195 }
196 void get_both(GSM::Time *t, int64_t *ts)
197 {
198 std::lock_guard<std::mutex> g(m);
199 *t = global_time_keeper;
200 *ts = global_ts_keeper;
201 }
202};
203
Eric805e0d92023-05-23 11:29:18 +0200204static struct sched_params {
205 enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
206 enum target { ODROID = 0, PI4 };
207 const char *name;
208 int core;
209 int schedtype;
210 int prio;
211} schdp[][sched_params::_THRD_NAME_COUNT]{
212 {
213 { "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
214 { "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
215 { "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
216
217 { "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
218 { "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
219 { "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
220
221 { "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
222 { "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
223 },
224 {
225 { "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
226 { "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
227 { "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
228
229 { "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
230 { "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
231 { "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
232
233 { "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
234 { "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
235 },
236};
237
Ericb7253c62022-11-28 19:21:08 +0100238using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
239
240struct ms_trx : public BASET {
241 using base = BASET;
242 static dummylog dummy_log;
243 unsigned int mTSC;
244 unsigned int mBSIC;
245 int timing_advance;
246 bool do_auto_gain;
247
Ericda5ffd62023-05-23 11:00:32 +0200248 std::thread lower_rx_task;
249 std::thread lower_tx_task;
Ericb7253c62022-11-28 19:21:08 +0100250 std::thread *calcrval_task;
251
252 // provides bursts to upper rx thread
253 rx_queue_t rxqueue;
254#ifdef SYNCTHINGONLY
255 ts_hitter_q_t ts_hitter_q;
256#endif
257 blade_sample_type *first_sch_buf;
258 blade_sample_type *burst_copy_buffer;
259
260 uint64_t first_sch_buf_rcv_ts;
261 std::atomic<bool> rcv_done;
262 std::atomic<bool> sch_thread_done;
263
264 int64_t temp_ts_corr_offset = 0;
265 int64_t first_sch_ts_start = -1;
266
267 time_keeper timekeeper;
Eric805e0d92023-05-23 11:29:18 +0200268 int hw_cpus;
269 sched_params::target hw_target;
Eric3e7f4b02023-05-23 12:50:25 +0200270 single_thread_pool worker_thread;
Ericb7253c62022-11-28 19:21:08 +0100271
272 void start();
273 std::atomic<bool> upper_is_ready;
274 void set_upper_ready(bool is_ready);
275
276 bool handle_sch_or_nb();
277 bool handle_sch(bool first = false);
278 bool decode_sch(float *bits, bool update_global_clock);
279 SCH_STATE search_for_sch(dev_buf_t *rcd);
280 void grab_bursts(dev_buf_t *rcd);
281
282 int init_device();
283 int init_dev_and_streams();
284 void stop_threads();
285 void *rx_cb(ms_trx *t);
286 void *tx_cb();
287 void maybe_update_gain(one_burst &brst);
288
289 ms_trx()
290 : timing_advance(0), do_auto_gain(false), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
Eric805e0d92023-05-23 11:29:18 +0200291 burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), rcv_done{ false },
292 sch_thread_done{ false }, hw_cpus(std::thread::hardware_concurrency()),
293 hw_target(hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4)
Ericb7253c62022-11-28 19:21:08 +0100294 {
Eric805e0d92023-05-23 11:29:18 +0200295 std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
Eric3e7f4b02023-05-23 12:50:25 +0200296 set_name_aff_sched(worker_thread.get_handle(), sched_params::thread_names::SCH_SEARCH);
Ericb7253c62022-11-28 19:21:08 +0100297 }
298
299 virtual ~ms_trx()
300 {
301 delete[] burst_copy_buffer;
302 delete[] first_sch_buf;
303 }
304 bh_fn_t rx_bh();
305 bh_fn_t tx_bh();
306
307 void submit_burst(blade_sample_type *buffer, int len, GSM::Time);
308 void set_ta(int val)
309 {
310 assert(val > -127 && val < 128);
311 timing_advance = val * 4;
312 }
313
Eric805e0d92023-05-23 11:29:18 +0200314 void set_name_aff_sched(sched_params::thread_names name)
Ericb7253c62022-11-28 19:21:08 +0100315 {
Eric805e0d92023-05-23 11:29:18 +0200316 set_name_aff_sched(pthread_self(), name);
Ericb7253c62022-11-28 19:21:08 +0100317 }
318
Eric805e0d92023-05-23 11:29:18 +0200319 void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
320 {
321 auto tgt = schdp[hw_target][name];
322 // std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
323 set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
324 }
325
326 private:
Ericb7253c62022-11-28 19:21:08 +0100327 void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
328 int prio)
329 {
330 pthread_setname_np(h, name);
331
332 cpu_set_t cpuset;
333
334 CPU_ZERO(&cpuset);
335 CPU_SET(cpunum, &cpuset);
336
Eric805e0d92023-05-23 11:29:18 +0200337 if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
Ericb7253c62022-11-28 19:21:08 +0100338 std::cerr << name << " affinity: errreur! " << std::strerror(errno);
339 return exit(0);
340 }
341
342 sched_param sch_params;
343 sch_params.sched_priority = prio;
Eric805e0d92023-05-23 11:29:18 +0200344 if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
Ericb7253c62022-11-28 19:21:08 +0100345 std::cerr << name << " sched: errreur! " << std::strerror(errno);
346 return exit(0);
347 }
348 }
349};