blob: ad2d056f1d075cb7be43d7477e37e71acf581386 [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;
Eric6a3e4b32023-04-26 22:12:06 +020087 static const unsigned int len_mul = 1;
Eric Wild621a49e2023-01-12 16:21:58 +010088};
89
90template <typename T>
91struct is_complex<std::complex<T>> : std::true_type {
92 using baset = typename std::complex<T>::value_type;
Eric6a3e4b32023-04-26 22:12:06 +020093 static const unsigned int len_mul = 2;
Eric Wild621a49e2023-01-12 16:21:58 +010094};
95
96template <typename T>
97struct is_complex<Complex<T>> : std::true_type {
98 using baset = typename Complex<T>::value_type;
Eric6a3e4b32023-04-26 22:12:06 +020099 static const unsigned int len_mul = 2;
Eric Wild621a49e2023-01-12 16:21:58 +0100100};
101
102} // namespace cvt_internal
103
104template <typename DST_T, typename SRC_T, typename ST>
105void convert_and_scale(DST_T *dst, const SRC_T *src, unsigned int src_len, ST scale)
106{
107 using vd = typename cvt_internal::is_complex<DST_T>::baset;
108 using vs = typename cvt_internal::is_complex<SRC_T>::baset;
109 return cvt_internal::convert_and_scale_i((vd *)dst, (vs *)src, src_len, scale);
Ericb7253c62022-11-28 19:21:08 +0100110}
111
Eric6a3e4b32023-04-26 22:12:06 +0200112template <typename array_t>
113float normed_abs_sum(array_t *src, int len)
114{
115 using vd = typename cvt_internal::is_complex<array_t>::baset;
116 auto len_mul = cvt_internal::is_complex<array_t>::len_mul;
117 auto ptr = reinterpret_cast<const vd *>(src);
118 float sum = 0;
119 for (unsigned int i = 0; i < len * len_mul; i++)
120 sum += std::abs(ptr[i]);
121 sum /= len * len_mul;
122 return sum;
123}
124
Ericb7253c62022-11-28 19:21:08 +0100125struct one_burst {
126 one_burst()
127 {
128 }
129 GSM::Time gsmts;
130 union {
131 blade_sample_type burst[ONE_TS_BURST_LEN];
132 char sch_bits[148];
133 };
134};
135
Ericc3e515a2023-05-08 12:56:56 +0200136using rx_queue_t = spsc_cond_timeout<8 * NUM_RXQ_FRAMES, one_burst, true, false>;
Ericb7253c62022-11-28 19:21:08 +0100137
138enum class SCH_STATE { SEARCHING, FOUND };
139
140class dummylog : private std::streambuf {
141 std::ostream null_stream;
142
143 public:
144 dummylog() : null_stream(this){};
145 ~dummylog() override{};
146 std::ostream &operator()()
147 {
148 return null_stream;
149 }
150 int overflow(int c) override
151 {
152 return c;
153 }
154};
155
156// keeps relationship between gsm time and (continuously adjusted) ts
157class time_keeper {
158 GSM::Time global_time_keeper;
159 int64_t global_ts_keeper;
160 std::mutex m;
161
162 public:
163 time_keeper() : global_time_keeper(0), global_ts_keeper(0)
164 {
165 }
166
167 void set(GSM::Time t, int64_t ts)
168 {
169 std::lock_guard<std::mutex> g(m);
170 global_time_keeper = t;
171 global_ts_keeper = ts;
172 }
173 void inc_both()
174 {
175 std::lock_guard<std::mutex> g(m);
176 global_time_keeper.incTN(1);
177 global_ts_keeper += ONE_TS_BURST_LEN;
178 }
179 void inc_and_update(int64_t new_ts)
180 {
181 std::lock_guard<std::mutex> g(m);
182 global_time_keeper.incTN(1);
183 global_ts_keeper = new_ts;
184 // std::cerr << "u " << new_ts << std::endl;
185 }
186 void inc_and_update_safe(int64_t new_ts)
187 {
188 std::lock_guard<std::mutex> g(m);
189 auto diff = new_ts - global_ts_keeper;
190 assert(diff < 1.5 * ONE_TS_BURST_LEN);
191 assert(diff > 0.5 * ONE_TS_BURST_LEN);
192 global_time_keeper.incTN(1);
193 global_ts_keeper = new_ts;
194 // std::cerr << "s " << new_ts << std::endl;
195 }
196 void dec_by_one()
197 {
198 std::lock_guard<std::mutex> g(m);
199 global_time_keeper.decTN(1);
200 global_ts_keeper -= ONE_TS_BURST_LEN;
201 }
202 auto get_ts()
203 {
204 std::lock_guard<std::mutex> g(m);
205 return global_ts_keeper;
206 }
207 auto gsmtime()
208 {
209 std::lock_guard<std::mutex> g(m);
210 return global_time_keeper;
211 }
212 void get_both(GSM::Time *t, int64_t *ts)
213 {
214 std::lock_guard<std::mutex> g(m);
215 *t = global_time_keeper;
216 *ts = global_ts_keeper;
217 }
218};
219
Eric805e0d92023-05-23 11:29:18 +0200220static struct sched_params {
221 enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
222 enum target { ODROID = 0, PI4 };
223 const char *name;
224 int core;
225 int schedtype;
226 int prio;
227} schdp[][sched_params::_THRD_NAME_COUNT]{
228 {
229 { "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
230 { "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
231 { "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
232
233 { "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
234 { "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
235 { "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
236
237 { "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
238 { "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
239 },
240 {
241 { "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
242 { "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
243 { "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
244
245 { "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
246 { "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
247 { "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
248
249 { "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
250 { "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
251 },
252};
253
Ericb7253c62022-11-28 19:21:08 +0100254using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
255
256struct ms_trx : public BASET {
257 using base = BASET;
258 static dummylog dummy_log;
259 unsigned int mTSC;
260 unsigned int mBSIC;
261 int timing_advance;
262 bool do_auto_gain;
263
Ericda5ffd62023-05-23 11:00:32 +0200264 std::thread lower_rx_task;
265 std::thread lower_tx_task;
Ericb7253c62022-11-28 19:21:08 +0100266 std::thread *calcrval_task;
267
268 // provides bursts to upper rx thread
269 rx_queue_t rxqueue;
Ericea7bd5f2023-05-02 15:21:45 +0200270
Ericb7253c62022-11-28 19:21:08 +0100271 blade_sample_type *first_sch_buf;
272 blade_sample_type *burst_copy_buffer;
273
274 uint64_t first_sch_buf_rcv_ts;
275 std::atomic<bool> rcv_done;
276 std::atomic<bool> sch_thread_done;
277
278 int64_t temp_ts_corr_offset = 0;
279 int64_t first_sch_ts_start = -1;
280
281 time_keeper timekeeper;
Eric805e0d92023-05-23 11:29:18 +0200282 int hw_cpus;
283 sched_params::target hw_target;
Eric3e7f4b02023-05-23 12:50:25 +0200284 single_thread_pool worker_thread;
Ericb7253c62022-11-28 19:21:08 +0100285
Ericc3e515a2023-05-08 12:56:56 +0200286 void start_lower_ms();
Ericb7253c62022-11-28 19:21:08 +0100287 std::atomic<bool> upper_is_ready;
288 void set_upper_ready(bool is_ready);
289
290 bool handle_sch_or_nb();
291 bool handle_sch(bool first = false);
292 bool decode_sch(float *bits, bool update_global_clock);
293 SCH_STATE search_for_sch(dev_buf_t *rcd);
294 void grab_bursts(dev_buf_t *rcd);
295
296 int init_device();
297 int init_dev_and_streams();
298 void stop_threads();
299 void *rx_cb(ms_trx *t);
300 void *tx_cb();
301 void maybe_update_gain(one_burst &brst);
302
303 ms_trx()
304 : timing_advance(0), do_auto_gain(false), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
Eric805e0d92023-05-23 11:29:18 +0200305 burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), rcv_done{ false },
306 sch_thread_done{ false }, hw_cpus(std::thread::hardware_concurrency()),
307 hw_target(hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4)
Ericb7253c62022-11-28 19:21:08 +0100308 {
Eric805e0d92023-05-23 11:29:18 +0200309 std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
Eric3e7f4b02023-05-23 12:50:25 +0200310 set_name_aff_sched(worker_thread.get_handle(), sched_params::thread_names::SCH_SEARCH);
Ericb7253c62022-11-28 19:21:08 +0100311 }
312
313 virtual ~ms_trx()
314 {
315 delete[] burst_copy_buffer;
316 delete[] first_sch_buf;
317 }
318 bh_fn_t rx_bh();
319 bh_fn_t tx_bh();
320
321 void submit_burst(blade_sample_type *buffer, int len, GSM::Time);
322 void set_ta(int val)
323 {
324 assert(val > -127 && val < 128);
325 timing_advance = val * 4;
326 }
327
Eric805e0d92023-05-23 11:29:18 +0200328 void set_name_aff_sched(sched_params::thread_names name)
Ericb7253c62022-11-28 19:21:08 +0100329 {
Eric805e0d92023-05-23 11:29:18 +0200330 set_name_aff_sched(pthread_self(), name);
Ericb7253c62022-11-28 19:21:08 +0100331 }
332
Eric805e0d92023-05-23 11:29:18 +0200333 void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
334 {
335 auto tgt = schdp[hw_target][name];
336 // std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
337 set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
338 }
339
340 private:
Ericb7253c62022-11-28 19:21:08 +0100341 void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
342 int prio)
343 {
344 pthread_setname_np(h, name);
345
346 cpu_set_t cpuset;
347
348 CPU_ZERO(&cpuset);
349 CPU_SET(cpunum, &cpuset);
350
Eric805e0d92023-05-23 11:29:18 +0200351 if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
Ericb7253c62022-11-28 19:21:08 +0100352 std::cerr << name << " affinity: errreur! " << std::strerror(errno);
353 return exit(0);
354 }
355
356 sched_param sch_params;
357 sch_params.sched_priority = prio;
Eric805e0d92023-05-23 11:29:18 +0200358 if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
Ericb7253c62022-11-28 19:21:08 +0100359 std::cerr << name << " sched: errreur! " << std::strerror(errno);
360 return exit(0);
361 }
362 }
363};