/* * (C) 2023 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 . * */ // this allows messing with the demod to check the detecton offset impact, // not intended for actual automated tests. #include "sigProcLib.h" extern "C" { #include "convert.h" #include } #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #define DO_RACH const int SAMPLE_SCALE_FACTOR = 1; template void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale) { for (unsigned int i = 0; i < src_len; i++) reinterpret_cast(dst)[i] = static_cast((reinterpret_cast(src)[i]) * scale); } template void convert_and_scale_default(void *dst, void *src, unsigned int src_len) { return convert_and_scale(dst, src, src_len, SAMPLE_SCALE_FACTOR); } static const unsigned int txFullScale = (float)(1 << 14) - 1; // static const unsigned int rxFullScale = (float)(1 << 14) - 1; static const BitVector gRACHBurstx("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000"); static const BitVector gTrainingSequencex[] = { BitVector("00100101110000100010010111"), BitVector("00101101110111100010110111"), BitVector("01000011101110100100001110"), BitVector("01000111101101000100011110"), BitVector("00011010111001000001101011"), BitVector("01001110101100000100111010"), BitVector("10100111110110001010011111"), BitVector("11101111000100101110111100"), }; struct mrv { std::vector bits; signalVector *rvbuf; std::unique_ptr>> convolved; // mrv(): bits(), demod_bits() {} CorrType ct; }; static mrv genRandNormalBurstx(int tsc, int sps, int tn) { mrv retstruct; int i = 0; BitVector bits(148); /* Tail bits */ for (; i < 3; i++) bits[i] = 0; /* Random bits */ for (int j = 0; i < 60; i++, j++) bits[i] = rand() % 2; /* Stealing bit */ bits[i++] = 0; /* Training sequence */ for (int n = 0; i < 87; i++, n++) bits[i] = gTrainingSequencex[tsc][n]; /* Stealing bit */ bits[i++] = 0; /* Random bits */ for (; i < 145; i++) bits[i] = rand() % 2; /* Tail bits */ for (; i < 148; i++) bits[i] = 0; int guard = 8 + !(tn % 4); auto r = modulateBurst(bits, guard, sps); retstruct.rvbuf = r; for (size_t i = 0; i < bits.size(); i++) retstruct.bits.push_back(bits.bit(i) ? 1 : 0); return retstruct; } static mrv genRandAccessBurstx(int delay, int sps, int tn) { mrv retstruct; int i = 0; BitVector bits(88 + delay); /* delay */ for (; i < delay; i++) bits[i] = 0; /* head and synch bits */ for (int n = 0; i < 49 + delay; i++, n++) bits[i] = gRACHBurstx[n]; /* Random bits */ for (int j = 0; i < 85 + delay; i++, j++) bits[i] = rand() % 2; for (; i < 88 + delay; i++) bits[i] = 0; int guard = 68 - delay + !(tn % 4); auto r = modulateBurst(bits, guard, sps); retstruct.rvbuf = r; for (size_t i = 0; i < bits.size(); i++) retstruct.bits.push_back(bits.bit(i) ? 1 : 0); return retstruct; } extern gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///begin(); auto n = burst->bytes(); char *data = reinterpret_cast(start); const int len_in_real = burst->size() * 2; auto cvrtbuf_tx_a = new int16_t[len_in_real]; convert_float_short(cvrtbuf_tx_a, (float *)burst->begin(), float(txFullScale), len_in_real); std::ofstream fout; fout.open(fname + ".cfile", std::ios::binary | std::ios::out); fout.write(data, n); fout.close(); fout.open(fname + ".cs16", std::ios::binary | std::ios::out); fout.write((char *)cvrtbuf_tx_a, len_in_real * sizeof(uint16_t)); fout.close(); delete[] cvrtbuf_tx_a; } // borrowed from a real world burst.. static std::vector> chan_im_resp = { { 4.1588e-05 + -0.000361925 }, { 0.000112728 + -0.000289796 }, { 0.000162952 + -0.000169028 }, { 0.000174185 + -2.54575e-05 }, { 0.000142947 + 0.000105992 }, { 8.65919e-05 + 0.000187041 }, { 4.15799e-05 + 0.000184346 }, { 5.30207e-05 + 7.84921e-05 }, { 0.000158877 + -0.000128058 }, { 0.000373956 + -0.000407954 }, { 0.000680606 + -0.000712065 }, { 0.00102929 + -0.000979604 }, { 0.00135049 + -0.00115333 }, { 0.00157434 + -0.0011948 }, { 0.00165098 + -0.00109534 }, { 0.00156519 + -0.000878794 }, { 0.0013399 + -0.000594285 }, { 0.00102788 + -0.00030189 }, { 0.000694684 + -5.58912e-05 }, { 0.000399328 + 0.000109463 } }; // as above, downsampled to 1sps + just magnitude static std::vector chan_im_resp_trunc = { 1., 0.20513351, 0.10020305, 0.11490235 }; template auto conv(const std::vector &a, const std::vector &b) -> std::unique_ptr> { int data_len = a.size(); int conv_len = b.size(); int conv_size = conv_len + data_len - 1; auto retv = std::make_unique>(conv_size); for (int i = 0; i < data_len; ++i) { for (int j = 0; j < conv_len; ++j) { (*retv)[i + j] += a[i] * b[j]; } } return retv; } template static auto conv(const A *a, int len, std::vector &b) { std::vector aa(len); std::copy_n(a, len, aa.begin()); std::reverse(b.begin(), b.end()); return conv(aa, b); } template static auto conv(const A *a, int len, std::vector &b) { std::vector aa(len); std::copy_n(a, len, aa.begin()); std::reverse(b.begin(), b.end()); return conv(aa, b); } // signalvector is owning despite claiming not to, but we can pretend, too.. static void dummy_free(void *wData){}; static void *dummy_alloc(size_t newSize) { return 0; }; template size_t read_from_file(std::string path, std::vector &outvec) { std::ifstream infile; infile.open(path, std::ios::in | std::ios::binary); if (infile.fail()) { std::cout << " not found: " << path << std::endl; exit(0); } infile.seekg(0, std::ios_base::end); size_t fsize = infile.tellg(); auto fsize_in_T = fsize / sizeof(T); infile.seekg(0, std::ios_base::beg); outvec.resize(fsize_in_T); infile.read(reinterpret_cast(&outvec[0]), fsize); infile.close(); std::cout << "Read " << fsize << " from " << path << std::endl; return fsize; } void demod_real_burst(int num = 0) { auto path = "./nb_chunk_tsc7.cfile"; auto bitfile = "./demodbits_tsc7.s8"; std::vector> burstdata; std::vector bitdata; read_from_file(path, burstdata); read_from_file(bitfile, bitdata); // print "known good" burst bits std::cerr << "known bits:" << std::endl; std::cerr << std::setw(5) << 0 << " - "; for (auto i : bitdata) std::cout << (i > 0 ? "1" : "0"); std::cerr << std::endl; std::cerr << "demod tests sigproclib:" << std::endl; auto ct = CorrType::TSC; auto delay = 0; auto tsc = 7; int offset = 0; auto cplx = reinterpret_cast(&burstdata[offset]); auto stdcplx = reinterpret_cast *>(&burstdata[offset]); signalVector sv(&cplx[0], 0, burstdata.size() - offset, dummy_alloc, dummy_free); struct estim_burst_params ebp; auto rc = detectAnyBurst(sv, tsc, BURST_THRESH, 4, ct, 40, &ebp); auto rxBurst = std::unique_ptr(demodAnyBurst(sv, (CorrType)rc, 4, &ebp)); // print osmotrx sigproclib demod result std::cerr << std::setw(5) << int(ebp.toa) << " o "; for (ssize_t i = 0 + delay; i < 148 + delay; i++) std::cout << (rxBurst->bit(i) ? "1" : "0"); std::cerr << std::endl; std::cerr << "demod test va:" << std::endl; std::complex chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; float ncmax; char demodded_softbits[444]; // demod at known offset { auto inp = &stdcplx[29]; // known offset auto normal_burst_startX = get_norm_chan_imp_resp(inp, &chan_imp_resp[0], &ncmax, tsc); detect_burst_nb(inp, &chan_imp_resp[0], normal_burst_startX, demodded_softbits); std::cerr << std::setw(5) << normal_burst_startX << " v "; for (size_t i = 0; i < 148; i++) std::cerr << (demodded_softbits[i] < 0 ? "1" : "0"); std::cerr << std::endl; } { std::cerr << "-- va start offset loop --" << std::endl; std::cerr << "offset/det offset/#errors/known^demod bits" << std::endl; for (int i = 0; i < 34; i++) { auto inp = &stdcplx[i]; auto conved_beg = inp; auto me = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc); detect_burst_nb(conved_beg, &chan_imp_resp[0], me, demodded_softbits); auto bitdiffarr = std::make_unique(148); for (size_t i = 0; i < 148; i++) bitdiffarr.get()[i] = (demodded_softbits[i] < 0 ? 1 : 0) ^ (bitdata[i] > 0 ? 1 : 0); auto ber = std::accumulate(bitdiffarr.get(), bitdiffarr.get() + 148, 0); std::cerr << std::setw(3) << i << ": " << std::setw(3) << me << " v " << std::setw(3) << ber << " "; for (size_t i = 0; i < 148; i++) std::cerr << (bitdiffarr[i] ? "1" : "0"); std::cerr << std::endl; // std::cerr << std::setw(4) << i << " (" << std::setw(4) << 29 - i << "):" << std::setw(4) << org // << " " << std::setw(4) << me << " y " << std::endl; } } } auto gen_burst(CorrType t, int delay, int tsc) { mrv rs; if (t == CorrType::RACH) { rs = genRandAccessBurstx(delay, 4, tsc); } else if (t == CorrType::TSC) { rs = genRandNormalBurstx(tsc, 4, 0); } else { std::cerr << "wtf?" << std::endl; exit(0); } rs.ct = t; signalVector *burst = rs.rvbuf; // sv_write_helper(burst, std::to_string(num)); // scaleVector(*burst, {1, 0}); const int len_in_real = burst->size() * 2; auto cvrtbuf_tx_a = std::make_unique(len_in_real); auto cvrtbuf_rx_a = std::make_unique(len_in_real); auto rx_cfloat = reinterpret_cast *>(&cvrtbuf_rx_a[0]); convert_float_short(cvrtbuf_tx_a.get(), (float *)burst->begin(), float(txFullScale), len_in_real); convert_short_float(cvrtbuf_rx_a.get(), cvrtbuf_tx_a.get(), len_in_real); for (int i = 0; i < len_in_real; i++) // scale properly! cvrtbuf_rx_a[i] *= 1. / txFullScale; auto conved = conv(rx_cfloat, burst->size(), chan_im_resp); std::cerr << "-- generated " << (t == CorrType::RACH ? "RACH" : "TSC") << " burst --" << std::endl; for (size_t i = 0; i < rs.bits.size(); i++) std::cerr << (rs.bits[i] ? "1" : "0"); std::cerr << std::endl; delete burst; rs.convolved = std::move(conved); return rs; } void demod_generated_burst(CorrType t) { int tsc = 0; int delay = 0; auto rs = gen_burst(t, delay, tsc); auto conved_beg = &(*rs.convolved)[0]; if (rs.ct == CorrType::RACH) { std::complex chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; float ncmax; char demodded_softbits[444]; int normal_burst_start = 0; normal_burst_start = get_access_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, 0); normal_burst_start = std::max(normal_burst_start, 0); for (int j = 0; j < 4; j++) { for (int start_val = 0; start_val < 16; start_val++) { auto bitdiffarr = std::make_unique(rs.bits.size()); detect_burst_ab(conved_beg, &chan_imp_resp[0], normal_burst_start + j, demodded_softbits, start_val); for (size_t i = 0; i < rs.bits.size(); i++) bitdiffarr.get()[i] = (demodded_softbits[i] < 0 ? 1 : 0) ^ rs.bits[i]; auto ber = std::accumulate(bitdiffarr.get(), bitdiffarr.get() + rs.bits.size(), 0); std::cerr << "ber " << std::setw(4) << ber << " bo:" << std::setw(4) << j << " vas:" << std::setw(4) << start_val << " "; // for (size_t i = 0; i < rs.num_bits; i++) // std::cerr << (demodded_softbits[i] < 0 ? "1" : "0"); // std::cerr << std::endl; // std::cerr << "d " << std::setw(4) << ber << " "; for (size_t i = 0; i < rs.bits.size(); i++) std::cerr << (bitdiffarr.get()[i] ? "1" : "0"); std::cerr << std::endl; // std::cerr << "v " << std::setw(4) << j << std::setw(4) << start_val << " "; // for (size_t i = 0; i < rs.num_bits; i++) // std::cerr << (demodded_softbits[i] < 0 ? "1" : "0"); // std::cerr << std::endl; // std::cerr << "d " << std::setw(4) << ber << " "; // for (size_t i = 0; i < rs.num_bits; i++) // std::cerr << (ptr.get()[i] ? "1" : "0"); // std::cerr << std::endl; } } } else { std::complex chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; float ncmax; char demodded_softbits[444]; auto normal_burst_start = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc); detect_burst_nb(conved_beg, &chan_imp_resp[0], normal_burst_start + 0, demodded_softbits); std::cerr << "toa " << std::setprecision(2) << normal_burst_start << std::endl; std::cerr << "vita "; for (size_t i = 0; i < rs.bits.size(); i++) std::cerr << (demodded_softbits[i] < 0 ? "1" : "0"); std::cerr << std::endl; std::cerr << "diff "; for (size_t i = 0; i < rs.bits.size(); i++) std::cerr << ((demodded_softbits[i] < 0 ? 1 : 0) ^ rs.bits[i] ? "1" : "0"); std::cerr << std::endl; } struct estim_burst_params ebp; char demodded_softbits[444]; complex *rx_sigproc_cfloat = reinterpret_cast(conved_beg); signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free); auto rc = detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 40, &ebp); auto rxBurst = std::unique_ptr(demodAnyBurst(sv, (CorrType)rc, 4, &ebp)); std::cerr << "toa " << std::setprecision(2) << ebp.toa << std::endl; for (ssize_t i = 0; i < delay; i++) // maybe pad rach op? demodded_softbits[i] = 0; for (size_t i = 0 + delay; i < rs.bits.size() + delay; i++) demodded_softbits[i] = (rxBurst->bit(i) ? 1 : 0); std::cerr << "sigp "; for (size_t i = 0; i < rs.bits.size(); i++) std::cerr << (demodded_softbits[i] ? "1" : "0"); std::cerr << std::endl; std::cerr << "diff "; for (size_t i = 0; i < rs.bits.size(); i++) std::cerr << (demodded_softbits[i] ^ rs.bits[i] ? "1" : "0"); std::cerr << std::endl; } void demod_test_offsets() { const int tsc = 0; const int delaybuffer_realoffset = 100; { auto rs = gen_burst(CorrType::RACH, 0, tsc); typeof(*rs.convolved) delay_buffer(rs.convolved->size() * 2); // plenty of space.. for (int delay = -10; delay < 60; delay++) { std::fill(delay_buffer.begin(), delay_buffer.end(), 0); std::copy(rs.convolved->begin(), rs.convolved->end(), delay_buffer.begin() + delaybuffer_realoffset + delay); auto conved_beg = &delay_buffer[delaybuffer_realoffset]; std::complex chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; float ncmax; auto va_burst_start = get_access_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, 60); complex *rx_sigproc_cfloat = reinterpret_cast(conved_beg); struct estim_burst_params ebp; signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free); detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 60, &ebp); std::cerr << "delay:" << std::setw(3) << std::setprecision(2) << delay; std::cerr << " va: " << std::setw(3) << std::setprecision(2) << va_burst_start; std::cerr << " sg: " << std::setw(3) << std::setprecision(2) << ebp.toa; std::cerr << " d: " << std::setw(3) << std::setprecision(2) << (ebp.toa * 4) - va_burst_start; std::cerr << " ! " << float(va_burst_start + 13) / 4 << std::endl; } } { auto rs = gen_burst(CorrType::TSC, 0, tsc); typeof(*rs.convolved) delay_buffer(rs.convolved->size() * 2); // plenty of space.. for (int delay = -10; delay < 10; delay++) { std::fill(delay_buffer.begin(), delay_buffer.end(), 0); std::copy(rs.convolved->begin(), rs.convolved->end(), delay_buffer.begin() + delaybuffer_realoffset + delay); auto conved_beg = &delay_buffer[delaybuffer_realoffset]; std::complex chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; float ncmax; auto va_burst_start = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc); complex *rx_sigproc_cfloat = reinterpret_cast(conved_beg); struct estim_burst_params ebp; signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free); detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 60, &ebp); std::cerr << "delay:" << std::setw(3) << std::setprecision(2) << delay; std::cerr << " va: " << std::setw(3) << std::setprecision(2) << va_burst_start; std::cerr << " sg: " << std::setw(3) << std::setprecision(2) << ebp.toa; std::cerr << " d: " << std::setw(3) << std::setprecision(2) << (ebp.toa * 4) - va_burst_start; std::cerr << " ! " << float(va_burst_start + 19) / 4 << std::endl; } } } int main() { convolve_init(); convert_init(); sigProcLibSetup(); initvita(); for (int i = 0; i < 1; i++) { demod_real_burst(i); demod_generated_burst(CorrType::RACH); demod_generated_burst(CorrType::TSC); demod_test_offsets(); } }