blob: 34809a2b3dc32bcc048d7c1650066d00dac52725 [file] [log] [blame]
Ericb7253c62022-11-28 19:21:08 +01001/*
2 * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
3 * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
4 * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
5 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
6 * (C) 2022 by 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> / Eric Wild <ewild@sysmocom.de>
7 *
8 * All Rights Reserved
9 *
10 * SPDX-License-Identifier: GPL-2.0+
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 */
22
23#include <complex.h>
24#include <stdio.h>
25#include <math.h>
26#include <string.h>
27
28#include <osmocom/core/bits.h>
29#include <osmocom/core/conv.h>
30#include <osmocom/core/utils.h>
31#include <osmocom/core/crcgen.h>
32#include <osmocom/coding/gsm0503_coding.h>
33#include <osmocom/coding/gsm0503_parity.h>
34
35#include "sch.h"
36
37#pragma GCC diagnostic push
38#pragma GCC diagnostic ignored "-Wunused-variable"
39
40/* GSM 04.08, 9.1.30 Synchronization channel information */
41struct sch_packed_info {
42 ubit_t t1_hi[2];
43 ubit_t bsic[6];
44 ubit_t t1_md[8];
45 ubit_t t3p_hi[2];
46 ubit_t t2[5];
47 ubit_t t1_lo[1];
48 ubit_t t3p_lo[1];
49} __attribute__((packed));
50
51struct sch_burst {
52 sbit_t tail0[3];
53 sbit_t data0[39];
54 sbit_t etsc[64];
55 sbit_t data1[39];
56 sbit_t tail1[3];
57 sbit_t guard[8];
58} __attribute__((packed));
59
60static const uint8_t sch_next_output[][2] = {
61 { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
62 { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
63 { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
64 { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
65};
66
67static const uint8_t sch_next_state[][2] = {
68 { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
69 { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
70 { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
71 { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
72};
73
74static const struct osmo_conv_code gsm_conv_sch = {
75 .N = 2,
76 .K = 5,
77 .len = GSM_SCH_UNCODED_LEN,
78 .next_output = sch_next_output,
79 .next_state = sch_next_state,
80};
81
82#define GSM_MAX_BURST_LEN 157 * 4
83#define GSM_SYM_RATE (1625e3 / 6) * 4
84
85/* Pre-generated FCCH measurement tone */
86static complex float fcch_ref[GSM_MAX_BURST_LEN];
87
88int float_to_sbit(const float *in, sbit_t *out, float scale, int len)
89{
90 int i;
91
92 for (i = 0; i < len; i++) {
93 out[i] = (in[i] - 0.5f) * scale;
94 }
95
96 return 0;
97}
98
99/* Check if FN contains a FCCH burst */
100int gsm_fcch_check_fn(int fn)
101{
102 int fn51 = fn % 51;
103
104 switch (fn51) {
105 case 0:
106 case 10:
107 case 20:
108 case 30:
109 case 40:
110 return 1;
111 }
112
113 return 0;
114}
115
116/* Check if FN contains a SCH burst */
117int gsm_sch_check_fn(int fn)
118{
119 int fn51 = fn % 51;
120
121 switch (fn51) {
122 case 1:
123 case 11:
124 case 21:
125 case 31:
126 case 41:
127 return 1;
128 }
129
130 return 0;
131}
132
133int gsm_fcch_check_ts(int ts, int fn) {
134 return ts == 0 && gsm_fcch_check_fn(fn);
135}
136
137int gsm_sch_check_ts(int ts, int fn) {
138 return ts == 0 && gsm_sch_check_fn(fn);
139}
140
141/* SCH (T1, T2, T3p) to full FN value */
142int gsm_sch_to_fn(struct sch_info *sch)
143{
144 int t1 = sch->t1;
145 int t2 = sch->t2;
146 int t3p = sch->t3p;
147
148 if ((t1 < 0) || (t2 < 0) || (t3p < 0))
149 return -1;
150 int tt;
151 int t3 = t3p * 10 + 1;
152
153 if (t3 < t2)
154 tt = (t3 + 26) - t2;
155 else
156 tt = (t3 - t2) % 26;
157
158 return t1 * 51 * 26 + tt * 51 + t3;
159}
160
161/* Parse encoded SCH message */
162int gsm_sch_parse(const uint8_t *info, struct sch_info *desc)
163{
164 struct sch_packed_info *p = (struct sch_packed_info *) info;
165
166 desc->bsic = (p->bsic[0] << 0) | (p->bsic[1] << 1) |
167 (p->bsic[2] << 2) | (p->bsic[3] << 3) |
168 (p->bsic[4] << 4) | (p->bsic[5] << 5);
169
170 desc->t1 = (p->t1_lo[0] << 0) | (p->t1_md[0] << 1) |
171 (p->t1_md[1] << 2) | (p->t1_md[2] << 3) |
172 (p->t1_md[3] << 4) | (p->t1_md[4] << 5) |
173 (p->t1_md[5] << 6) | (p->t1_md[6] << 7) |
174 (p->t1_md[7] << 8) | (p->t1_hi[0] << 9) |
175 (p->t1_hi[1] << 10);
176
177 desc->t2 = (p->t2[0] << 0) | (p->t2[1] << 1) |
178 (p->t2[2] << 2) | (p->t2[3] << 3) |
179 (p->t2[4] << 4);
180
181 desc->t3p = (p->t3p_lo[0] << 0) | (p->t3p_hi[0] << 1) |
182 (p->t3p_hi[1] << 2);
183
184 return 0;
185}
186
187/* From osmo-bts */
188int gsm_sch_decode(uint8_t *info, sbit_t *data)
189{
190 int rc;
191 ubit_t uncoded[GSM_SCH_UNCODED_LEN];
192
193 osmo_conv_decode(&gsm_conv_sch, data, uncoded);
194
195 rc = osmo_crc16gen_check_bits(&gsm0503_sch_crc10,
196 uncoded, GSM_SCH_INFO_LEN,
197 uncoded + GSM_SCH_INFO_LEN);
198 if (rc)
199 return -1;
200
201 memcpy(info, uncoded, GSM_SCH_INFO_LEN * sizeof(ubit_t));
202
203 return 0;
204}
205
206#define FCCH_TAIL_BITS_LEN 3*4
207#define FCCH_DATA_LEN 100*4// 142
208#if 1
209/* Compute FCCH frequency offset */
210double org_gsm_fcch_offset(float *burst, int len)
211{
212 int i, start, end;
213 float a, b, c, d, ang, avg = 0.0f;
214 double freq;
215
216 if (len > GSM_MAX_BURST_LEN)
217 len = GSM_MAX_BURST_LEN;
218
219 for (i = 0; i < len; i++) {
220 a = burst[2 * i + 0];
221 b = burst[2 * i + 1];
222 c = crealf(fcch_ref[i]);
223 d = cimagf(fcch_ref[i]);
224
225 burst[2 * i + 0] = a * c - b * d;
226 burst[2 * i + 1] = a * d + b * c;
227 }
228
229 start = FCCH_TAIL_BITS_LEN;
230 end = start + FCCH_DATA_LEN;
231
232 for (i = start; i < end; i++) {
233 a = cargf(burst[2 * (i - 1) + 0] +
234 burst[2 * (i - 1) + 1] * I);
235 b = cargf(burst[2 * i + 0] +
236 burst[2 * i + 1] * I);
237
238 ang = b - a;
239
240 if (ang > M_PI)
241 ang -= 2 * M_PI;
242 else if (ang < -M_PI)
243 ang += 2 * M_PI;
244
245 avg += ang;
246 }
247
248 avg /= (float) (end - start);
249 freq = avg / (2 * M_PI) * GSM_SYM_RATE;
250
251 return freq;
252}
253
254
255static const int L1 = 3;
256static const int L2 = 32;
257static const int N1 = 92;
258static const int N2 = 92;
259
260static struct { int8_t r; int8_t s; } P_inv_table[3+32];
261
262void pinv(int P, int8_t* r, int8_t* s, int L1, int L2) {
263 for (int i = 0; i < L1; i++)
264 for (int j = 0; j < L2; j++)
265 if (P == L2 * i - L1 * j) {
266 *r = i;
267 *s = j;
268 return;
269 }
270}
271
272
273float ac_sum_with_lag( complex float* in, int lag, int offset, int N) {
274 complex float v = 0 + 0*I;
275 int total_offset = offset + lag;
276 for (int s = 0; s < N; s++)
277 v += in[s + total_offset] * conjf(in[s + total_offset - lag]);
278 return cargf(v);
279}
280
281
282double gsm_fcch_offset(float *burst, int len)
283{
284 int start;
285
286 const float fs = 13. / 48. * 1e6 * 4;
287 const float expected_fcch_val = ((2 * M_PI) / (fs)) * 67700;
288
289 if (len > GSM_MAX_BURST_LEN)
290 len = GSM_MAX_BURST_LEN;
291
292 start = FCCH_TAIL_BITS_LEN+10 * 4;
293 float alpha_one = ac_sum_with_lag((complex float*)burst, L1, start, N1);
294 float alpha_two = ac_sum_with_lag((complex float*)burst, L2, start, N2);
295
296 float P_unrounded = (L1 * alpha_two - L2 * alpha_one) / (2 * M_PI);
297 int P = roundf(P_unrounded);
298
299 int8_t r = 0, s = 0;
300 pinv(P, &r, &s, L1, L2);
301
302 float omegal1 = (alpha_one + 2 * M_PI * r) / L1;
303 float omegal2 = (alpha_two + 2 * M_PI * s) / L2;
304
305 float rv = org_gsm_fcch_offset(burst, len);
306 //return rv;
307
308 float reval = GSM_SYM_RATE / (2 * M_PI) * (expected_fcch_val - (omegal1+omegal2)/2);
309 //fprintf(stderr, "XX rv %f %f %f %f\n", rv, reval, omegal1 / (2 * M_PI) * fs, omegal2 / (2 * M_PI) * fs);
310
311 //fprintf(stderr, "XX rv %f %f\n", rv, reval);
312
313 return -reval;
314}
315#endif
316/* Generate FCCH measurement tone */
317static __attribute__((constructor)) void init()
318{
319 int i;
320 double freq = 0.25;
321
322 for (i = 0; i < GSM_MAX_BURST_LEN; i++) {
323 fcch_ref[i] = sin(2 * M_PI * freq * (double) i) +
324 cos(2 * M_PI * freq * (double) i) * I;
325 }
326
327}
328
329#pragma GCC diagnostic pop