blob: 6f63a73788ec99cf44db617821d1c4039886acfc [file] [log] [blame]
Ericb7253c62022-11-28 19:21:08 +01001
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#include "GSMCommon.h"
23#include <atomic>
24#include <cassert>
25#include <complex>
26#include <iostream>
27#include <cstdlib>
28#include <cstdio>
29#include <thread>
30#include <fstream>
31
32#include "sigProcLib.h"
33
34#include "ms.h"
35#include "ms_rx_burst.h"
36#include "grgsm_vitac/grgsm_vitac.h"
37
38extern "C" {
39#include "sch.h"
40#include "convolve.h"
41#include "convert.h"
42}
43
44dummylog ms_trx::dummy_log;
45
46#ifdef DBGXX
47const int offsetrange = 200;
48const int offset_start = -15;
49static int offset_ctr = 0;
50#endif
51
52template <> std::atomic<bool> ms_trx::base::stop_me_flag(false);
53
54void tx_test(ms_trx *t, ts_hitter_q_t *q, unsigned int *tsc)
55{
56 sched_param sch_params;
57 sch_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
58 pthread_setschedparam(pthread_self(), SCHED_FIFO, &sch_params);
59
60 auto burst = genRandAccessBurst(0, 4, 0);
61 scaleVector(*burst, t->txFullScale * 0.7);
62
63 // float -> int16
64 blade_sample_type burst_buf[burst->size()];
Eric Wild621a49e2023-01-12 16:21:58 +010065 convert_and_scale(burst_buf, burst->begin(), burst->size() * 2, 1);
Ericb7253c62022-11-28 19:21:08 +010066
67 while (1) {
68 GSM::Time target;
69 while (!q->spsc_pop(&target)) {
70 q->spsc_prep_pop();
71 }
72
73 std::cerr << std::endl << "\x1B[32m hitting " << target.FN() << "\033[0m" << std::endl;
74
75 int timing_advance = 0;
76 int64_t now_ts;
77 GSM::Time now_time;
78 target.incTN(3); // ul dl offset
79 int target_fn = target.FN();
80 int target_tn = target.TN();
81 t->timekeeper.get_both(&now_time, &now_ts);
82
83 auto diff_fn = GSM::FNDelta(target_fn, now_time.FN());
84 int diff_tn = (target_tn - (int)now_time.TN()) % 8;
85 auto tosend = GSM::Time(diff_fn, 0);
86
87 if (diff_tn > 0)
88 tosend.incTN(diff_tn);
89 else if (diff_tn < 0)
90 tosend.decTN(-diff_tn);
91
92 // in thory fn equal and tn+3 equal is also a problem...
93 if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
94 std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
95 << " tn OTHER: " << target_tn << std::endl;
96 return;
97 }
98
99 auto check = now_time + tosend;
100 int64_t send_ts =
101 now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
102
103#ifdef DBGXX
104 std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL: " << now_time.TN()
105 << " tn OTHER: " << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":"
106 << tosend.TN() << " calc: " << check.FN() << ":" << check.TN() << " target: " << target.FN()
107 << ":" << target.TN() << " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
108#endif
109
110 unsigned int pad = 4 * 25;
111 blade_sample_type buf2[burst->size() + pad];
112 std::fill(buf2, buf2 + pad, 0);
113
114 memcpy(&buf2[pad], burst_buf, burst->size() * sizeof(blade_sample_type));
115
116 assert(target.FN() == check.FN());
117 assert(target.TN() == check.TN());
118 assert(target.FN() % 51 == 21);
119#ifdef DBGXX
120 auto this_offset = offset_start + (offset_ctr++ % offsetrange);
121 std::cerr << "-- O " << this_offset << std::endl;
122 send_ts = now_ts - timing_advance +
123 ((target.FN() * 8 + (int)target.TN()) - (now_time.FN() * 8 + (int)now_time.TN())) *
124 ONE_TS_BURST_LEN;
125#endif
126 t->submit_burst_ts(buf2, burst->size() + pad, send_ts - pad);
127#ifdef DBGXX
128 signalVector test(burst->size() + pad);
Eric Wild621a49e2023-01-12 16:21:58 +0100129 convert_and_scale(test.begin(), buf2, burst->size() * 2 + pad, 1.f / float(scale));
Ericb7253c62022-11-28 19:21:08 +0100130 estim_burst_params ebp;
131 auto det = detectAnyBurst(test, 0, 4, 4, CorrType::RACH, 40, &ebp);
132 if (det > 0)
133 std::cerr << "## Y " << ebp.toa << std::endl;
134 else
135 std::cerr << "## NOOOOOOOOO " << ebp.toa << std::endl;
136#endif
137 }
138}
139#ifdef SYNCTHINGONLY
140template <typename A> auto parsec(std::vector<std::string> &v, A &itr, std::string arg, bool *rv)
141{
142 if (*itr == arg) {
143 *rv = true;
144 return true;
145 }
146 return false;
147}
148
149template <typename A, typename B, typename C>
150bool parsec(std::vector<std::string> &v, A &itr, std::string arg, B f, C *rv)
151{
152 if (*itr == arg) {
153 itr++;
154 if (itr != v.end()) {
155 *rv = f(itr->c_str());
156 return true;
157 }
158 }
159 return false;
160}
161template <typename A> bool parsec(std::vector<std::string> &v, A &itr, std::string arg, int scale, int *rv)
162{
163 return parsec(
164 v, itr, arg, [scale](const char *v) -> auto{ return atoi(v) * scale; }, rv);
165}
166template <typename A> bool parsec(std::vector<std::string> &v, A &itr, std::string arg, int scale, unsigned int *rv)
167{
168 return parsec(
169 v, itr, arg, [scale](const char *v) -> auto{ return atoi(v) * scale; }, rv);
170}
171
172int main(int argc, char *argv[])
173{
174 cpu_set_t cpuset;
175
176 CPU_ZERO(&cpuset);
177 CPU_SET(2, &cpuset);
178
179 auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
180 if (rv < 0) {
181 std::cerr << "affinity: errreur! " << std::strerror(errno);
182 return 0;
183 }
184
185 unsigned int default_tx_freq(881000 * 1000), default_rx_freq(926000 * 1000);
186 unsigned int grx = 20, gtx = 20;
187 bool tx_flag = false;
188 pthread_setname_np(pthread_self(), "main");
189 convolve_init();
190 convert_init();
191 sigProcLibSetup();
192 initvita();
193
194 int status = 0;
195 auto trx = new ms_trx();
196 trx->do_auto_gain = true;
197
198 std::vector<std::string> args(argv + 1, argv + argc);
199 for (auto i = args.begin(); i != args.end(); ++i) {
200 parsec(args, i, "-r", 1000, &default_rx_freq);
201 parsec(args, i, "-t", 1000, &default_tx_freq);
202 parsec(args, i, "-gr", 1, &grx);
203 parsec(args, i, "-gt", 1, &gtx);
204 parsec(args, i, "-tx", &tx_flag);
205 }
206
207 std::cerr << "usage: " << argv[0] << " <rxfreq in khz, i.e. 926000> [txfreq in khz, i.e. 881000] [TX]"
208 << std::endl
209 << "rx" << (argc == 1 ? " (default) " : " ") << default_rx_freq << "hz, tx " << default_tx_freq
210 << "hz" << std::endl
211 << "gain rx " << grx << " gain tx " << gtx << std::endl
212 << (tx_flag ? "##!!## RACH TX ACTIVE ##!!##" : "-- no rach tx --") << std::endl;
213
214 status = trx->init_dev_and_streams();
215 if (status < 0)
216 return status;
217 trx->tuneRx(default_rx_freq);
218 trx->tuneTx(default_tx_freq);
219 trx->setRxGain(grx);
220 trx->setTxGain(gtx);
221
222 if (status == 0) {
223 // FIXME: hacks! needs exit flag for detached threads!
224
225 std::thread(rcv_bursts_test, &trx->rxqueue, &trx->mTSC, trx->rxFullScale).detach();
226 if (tx_flag)
227 std::thread(tx_test, trx, &trx->ts_hitter_q, &trx->mTSC).detach();
228 trx->start();
229 do {
230 sleep(1);
231 } while (1);
232
233 trx->stop_threads();
234 }
235 delete trx;
236
237 return status;
238}
239#endif
240
241int ms_trx::init_dev_and_streams()
242{
243 int status = 0;
244 status = base::init_device(rx_bh(), tx_bh());
245 if (status < 0) {
246 std::cerr << "failed to init dev!" << std::endl;
247 return -1;
248 }
249 return status;
250}
251
252bh_fn_t ms_trx::rx_bh()
253{
254 return [this](dev_buf_t *rcd) -> int {
255 if (this->search_for_sch(rcd) == SCH_STATE::FOUND)
256 this->grab_bursts(rcd);
257 return 0;
258 };
259}
260
261bh_fn_t ms_trx::tx_bh()
262{
263 return [this](dev_buf_t *rcd) -> int {
264#pragma GCC diagnostic push
265#pragma GCC diagnostic ignored "-Wunused-variable"
266 auto y = this;
267#pragma GCC diagnostic pop
268 /* nothing to do here */
269 return 0;
270 };
271}
272
273void ms_trx::start()
274{
275 auto fn = get_rx_burst_handler_fn(rx_bh());
276 rx_task = std::thread(fn);
277 set_name_aff_sched(rx_task.native_handle(), "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2);
278
279 usleep(1000);
280 auto fn2 = get_tx_burst_handler_fn(tx_bh());
281 tx_task = std::thread(fn2);
282 set_name_aff_sched(tx_task.native_handle(), "txrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
283}
284
285void ms_trx::set_upper_ready(bool is_ready)
286{
287 upper_is_ready = is_ready;
288}
289
290void ms_trx::stop_threads()
291{
292 std::cerr << "killing threads...\r\n" << std::endl;
293 stop_me_flag = true;
294 close_device();
295 rx_task.join();
296 tx_task.join();
297}
298
299void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
300{
301 int64_t now_ts;
302 GSM::Time now_time;
303 target.incTN(3); // ul dl offset
304 int target_fn = target.FN();
305 int target_tn = target.TN();
306 timekeeper.get_both(&now_time, &now_ts);
307
308 auto diff_fn = GSM::FNDelta(target_fn, now_time.FN());
309 int diff_tn = (target_tn - (int)now_time.TN()) % 8;
310 auto tosend = GSM::Time(diff_fn, 0);
311
312 if (diff_tn > 0)
313 tosend.incTN(diff_tn);
314 else
315 tosend.decTN(-diff_tn);
316
317 // in theory fn equal and tn+3 equal is also a problem...
318 if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
319 std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
320 << " tn OTHER: " << target_tn << std::endl;
321 return;
322 }
323
324 auto check = now_time + tosend;
325 int64_t send_ts = now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
326#ifdef DBGXX
327 std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL/OTHER: " << now_time.TN()
328 << "/" << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":" << tosend.TN()
329 << " check: " << check.FN() << ":" << check.TN() << " target: " << target.FN() << ":" << target.TN()
330 << " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
331#endif
332#if 1
333 unsigned int pad = 4 * 4;
334 blade_sample_type buf2[len + pad];
335 std::fill(buf2, buf2 + pad, 0);
336 memcpy(&buf2[pad], buffer, len * sizeof(blade_sample_type));
337
338 assert(target.FN() == check.FN());
339 assert(target.TN() == check.TN());
340 submit_burst_ts(buf2, len + pad, send_ts - pad);
341#else
342 submit_burst_ts(buffer, len, send_ts);
343#endif
344}