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