blob: dbf2e9aa0adae5a5205c48fa6adcbcd02f45c5a1 [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"
44
45const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
46const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
47const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
48
Eric Wild621a49e2023-01-12 16:21:58 +010049template <typename T>
50void clamp_array(T *start2, unsigned int len, T max)
Ericb7253c62022-11-28 19:21:08 +010051{
52 for (int i = 0; i < len; i++) {
53 const T t1 = start2[i] < -max ? -max : start2[i];
54 const T t2 = t1 > max ? max : t1;
55 start2[i] = t2;
56 }
57}
Eric Wild621a49e2023-01-12 16:21:58 +010058
59namespace cvt_internal
60{
61
62template <typename SRC_T, typename ST>
63void convert_and_scale_i(float *dst, const SRC_T *src, unsigned int src_len, ST scale)
Ericb7253c62022-11-28 19:21:08 +010064{
65 for (unsigned int i = 0; i < src_len; i++)
Eric Wild621a49e2023-01-12 16:21:58 +010066 dst[i] = static_cast<float>(src[i]) * scale;
Ericb7253c62022-11-28 19:21:08 +010067}
Eric Wild621a49e2023-01-12 16:21:58 +010068
69template <typename DST_T, typename ST>
70void convert_and_scale_i(DST_T *dst, const float *src, unsigned int src_len, ST scale)
Ericb7253c62022-11-28 19:21:08 +010071{
Eric Wild621a49e2023-01-12 16:21:58 +010072 for (unsigned int i = 0; i < src_len; i++)
73 dst[i] = static_cast<DST_T>(src[i] * scale);
74}
75
76template <typename ST>
77void convert_and_scale_i(float *dst, const float *src, unsigned int src_len, ST scale)
78{
79 for (unsigned int i = 0; i < src_len; i++)
80 dst[i] = src[i] * scale;
81}
82
83template <typename T>
84struct is_complex : std::false_type {
85 using baset = T;
86};
87
88template <typename T>
89struct is_complex<std::complex<T>> : std::true_type {
90 using baset = typename std::complex<T>::value_type;
91};
92
93template <typename T>
94struct is_complex<Complex<T>> : std::true_type {
95 using baset = typename Complex<T>::value_type;
96};
97
98} // namespace cvt_internal
99
100template <typename DST_T, typename SRC_T, typename ST>
101void convert_and_scale(DST_T *dst, const SRC_T *src, unsigned int src_len, ST scale)
102{
103 using vd = typename cvt_internal::is_complex<DST_T>::baset;
104 using vs = typename cvt_internal::is_complex<SRC_T>::baset;
105 return cvt_internal::convert_and_scale_i((vd *)dst, (vs *)src, src_len, scale);
Ericb7253c62022-11-28 19:21:08 +0100106}
107
108struct one_burst {
109 one_burst()
110 {
111 }
112 GSM::Time gsmts;
113 union {
114 blade_sample_type burst[ONE_TS_BURST_LEN];
115 char sch_bits[148];
116 };
117};
118
119using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
120
121enum class SCH_STATE { SEARCHING, FOUND };
122
123class dummylog : private std::streambuf {
124 std::ostream null_stream;
125
126 public:
127 dummylog() : null_stream(this){};
128 ~dummylog() override{};
129 std::ostream &operator()()
130 {
131 return null_stream;
132 }
133 int overflow(int c) override
134 {
135 return c;
136 }
137};
138
139// keeps relationship between gsm time and (continuously adjusted) ts
140class time_keeper {
141 GSM::Time global_time_keeper;
142 int64_t global_ts_keeper;
143 std::mutex m;
144
145 public:
146 time_keeper() : global_time_keeper(0), global_ts_keeper(0)
147 {
148 }
149
150 void set(GSM::Time t, int64_t ts)
151 {
152 std::lock_guard<std::mutex> g(m);
153 global_time_keeper = t;
154 global_ts_keeper = ts;
155 }
156 void inc_both()
157 {
158 std::lock_guard<std::mutex> g(m);
159 global_time_keeper.incTN(1);
160 global_ts_keeper += ONE_TS_BURST_LEN;
161 }
162 void inc_and_update(int64_t new_ts)
163 {
164 std::lock_guard<std::mutex> g(m);
165 global_time_keeper.incTN(1);
166 global_ts_keeper = new_ts;
167 // std::cerr << "u " << new_ts << std::endl;
168 }
169 void inc_and_update_safe(int64_t new_ts)
170 {
171 std::lock_guard<std::mutex> g(m);
172 auto diff = new_ts - global_ts_keeper;
173 assert(diff < 1.5 * ONE_TS_BURST_LEN);
174 assert(diff > 0.5 * ONE_TS_BURST_LEN);
175 global_time_keeper.incTN(1);
176 global_ts_keeper = new_ts;
177 // std::cerr << "s " << new_ts << std::endl;
178 }
179 void dec_by_one()
180 {
181 std::lock_guard<std::mutex> g(m);
182 global_time_keeper.decTN(1);
183 global_ts_keeper -= ONE_TS_BURST_LEN;
184 }
185 auto get_ts()
186 {
187 std::lock_guard<std::mutex> g(m);
188 return global_ts_keeper;
189 }
190 auto gsmtime()
191 {
192 std::lock_guard<std::mutex> g(m);
193 return global_time_keeper;
194 }
195 void get_both(GSM::Time *t, int64_t *ts)
196 {
197 std::lock_guard<std::mutex> g(m);
198 *t = global_time_keeper;
199 *ts = global_ts_keeper;
200 }
201};
202
Eric805e0d92023-05-23 11:29:18 +0200203static struct sched_params {
204 enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
205 enum target { ODROID = 0, PI4 };
206 const char *name;
207 int core;
208 int schedtype;
209 int prio;
210} schdp[][sched_params::_THRD_NAME_COUNT]{
211 {
212 { "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
213 { "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
214 { "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
215
216 { "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
217 { "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
218 { "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
219
220 { "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
221 { "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
222 },
223 {
224 { "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
225 { "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
226 { "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
227
228 { "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
229 { "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
230 { "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
231
232 { "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
233 { "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
234 },
235};
236
Ericb7253c62022-11-28 19:21:08 +0100237using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
238
239struct ms_trx : public BASET {
240 using base = BASET;
241 static dummylog dummy_log;
242 unsigned int mTSC;
243 unsigned int mBSIC;
244 int timing_advance;
245 bool do_auto_gain;
246
247 std::thread rx_task;
248 std::thread tx_task;
249 std::thread *calcrval_task;
250
251 // provides bursts to upper rx thread
252 rx_queue_t rxqueue;
253#ifdef SYNCTHINGONLY
254 ts_hitter_q_t ts_hitter_q;
255#endif
256 blade_sample_type *first_sch_buf;
257 blade_sample_type *burst_copy_buffer;
258
259 uint64_t first_sch_buf_rcv_ts;
260 std::atomic<bool> rcv_done;
261 std::atomic<bool> sch_thread_done;
262
263 int64_t temp_ts_corr_offset = 0;
264 int64_t first_sch_ts_start = -1;
265
266 time_keeper timekeeper;
Eric805e0d92023-05-23 11:29:18 +0200267 int hw_cpus;
268 sched_params::target hw_target;
Ericb7253c62022-11-28 19:21:08 +0100269
270 void start();
271 std::atomic<bool> upper_is_ready;
272 void set_upper_ready(bool is_ready);
273
274 bool handle_sch_or_nb();
275 bool handle_sch(bool first = false);
276 bool decode_sch(float *bits, bool update_global_clock);
277 SCH_STATE search_for_sch(dev_buf_t *rcd);
278 void grab_bursts(dev_buf_t *rcd);
279
280 int init_device();
281 int init_dev_and_streams();
282 void stop_threads();
283 void *rx_cb(ms_trx *t);
284 void *tx_cb();
285 void maybe_update_gain(one_burst &brst);
286
287 ms_trx()
288 : timing_advance(0), do_auto_gain(false), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
Eric805e0d92023-05-23 11:29:18 +0200289 burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), rcv_done{ false },
290 sch_thread_done{ false }, hw_cpus(std::thread::hardware_concurrency()),
291 hw_target(hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4)
Ericb7253c62022-11-28 19:21:08 +0100292 {
Eric805e0d92023-05-23 11:29:18 +0200293 std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
Ericb7253c62022-11-28 19:21:08 +0100294 }
295
296 virtual ~ms_trx()
297 {
298 delete[] burst_copy_buffer;
299 delete[] first_sch_buf;
300 }
301 bh_fn_t rx_bh();
302 bh_fn_t tx_bh();
303
304 void submit_burst(blade_sample_type *buffer, int len, GSM::Time);
305 void set_ta(int val)
306 {
307 assert(val > -127 && val < 128);
308 timing_advance = val * 4;
309 }
310
Eric805e0d92023-05-23 11:29:18 +0200311 void set_name_aff_sched(sched_params::thread_names name)
Ericb7253c62022-11-28 19:21:08 +0100312 {
Eric805e0d92023-05-23 11:29:18 +0200313 set_name_aff_sched(pthread_self(), name);
Ericb7253c62022-11-28 19:21:08 +0100314 }
315
Eric805e0d92023-05-23 11:29:18 +0200316 void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
317 {
318 auto tgt = schdp[hw_target][name];
319 // std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
320 set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
321 }
322
323 private:
Ericb7253c62022-11-28 19:21:08 +0100324 void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
325 int prio)
326 {
327 pthread_setname_np(h, name);
328
329 cpu_set_t cpuset;
330
331 CPU_ZERO(&cpuset);
332 CPU_SET(cpunum, &cpuset);
333
Eric805e0d92023-05-23 11:29:18 +0200334 if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
Ericb7253c62022-11-28 19:21:08 +0100335 std::cerr << name << " affinity: errreur! " << std::strerror(errno);
336 return exit(0);
337 }
338
339 sched_param sch_params;
340 sch_params.sched_priority = prio;
Eric805e0d92023-05-23 11:29:18 +0200341 if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
Ericb7253c62022-11-28 19:21:08 +0100342 std::cerr << name << " sched: errreur! " << std::strerror(errno);
343 return exit(0);
344 }
345 }
346};