blob: 5425283672a5f3c9127c86bc0ad901b472cc48b6 [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
41#include "GSMCommon.h"
42#include "itrq.h"
43
44const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
45const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
46const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
47
48template <typename T> void clamp_array(T *start2, unsigned int len, T max)
49{
50 for (int i = 0; i < len; i++) {
51 const T t1 = start2[i] < -max ? -max : start2[i];
52 const T t2 = t1 > max ? max : t1;
53 start2[i] = t2;
54 }
55}
56template <typename DST_T, typename SRC_T, typename ST>
57void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
58{
59 for (unsigned int i = 0; i < src_len; i++)
60 reinterpret_cast<DST_T *>(dst)[i] = static_cast<DST_T>((reinterpret_cast<SRC_T *>(src)[i])) * scale;
61}
62template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
63{
64 return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
65}
66
67struct one_burst {
68 one_burst()
69 {
70 }
71 GSM::Time gsmts;
72 union {
73 blade_sample_type burst[ONE_TS_BURST_LEN];
74 char sch_bits[148];
75 };
76};
77
78using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
79
80enum class SCH_STATE { SEARCHING, FOUND };
81
82class dummylog : private std::streambuf {
83 std::ostream null_stream;
84
85 public:
86 dummylog() : null_stream(this){};
87 ~dummylog() override{};
88 std::ostream &operator()()
89 {
90 return null_stream;
91 }
92 int overflow(int c) override
93 {
94 return c;
95 }
96};
97
98// keeps relationship between gsm time and (continuously adjusted) ts
99class time_keeper {
100 GSM::Time global_time_keeper;
101 int64_t global_ts_keeper;
102 std::mutex m;
103
104 public:
105 time_keeper() : global_time_keeper(0), global_ts_keeper(0)
106 {
107 }
108
109 void set(GSM::Time t, int64_t ts)
110 {
111 std::lock_guard<std::mutex> g(m);
112 global_time_keeper = t;
113 global_ts_keeper = ts;
114 }
115 void inc_both()
116 {
117 std::lock_guard<std::mutex> g(m);
118 global_time_keeper.incTN(1);
119 global_ts_keeper += ONE_TS_BURST_LEN;
120 }
121 void inc_and_update(int64_t new_ts)
122 {
123 std::lock_guard<std::mutex> g(m);
124 global_time_keeper.incTN(1);
125 global_ts_keeper = new_ts;
126 // std::cerr << "u " << new_ts << std::endl;
127 }
128 void inc_and_update_safe(int64_t new_ts)
129 {
130 std::lock_guard<std::mutex> g(m);
131 auto diff = new_ts - global_ts_keeper;
132 assert(diff < 1.5 * ONE_TS_BURST_LEN);
133 assert(diff > 0.5 * ONE_TS_BURST_LEN);
134 global_time_keeper.incTN(1);
135 global_ts_keeper = new_ts;
136 // std::cerr << "s " << new_ts << std::endl;
137 }
138 void dec_by_one()
139 {
140 std::lock_guard<std::mutex> g(m);
141 global_time_keeper.decTN(1);
142 global_ts_keeper -= ONE_TS_BURST_LEN;
143 }
144 auto get_ts()
145 {
146 std::lock_guard<std::mutex> g(m);
147 return global_ts_keeper;
148 }
149 auto gsmtime()
150 {
151 std::lock_guard<std::mutex> g(m);
152 return global_time_keeper;
153 }
154 void get_both(GSM::Time *t, int64_t *ts)
155 {
156 std::lock_guard<std::mutex> g(m);
157 *t = global_time_keeper;
158 *ts = global_ts_keeper;
159 }
160};
161
162using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
163
164struct ms_trx : public BASET {
165 using base = BASET;
166 static dummylog dummy_log;
167 unsigned int mTSC;
168 unsigned int mBSIC;
169 int timing_advance;
170 bool do_auto_gain;
171
172 std::thread rx_task;
173 std::thread tx_task;
174 std::thread *calcrval_task;
175
176 // provides bursts to upper rx thread
177 rx_queue_t rxqueue;
178#ifdef SYNCTHINGONLY
179 ts_hitter_q_t ts_hitter_q;
180#endif
181 blade_sample_type *first_sch_buf;
182 blade_sample_type *burst_copy_buffer;
183
184 uint64_t first_sch_buf_rcv_ts;
185 std::atomic<bool> rcv_done;
186 std::atomic<bool> sch_thread_done;
187
188 int64_t temp_ts_corr_offset = 0;
189 int64_t first_sch_ts_start = -1;
190
191 time_keeper timekeeper;
192
193 void start();
194 std::atomic<bool> upper_is_ready;
195 void set_upper_ready(bool is_ready);
196
197 bool handle_sch_or_nb();
198 bool handle_sch(bool first = false);
199 bool decode_sch(float *bits, bool update_global_clock);
200 SCH_STATE search_for_sch(dev_buf_t *rcd);
201 void grab_bursts(dev_buf_t *rcd);
202
203 int init_device();
204 int init_dev_and_streams();
205 void stop_threads();
206 void *rx_cb(ms_trx *t);
207 void *tx_cb();
208 void maybe_update_gain(one_burst &brst);
209
210 ms_trx()
211 : timing_advance(0), do_auto_gain(false), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
212 burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), rcv_done{ false }, sch_thread_done{ false }
213 {
214 }
215
216 virtual ~ms_trx()
217 {
218 delete[] burst_copy_buffer;
219 delete[] first_sch_buf;
220 }
221 bh_fn_t rx_bh();
222 bh_fn_t tx_bh();
223
224 void submit_burst(blade_sample_type *buffer, int len, GSM::Time);
225 void set_ta(int val)
226 {
227 assert(val > -127 && val < 128);
228 timing_advance = val * 4;
229 }
230
231 void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
232 {
233 set_name_aff_sched(pthread_self(), name, cpunum, schedtype, prio);
234 }
235
236 void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
237 int prio)
238 {
239 pthread_setname_np(h, name);
240
241 cpu_set_t cpuset;
242
243 CPU_ZERO(&cpuset);
244 CPU_SET(cpunum, &cpuset);
245
246 auto rv = pthread_setaffinity_np(h, sizeof(cpuset), &cpuset);
247 if (rv < 0) {
248 std::cerr << name << " affinity: errreur! " << std::strerror(errno);
249 return exit(0);
250 }
251
252 sched_param sch_params;
253 sch_params.sched_priority = prio;
254 rv = pthread_setschedparam(h, schedtype, &sch_params);
255 if (rv < 0) {
256 std::cerr << name << " sched: errreur! " << std::strerror(errno);
257 return exit(0);
258 }
259 }
260};