blob: 2016541a2d97c00574c2cd0458f9bc8ef25ee8f2 [file] [log] [blame]
Ericac726b12022-11-28 18:57:58 +01001/* -*- c++ -*- */
2/*
3 * @file
4 * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
5 * @author Contributions by sysmocom - s.f.m.c. GmbH / Eric Wild <ewild@sysmocom.de>
6 * @section LICENSE
7 *
8 * Gr-gsm is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3, or (at your option)
11 * any later version.
12 *
13 * Gr-gsm 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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with gr-gsm; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "constants.h"
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29#include <complex>
30
31
32#include <algorithm>
33#include <string.h>
34#include <iostream>
35#include <numeric>
36#include <vector>
37#include <fstream>
38
39#include "viterbi_detector.h"
40#include "grgsm_vitac.h"
41
Erica5a22752023-01-09 22:46:41 +010042gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a RACH burst
Ericac726b12022-11-28 18:57:58 +010043gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
44gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
45const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
46
Erica5a22752023-01-09 22:46:41 +010047void initvita()
48{
Ericac726b12022-11-28 18:57:58 +010049 /**
50 * Prepare SCH sequence bits
51 *
52 * (TS_BITS + 2 * GUARD_PERIOD)
53 * Burst and two guard periods
54 * (one guard period is an arbitrary overlap)
55 */
Erica5a22752023-01-09 22:46:41 +010056 gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
Ericac726b12022-11-28 18:57:58 +010057 for (auto &i : d_sch_training_seq)
58 i = conj(i);
59
Erica5a22752023-01-09 22:46:41 +010060 /* ab */
61 gmsk_mapper(ACCESS_BITS, N_ACCESS_BITS, d_acc_training_seq, gr_complex(0.0, -1.0));
62 for (auto &i : d_acc_training_seq)
63 i = conj(i);
64
Ericac726b12022-11-28 18:57:58 +010065 /* Prepare bits of training sequences */
66 for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
67 /**
68 * If first bit of the sequence is 0
69 * => first symbol is 1, else -1
70 */
Erica5a22752023-01-09 22:46:41 +010071 gr_complex startpoint = train_seq[i][0] == 0 ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
72 gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
Ericac726b12022-11-28 18:57:58 +010073 for (auto &i : d_norm_training_seq[i])
74 i = conj(i);
75 }
Ericac726b12022-11-28 18:57:58 +010076}
77
Erica5a22752023-01-09 22:46:41 +010078template <unsigned int burst_size>
79NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start,
80 char *output_binary, int ss)
Ericac726b12022-11-28 18:57:58 +010081{
82 std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
83 unsigned int stop_states[2] = { 4, 12 };
Erica5a22752023-01-09 22:46:41 +010084 gr_complex filtered_burst[burst_size];
Ericac726b12022-11-28 18:57:58 +010085 gr_complex rhh[CHAN_IMP_RESP_LENGTH];
Erica5a22752023-01-09 22:46:41 +010086 float output[burst_size];
87 int start_state = ss;
Ericac726b12022-11-28 18:57:58 +010088
89 autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
90 for (int ii = 0; ii < d_chan_imp_length; ii++)
91 rhh[ii] = conj(rhh_temp[ii * d_OSR]);
92
Erica5a22752023-01-09 22:46:41 +010093 mafi(&input[burst_start], burst_size, chan_imp_resp, d_chan_imp_length * d_OSR, filtered_burst);
Ericac726b12022-11-28 18:57:58 +010094
Erica5a22752023-01-09 22:46:41 +010095 viterbi_detector(filtered_burst, burst_size, rhh, start_state, stop_states, 2, output);
Ericac726b12022-11-28 18:57:58 +010096
Erica5a22752023-01-09 22:46:41 +010097 for (unsigned int i = 0; i < burst_size; i++)
Eric Wild56c7b772024-02-21 18:58:42 +010098 output_binary[i] = output[i] > 0 ? -127 : 127; // pre flip bits!
Ericac726b12022-11-28 18:57:58 +010099}
100
Erica5a22752023-01-09 22:46:41 +0100101NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
102 int ss)
103{
104 return detect_burst_generic<BURST_SIZE>(input, chan_imp_resp, burst_start, output_binary, ss);
105}
106NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
107 int ss)
108{
109 return detect_burst_generic<8 + 41 + 36 + 3>(input, chan_imp_resp, burst_start, output_binary, ss);
110}
111
112NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
113{
114 return detect_burst_nb(input, chan_imp_resp, burst_start, output_binary, 3);
115}
116NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
117{
118 return detect_burst_ab(input, chan_imp_resp, burst_start, output_binary, 3);
119}
120
121void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point)
Ericac726b12022-11-28 18:57:58 +0100122{
123 gr_complex j = gr_complex(0.0, 1.0);
124 gmsk_output[0] = start_point;
125
126 int previous_symbol = 2 * input[0] - 1;
127 int current_symbol;
128 int encoded_symbol;
129
130 for (int i = 1; i < nitems; i++) {
131 /* Change bits representation to NRZ */
132 current_symbol = 2 * input[i] - 1;
133
134 /* Differentially encode */
135 encoded_symbol = current_symbol * previous_symbol;
136
137 /* And do GMSK mapping */
Erica5a22752023-01-09 22:46:41 +0100138 gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i - 1];
Ericac726b12022-11-28 18:57:58 +0100139
140 previous_symbol = current_symbol;
141 }
142}
143
Erica5a22752023-01-09 22:46:41 +0100144gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input)
Ericac726b12022-11-28 18:57:58 +0100145{
146 gr_complex result(0.0, 0.0);
147
148 for (int ii = 0; ii < length; ii++)
149 result += sequence[ii] * input[ii * d_OSR];
150
151 return conj(result) / gr_complex(length, 0);
152}
153
154/* Computes autocorrelation for positive arguments */
Erica5a22752023-01-09 22:46:41 +0100155inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems)
Ericac726b12022-11-28 18:57:58 +0100156{
157 for (int k = nitems - 1; k >= 0; k--) {
158 out[k] = gr_complex(0, 0);
159 for (int i = k; i < nitems; i++)
160 out[k] += input[i] * conj(input[i - k]);
161 }
162}
163
Erica5a22752023-01-09 22:46:41 +0100164inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int filter_length, gr_complex *output)
Ericac726b12022-11-28 18:57:58 +0100165{
166 for (int n = 0; n < nitems; n++) {
167 int a = n * d_OSR;
168 output[n] = 0;
169
170 for (int ii = 0; ii < filter_length; ii++) {
171 if ((a + ii) >= nitems * d_OSR)
172 break;
173
174 output[n] += input[a + ii] * filter[ii];
175 }
176 }
177}
178
Erica5a22752023-01-09 22:46:41 +0100179int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_start_pos, int search_stop_pos,
180 gr_complex *tseq, int tseqlen, float *corr_max)
Ericac726b12022-11-28 18:57:58 +0100181{
Erica5a22752023-01-09 22:46:41 +0100182 const int num_search_windows = search_stop_pos - search_start_pos;
183 const int power_search_window_len = d_chan_imp_length * d_OSR;
Ericac726b12022-11-28 18:57:58 +0100184 std::vector<float> window_energy_buffer;
185 std::vector<float> power_buffer;
Erica5a22752023-01-09 22:46:41 +0100186 std::vector<gr_complex> correlation_buffer;
Ericac726b12022-11-28 18:57:58 +0100187
Erica5a22752023-01-09 22:46:41 +0100188 power_buffer.reserve(num_search_windows);
189 correlation_buffer.reserve(num_search_windows);
190 window_energy_buffer.reserve(num_search_windows);
191
192 for (int ii = 0; ii < num_search_windows; ii++) {
193 gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[search_start_pos + ii]);
Ericac726b12022-11-28 18:57:58 +0100194 correlation_buffer.push_back(correlation);
195 power_buffer.push_back(std::pow(abs(correlation), 2));
196 }
197
Ericac726b12022-11-28 18:57:58 +0100198 /* Compute window energies */
Erica5a22752023-01-09 22:46:41 +0100199 float windowSum = 0;
Ericac726b12022-11-28 18:57:58 +0100200
Erica5a22752023-01-09 22:46:41 +0100201 // first window
202 for (int i = 0; i < power_search_window_len; i++) {
203 windowSum += power_buffer[i];
204 }
205 window_energy_buffer.push_back(windowSum);
Ericac726b12022-11-28 18:57:58 +0100206
Erica5a22752023-01-09 22:46:41 +0100207 // slide windows
208 for (int i = power_search_window_len; i < num_search_windows; i++) {
209 windowSum += power_buffer[i] - power_buffer[i - power_search_window_len];
210 window_energy_buffer.push_back(windowSum);
Ericac726b12022-11-28 18:57:58 +0100211 }
212
Erica5a22752023-01-09 22:46:41 +0100213 int strongest_window_nr = std::max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
Ericac726b12022-11-28 18:57:58 +0100214 window_energy_buffer.begin();
215
Ericac726b12022-11-28 18:57:58 +0100216 float max_correlation = 0;
Erica5a22752023-01-09 22:46:41 +0100217 for (int ii = 0; ii < power_search_window_len; ii++) {
Ericac726b12022-11-28 18:57:58 +0100218 gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
219 if (abs(correlation) > max_correlation)
220 max_correlation = abs(correlation);
221 chan_imp_resp[ii] = correlation;
222 }
223
224 *corr_max = max_correlation;
225
226 /**
227 * Compute first sample position, which corresponds
228 * to the first sample of the impulse response
229 */
Erica5a22752023-01-09 22:46:41 +0100230 return search_start_pos + strongest_window_nr;
231}
232
233/*
2348 ext tail bits
23541 sync seq
23636 encrypted bits
2373 tail bits
23868.25 extended tail bits (!)
239
240center at 8+5 (actually known tb -> known isi, start at 8?) FIXME
241*/
242int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay)
243{
244 const int search_center = 8 + 5;
245 const int search_start_pos = (search_center - 5) * d_OSR + 1;
246 const int search_stop_pos = (search_center + 5 + d_chan_imp_length + max_delay) * d_OSR;
247 const auto tseq = &d_acc_training_seq[TRAIN_BEGINNING];
248 const auto tseqlen = N_ACCESS_BITS - (2 * TRAIN_BEGINNING);
249 return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
250 search_center * d_OSR;
Ericac726b12022-11-28 18:57:58 +0100251}
252
253/*
254
2553 + 57 + 1 + 26 + 1 + 57 + 3 + 8.25
256
257search center = 3 + 57 + 1 + 5 (due to tsc 5+16+5 split)
258this is +-5 samples around (+5 beginning) of truncated t16 tsc
259
260*/
261int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc)
262{
263 const int search_center = TRAIN_POS;
264 const int search_start_pos = (search_center - 5) * d_OSR + 1;
265 const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR;
266 const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
267 const auto tseqlen = N_TRAIN_BITS - (2 * TRAIN_BEGINNING);
Erica5a22752023-01-09 22:46:41 +0100268 return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
269 search_center * d_OSR;
Ericac726b12022-11-28 18:57:58 +0100270}
271
272/*
273
2743 tail | 39 data | 64 tsc | 39 data | 3 tail | 8.25 guard
275start 3+39 - 10
276end 3+39 + SYNC_SEARCH_RANGE
277
278*/
279int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
280{
281 const int search_center = SYNC_POS + TRAIN_BEGINNING;
282 const int search_start_pos = (search_center - 10) * d_OSR;
283 const int search_stop_pos = (search_center + SYNC_SEARCH_RANGE) * d_OSR;
284 const auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
285 const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
286
287 // strongest_window_nr + chan_imp_resp_center + SYNC_POS *d_OSR - 48 * d_OSR - 2 * d_OSR + 2 ;
288 float corr_max;
Erica5a22752023-01-09 22:46:41 +0100289 return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, &corr_max) -
290 search_center * d_OSR;
291 ;
Ericac726b12022-11-28 18:57:58 +0100292}
293
294int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
295{
296 const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
297 const int search_center = SYNC_POS + TRAIN_BEGINNING;
298 const int search_start_pos = 0;
299 // FIXME: proper end offset
Erica5a22752023-01-09 22:46:41 +0100300 const int search_stop_pos = len - (N_SYNC_BITS * 8);
Ericac726b12022-11-28 18:57:58 +0100301 auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
302
Erica5a22752023-01-09 22:46:41 +0100303 return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
304 search_center * d_OSR;
Ericac726b12022-11-28 18:57:58 +0100305}