blob: 8b781a3f567c46a94abd6ff63da9d7d6792ca711 [file] [log] [blame]
Erica0e1ed32022-11-28 19:21:08 +01001#pragma once
2
3/*
4 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
5 * All Rights Reserved
6 *
7 * Author: Eric Wild <ewild@sysmocom.de>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <atomic>
25#include <cassert>
26#include <complex>
27#include <cstdint>
28#include <mutex>
29#include <iostream>
30#include <thread>
31
32#if defined(BUILDBLADE)
33#include "bladerf_specific.h"
34#define BASET blade_hw<ms_trx>
35#elif defined(BUILDUHD)
36#include "uhd_specific.h"
37#define BASET uhd_hw<ms_trx>
38#elif defined(BUILDIPC)
39#include "ipc_specific.h"
40#define BASET ipc_hw<ms_trx>
41#else
42#error wat? no device..
43#endif
44
45#include "GSMCommon.h"
46#include "itrq.h"
47
48const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
49const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
50const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
51
52template <typename T> void clamp_array(T *start2, unsigned int len, T max)
53{
54 for (int i = 0; i < len; i++) {
55 const T t1 = start2[i] < -max ? -max : start2[i];
56 const T t2 = t1 > max ? max : t1;
57 start2[i] = t2;
58 }
59}
60template <typename DST_T, typename SRC_T, typename ST>
61void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
62{
63 for (unsigned int i = 0; i < src_len; i++)
64 reinterpret_cast<DST_T *>(dst)[i] = static_cast<DST_T>((reinterpret_cast<SRC_T *>(src)[i])) * scale;
65}
66template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
67{
68 return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
69}
70
71struct one_burst {
72 one_burst()
73 {
74 }
75 GSM::Time gsmts;
76 union {
77 blade_sample_type burst[ONE_TS_BURST_LEN];
78 char sch_bits[148];
79 };
80};
81
82using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
83
84enum class SCH_STATE { SEARCHING, FOUND };
85
86class dummylog : private std::streambuf {
87 std::ostream null_stream;
88
89 public:
90 dummylog() : null_stream(this){};
91 ~dummylog() override{};
92 std::ostream &operator()()
93 {
94 return null_stream;
95 }
96 int overflow(int c) override
97 {
98 return c;
99 }
100};
101
102// keeps relationship between gsm time and (continuously adjusted) ts
103class time_keeper {
104 GSM::Time global_time_keeper;
105 int64_t global_ts_keeper;
106 std::mutex m;
107
108 public:
109 time_keeper() : global_time_keeper(0), global_ts_keeper(0)
110 {
111 }
112
113 void set(GSM::Time t, int64_t ts)
114 {
115 std::lock_guard<std::mutex> g(m);
116 global_time_keeper = t;
117 global_ts_keeper = ts;
118 }
119 void inc_both()
120 {
121 std::lock_guard<std::mutex> g(m);
122 global_time_keeper.incTN(1);
123 global_ts_keeper += ONE_TS_BURST_LEN;
124 }
125 void inc_and_update(int64_t new_ts)
126 {
127 std::lock_guard<std::mutex> g(m);
128 global_time_keeper.incTN(1);
129 global_ts_keeper = new_ts;
130 // std::cerr << "u " << new_ts << std::endl;
131 }
132 void inc_and_update_safe(int64_t new_ts)
133 {
134 std::lock_guard<std::mutex> g(m);
135 auto diff = new_ts - global_ts_keeper;
136 assert(diff < 1.5 * ONE_TS_BURST_LEN);
137 assert(diff > 0.5 * ONE_TS_BURST_LEN);
138 global_time_keeper.incTN(1);
139 global_ts_keeper = new_ts;
140 // std::cerr << "s " << new_ts << std::endl;
141 }
142 void dec_by_one()
143 {
144 std::lock_guard<std::mutex> g(m);
145 global_time_keeper.decTN(1);
146 global_ts_keeper -= ONE_TS_BURST_LEN;
147 }
148 auto get_ts()
149 {
150 std::lock_guard<std::mutex> g(m);
151 return global_ts_keeper;
152 }
153 auto gsmtime()
154 {
155 std::lock_guard<std::mutex> g(m);
156 return global_time_keeper;
157 }
158 void get_both(GSM::Time *t, int64_t *ts)
159 {
160 std::lock_guard<std::mutex> g(m);
161 *t = global_time_keeper;
162 *ts = global_ts_keeper;
163 }
164};
165
166using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
167
168struct ms_trx : public BASET {
169 using base = BASET;
170 static dummylog dummy_log;
171 unsigned int mTSC;
172 unsigned int mBSIC;
173 int timing_advance;
174 bool do_auto_gain;
175
176 std::thread rx_task;
177 std::thread tx_task;
178 std::thread *calcrval_task;
179
180 // provides bursts to upper rx thread
181 rx_queue_t rxqueue;
182#ifdef SYNCTHINGONLY
183 ts_hitter_q_t ts_hitter_q;
184#endif
185 blade_sample_type *first_sch_buf;
186 blade_sample_type *burst_copy_buffer;
187
188 uint64_t first_sch_buf_rcv_ts;
189 std::atomic<bool> rcv_done;
190 std::atomic<bool> sch_thread_done;
191
192 int64_t temp_ts_corr_offset = 0;
193 int64_t first_sch_ts_start = -1;
194
195 time_keeper timekeeper;
196
197 void start();
198 std::atomic<bool> upper_is_ready;
199 void set_upper_ready(bool is_ready);
200
201 bool handle_sch_or_nb();
202 bool handle_sch(bool first = false);
203 bool decode_sch(float *bits, bool update_global_clock);
204 SCH_STATE search_for_sch(dev_buf_t *rcd);
205 void grab_bursts(dev_buf_t *rcd) __attribute__((optnone));
206
207 int init_device();
208 int init_dev_and_streams();
209 void stop_threads();
210 void *rx_cb(ms_trx *t);
211 void *tx_cb();
212 void maybe_update_gain(one_burst &brst);
213
214 ms_trx()
215 : timing_advance(0), do_auto_gain(false), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
216 burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), rcv_done{ false }, sch_thread_done{ false }
217 {
218 }
219
220 virtual ~ms_trx()
221 {
222 delete[] burst_copy_buffer;
223 delete[] first_sch_buf;
224 }
225 bh_fn_t rx_bh();
226 bh_fn_t tx_bh();
227
228 void submit_burst(blade_sample_type *buffer, int len, GSM::Time);
229 void set_ta(int val)
230 {
231 assert(val > -127 && val < 128);
232 timing_advance = val * 4;
233 }
234
235 void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
236 {
237 set_name_aff_sched(pthread_self(), name, cpunum, schedtype, prio);
238 }
239
240 void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
241 int prio)
242 {
243 pthread_setname_np(h, name);
244
245 cpu_set_t cpuset;
246
247 CPU_ZERO(&cpuset);
248 CPU_SET(cpunum, &cpuset);
249
250 auto rv = pthread_setaffinity_np(h, sizeof(cpuset), &cpuset);
251 if (rv < 0) {
252 std::cerr << name << " affinity: errreur! " << std::strerror(errno);
253 return exit(0);
254 }
255
256 sched_param sch_params;
257 sch_params.sched_priority = prio;
258 rv = pthread_setschedparam(h, schedtype, &sch_params);
259 if (rv < 0) {
260 std::cerr << name << " sched: errreur! " << std::strerror(errno);
261 return exit(0);
262 }
263 }
264};