/* * (C) 2022 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Eric Wild * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "ms.h" #include "sigProcLib.h" #include "signalVector.h" #include "grgsm_vitac/grgsm_vitac.h" extern "C" { #include "sch.h" } #if !defined(SYNCTHINGONLY) || !defined(NODAMNLOG) #define DBGLG(...) ms_trx::dummy_log() #else #define DBGLG(...) std::cerr #endif #if !defined(SYNCTHINGONLY) #define DBGLG2(...) ms_trx::dummy_log() #else #define DBGLG2(...) std::cerr #endif static bool decode_sch(float *bits, bool update_global_clock) { struct sch_info sch; ubit_t info[GSM_SCH_INFO_LEN]; sbit_t data[GSM_SCH_CODED_LEN]; float_to_sbit(&bits[3], &data[0], 62, 39); float_to_sbit(&bits[106], &data[39], 62, 39); if (!gsm_sch_decode(info, data)) { gsm_sch_parse(info, &sch); DBGLG() << "SCH : Decoded values" << std::endl; DBGLG() << " BSIC: " << sch.bsic << std::endl; DBGLG() << " TSC: " << (sch.bsic & 0x7) << std::endl; DBGLG() << " T1 : " << sch.t1 << std::endl; DBGLG() << " T2 : " << sch.t2 << std::endl; DBGLG() << " T3p : " << sch.t3p << std::endl; DBGLG() << " FN : " << gsm_sch_to_fn(&sch) << std::endl; return true; } return false; } static void check_rcv_fn(GSM::Time t, bool first, unsigned int &lastfn, unsigned int &fnbm) { if (first && t.TN() == 0) { lastfn = t.FN(); fnbm = 1 << 0; first = false; } if (!first && t.FN() != (int)lastfn) { if (fnbm != 255) std::cerr << "rx " << lastfn << ":" << fnbm << " " << __builtin_popcount(fnbm) << std::endl; lastfn = t.FN(); fnbm = 1 << t.TN(); } fnbm |= 1 << t.TN(); } static void handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale) { std::fill(burst.begin(), burst.begin() + burst.size(), 0.0); const auto is_sch = gsm_sch_check_ts(e.gsmts.TN(), e.gsmts.FN()); const auto is_fcch = gsm_fcch_check_ts(e.gsmts.TN(), e.gsmts.FN()); if (is_fcch) return; if (is_sch) { char outbin[148]; convert_and_scale(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2, SAMPLE_SCALE_FACTOR); std::stringstream dbgout; #if 0 { struct estim_burst_params ebp; auto rv2 = detectSCHBurst(burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp); auto bits = demodAnyBurst(burst, SCH, 4, &ebp); // clamp_array(bits->begin(), 148, 1.5f); for (auto &i : *bits) i = (i > 0 ? 1 : -1); auto rv = decode_sch(bits->begin(), false); dbgout << "U DET@" << (rv2 ? "yes " : " ") << "Timing offset " << ebp.toa << " symbols, DECODE: " << (rv ? "yes" : "---") << " "; delete bits; } #endif { convert_and_scale(burst.begin(), burst.begin(), ONE_TS_BURST_LEN * 2, 1.f / float(scale)); std::complex channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; auto ss = reinterpret_cast *>(burst.begin()); int d_c0_burst_start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]); detect_burst(ss, &channel_imp_resp[0], d_c0_burst_start, outbin); SoftVector bits; bits.resize(148); for (int i = 0; i < 148; i++) { bits[i] = (!outbin[i]); // < 1 ? -1 : 1; } auto rv = decode_sch(bits.begin(), false); dbgout << "U SCH@" << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << d_c0_burst_start << " DECODE:" << (rv ? "yes" : "---") << std::endl; } DBGLG() << dbgout.str(); return; } #if 1 convert_and_scale(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(scale)); // std::cerr << "@" << tsc << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << " " // << std::endl; char outbin[148]; auto ss = reinterpret_cast *>(burst.begin()); float ncmax, dcmax; std::complex chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR], chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR]; auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, tsc); auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY); auto is_nb = ncmax > dcmax; DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start << " o db: " << dummy_burst_start << std::endl; if (is_nb) detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin); else detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin); ; #ifdef DBGXX // auto bits = SoftVector(148); // for (int i = 0; i < 148; i++) // (bits)[i] = outbin[i] < 1 ? -1 : 1; #endif #endif } void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale) { static bool first = true; unsigned int lastfn = 0; unsigned int fnbm = 0; signalVector burst(ONE_TS_BURST_LEN, 100, 100); cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(1, &cpuset); auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); if (rv < 0) { std::cerr << "affinity: errreur! " << std::strerror(errno); exit(0); } int prio = sched_get_priority_max(SCHED_RR); struct sched_param param; param.sched_priority = prio; rv = sched_setscheduler(0, SCHED_RR, ¶m); if (rv < 0) { std::cerr << "scheduler: errreur! " << std::strerror(errno); exit(0); } while (1) { one_burst e; while (!q->spsc_pop(&e)) { q->spsc_prep_pop(); } check_rcv_fn(e.gsmts, first, lastfn, fnbm); handle_it(e, burst, *tsc, scale); #ifdef DBGXX rv = detectSCHBurst(*burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp); if (rv > 0) std::cerr << "#" << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << std::endl; sched_yield(); #endif } }