blob: 54c2e98d6aa807490833717b3730663edb31c5d2 [file] [log] [blame]
Harald Weltea082a692017-07-15 15:58:13 +02001/* Encoding/Decoding routines for GSM System Information messages
Harald Welte34b5a952019-05-27 11:54:11 +02002 * according to 3GPP TS 44.018 Version 12.3.0 Release 12
3 *
4 * (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
5 * All rights reserved.
6 *
7 * Released under the terms of GNU General Public License, Version 2 or
8 * (at your option) any later version.
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 */
Harald Weltea082a692017-07-15 15:58:13 +020012
13module GSM_Types {
14
Alexander Couzens5e6ae792020-09-11 19:49:22 +020015import from General_Types all;
16import from Osmocom_Types all;
Pau Espin Pedrol38554622021-02-05 15:57:59 +010017import from Misc_Helpers all;
Harald Weltea082a692017-07-15 15:58:13 +020018
Alexander Couzens5e6ae792020-09-11 19:49:22 +020019type integer GsmArfcn (0..1023);
20type integer UmtsArfcn (0..16383);
21type integer UmtsScramblingCode (0..511);
22const integer GsmMaxFrameNumber := 26*51*2048;
23type integer GsmFrameNumber (0..GsmMaxFrameNumber);
24type integer GsmRxLev (0..63);
25type integer GsmTsc (0..7) with { variant "FIELDLENGTH(8)" };
26type uint32_t GsmTmsi;
27type OCT4 GprsTlli;
28type hexstring GsmMcc length(3);
29type hexstring GsmMnc length(2 .. 3);
30type uint16_t GsmLac;
31type uint16_t GsmCellId;
Harald Weltee33e5152023-04-09 23:29:48 +020032type octetstring GroupCallRef length(1 .. 5);
33
Harald Weltebdc5dbd2017-07-16 00:00:43 +020034
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +070035/* ARFCN with explicit band discrimination */
36type record GsmBandArfcn {
37 boolean pcs,
Vadim Yanitskiy05a236a2020-11-14 04:58:30 +070038 boolean uplink,
39 BIT4 spare,
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +070040 GsmArfcn arfcn
41} with {
42 variant (arfcn) "BYTEORDER(last)"
43 variant (arfcn) "FIELDLENGTH(10)"
44};
45
46template (value) GsmBandArfcn
47ts_GsmBandArfcn(template (value) GsmArfcn arfcn,
Vadim Yanitskiy05a236a2020-11-14 04:58:30 +070048 template (value) boolean pcs := false,
49 template (value) boolean uplink := false) := {
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +070050 pcs := pcs,
Vadim Yanitskiy05a236a2020-11-14 04:58:30 +070051 uplink := uplink,
52 spare := '0000'B,
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +070053 arfcn := arfcn
54};
55template GsmBandArfcn
56tr_GsmBandArfcn(template (present) GsmArfcn arfcn,
Vadim Yanitskiy05a236a2020-11-14 04:58:30 +070057 template (present) boolean pcs := ?,
58 template (present) boolean uplink := ?) := {
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +070059 pcs := pcs,
Vadim Yanitskiy05a236a2020-11-14 04:58:30 +070060 uplink := uplink,
Vadim Yanitskiy1acc7bb2020-11-14 04:24:57 +070061 spare := ?,
62 arfcn := arfcn
63};
64
Vadim Yanitskiy42d8bd52020-11-15 20:41:02 +070065/* see enum 'gsm_phys_chan_config' in libosmocore */
66type enumerated PchanConfig {
67 GSM_PCHAN_NONE,
68 GSM_PCHAN_CCCH,
69 GSM_PCHAN_CCCH_SDCCH4,
70 GSM_PCHAN_CCCH_SDCCH4_CBCH,
71 GSM_PCHAN_SDCCH8,
72 GSM_PCHAN_SDCCH8_CBCH,
73 GSM_PCHAN_TCHF,
74 GSM_PCHAN_TCHH,
75 GSM_PCHAN_PDCH,
76 /* IPA style dynamic TCH/F+PDCH */
77 GSM_PCHAN_TCHF_PDCH,
78 /* Osmocom style dynamic TCH/H+TCH/F+PDCH */
79 GSM_PCHAN_TCHH_TCHF_PDCH
80};
81
Alexander Couzens5e6ae792020-09-11 19:49:22 +020082type enumerated GprsCodingScheme {
83 CS1, CS2, CS3, CS4
84};
Harald Welte1dcc3712017-08-01 00:05:52 +020085
Alexander Couzens5e6ae792020-09-11 19:49:22 +020086function f_gprs_blocksize(GprsCodingScheme cs) return integer {
87 select (cs) {
88 case (CS1) { return 22 }
89 case (CS2) { return 32 }
90 case (CS3) { return 38 }
91 case (CS3) { return 52 }
Harald Welte060e27a2018-03-03 20:38:19 +010092 }
Pau Espin Pedrol38554622021-02-05 15:57:59 +010093 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
94 log2str("Invalid GPRS CS ", cs));
95 return 0;
Alexander Couzens5e6ae792020-09-11 19:49:22 +020096}
Harald Welte060e27a2018-03-03 20:38:19 +010097
Pau Espin Pedrolbfd69f62020-10-22 18:06:07 +020098const GprsTlli TLLI_UNUSED := 'FFFFFFFF'O;
99function f_gen_tlli() return GprsTlli {
100 var GprsTlli tlli := f_rnd_octstring(4);
101 if (tlli == TLLI_UNUSED) {
102 tlli := 'EEEEEEEE'O;
103 }
104 return tlli;
105}
106
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200107/* 10.5.2.8 */
108type enumerated ChannelNeeded {
109 CHAN_NEED_ANY (0),
110 CHAN_NEED_SDCCH (1),
111 CHAN_NEED_TCH_F (2),
112 CHAN_NEED_TCH_H (3)
113} with { variant "FIELDLENGTH(2)" };
114type record ChannelNeeded12 {
115 ChannelNeeded second,
116 ChannelNeeded first
117} with { variant "" };
Harald Welted2e342f2017-07-16 07:34:13 +0200118
Harald Welted2e342f2017-07-16 07:34:13 +0200119
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200120/* TS 48.058 9.3.1 Channel Number IE */
121type enumerated RslChanNr0 {
122 RSL_CHAN_NR_INVALID ('00000'B),
123 RSL_CHAN_NR_Bm_ACCH ('00001'B),
124 RSL_CHAN_NR_BCCH ('10000'B),
125 RSL_CHAN_NR_RACH ('10001'B),
126 RSL_CHAN_NR_PCH_AGCH ('10010'B),
127 RSL_CHAN_NR_OSMO_PDCH ('11000'B),
128 RSL_CHAN_NR_OSMO_CBCH4 ('11001'B),
Neels Hofmeyr9c3b82f2021-05-27 00:38:59 +0200129 RSL_CHAN_NR_OSMO_CBCH8 ('11010'B),
130 RSL_CHAN_NR_OSMO_VAMOS_Bm_ACCH ('11101'B)
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200131} with { variant "FIELDLENGTH(5)" variant "FIELDORDER(msb)" };
Harald Welte0c8d5c02017-07-16 18:53:58 +0200132
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200133type record RslChanNr2 {
Neels Hofmeyr9c3b82f2021-05-27 00:38:59 +0200134 BIT4 tag ('0001'B, '1111'B),
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200135 uint1_t sub_chan
136} with { variant "FIELDLENGTH(5)" variant "FIELDORDER(msb)" };
Harald Welte0c8d5c02017-07-16 18:53:58 +0200137
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200138type record RslChanNr4 {
139 BIT3 tag ('001'B),
140 uint2_t sub_chan
141} with { variant "FIELDLENGTH(5)" variant "FIELDORDER(msb)" };
Harald Welte0c8d5c02017-07-16 18:53:58 +0200142
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200143type record RslChanNr8 {
144 BIT2 tag ('01'B),
145 uint3_t sub_chan
146} with { variant "FIELDLENGTH(5)" variant "FIELDORDER(msb)" };
Harald Welte0c8d5c02017-07-16 18:53:58 +0200147
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200148type union RslChanNrU {
149 RslChanNr0 ch0,
150 RslChanNr2 lm,
151 RslChanNr4 sdcch4,
152 RslChanNr8 sdcch8
153} with {
154 variant "TAG(lm, tag = '0001'B;
Neels Hofmeyr9c3b82f2021-05-27 00:38:59 +0200155 lm, tag = '1111'B;
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200156 sdcch4, tag = '001'B;
157 sdcch8, tag = '01'B;
158 ch0, OTHERWISE)"
159 variant "FIELDLENGTH(5)"
160 variant "FIELDORDER(msb)"
161};
Harald Welte0c8d5c02017-07-16 18:53:58 +0200162
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200163type record RslChannelNr {
164 RslChanNrU u,
165 uint3_t tn
166} with { variant "FIELDLENGTH(8)" variant "FIELDORDER(msb)" };
Harald Welte0c8d5c02017-07-16 18:53:58 +0200167
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200168template RslChannelNr t_RslChanNr0(template uint3_t tn, template RslChanNr0 cht) := {
169 u := { ch0 := cht },
170 tn := tn
171}
172
173template RslChannelNr t_RslChanNr_RACH(template uint3_t tn) := t_RslChanNr0(tn, RSL_CHAN_NR_RACH);
174template RslChannelNr t_RslChanNr_BCCH(template uint3_t tn) := t_RslChanNr0(tn, RSL_CHAN_NR_BCCH);
175template RslChannelNr t_RslChanNr_PCH_AGCH(template uint3_t tn) := t_RslChanNr0(tn, RSL_CHAN_NR_PCH_AGCH);
176template RslChannelNr t_RslChanNr_Bm(template uint3_t tn) := t_RslChanNr0(tn, RSL_CHAN_NR_Bm_ACCH);
177template RslChannelNr t_RslChanNr_PDCH(template uint3_t tn) := t_RslChanNr0(tn, RSL_CHAN_NR_OSMO_PDCH);
178template RslChannelNr t_RslChanNr_CBCH4(template uint3_t tn) := t_RslChanNr0(tn, RSL_CHAN_NR_OSMO_CBCH4);
179template RslChannelNr t_RslChanNr_CBCH8(template uint3_t tn) := t_RslChanNr0(tn, RSL_CHAN_NR_OSMO_CBCH8);
180template RslChannelNr t_RslChanNr_Lm(template uint3_t tn, template uint1_t sub_slot) := {
181 u := { lm := { tag := '0001'B, sub_chan := sub_slot } },
182 tn := tn
183}
184template RslChannelNr t_RslChanNr_SDCCH4(template uint3_t tn, template uint2_t sub_slot) := {
185 u := { sdcch4 := { tag := '001'B, sub_chan := sub_slot } },
186 tn := tn
187}
188template RslChannelNr t_RslChanNr_SDCCH8(template uint3_t tn, template uint3_t sub_slot) := {
189 u := { sdcch8 := { tag := '01'B, sub_chan := sub_slot } },
190 tn := tn
191}
Neels Hofmeyr9c3b82f2021-05-27 00:38:59 +0200192template RslChannelNr t_RslChanNr_Osmo_VAMOS_Bm(template uint3_t tn) := {
193 u := { ch0 := RSL_CHAN_NR_OSMO_VAMOS_Bm_ACCH },
194 tn := tn
195}
196
197template RslChannelNr t_RslChanNr_Osmo_VAMOS_Lm(template uint3_t tn, template uint1_t sub_slot) := {
198 u := { lm := { tag := '1111'B, sub_chan := sub_slot } },
199 tn := tn
200}
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200201
202template (value) RslChannelNr ts_RslChanNr0(uint3_t tn, RslChanNr0 cht) := {
203 u := { ch0 := cht },
204 tn := tn
205}
206template (value) RslChannelNr ts_RslChanNr_RACH(uint3_t tn) := ts_RslChanNr0(tn, RSL_CHAN_NR_RACH);
207template (value) RslChannelNr ts_RslChanNr_BCCH(uint3_t tn) := ts_RslChanNr0(tn, RSL_CHAN_NR_BCCH);
208template (value) RslChannelNr ts_RslChanNr_PCH_AGCH(uint3_t tn) := ts_RslChanNr0(tn, RSL_CHAN_NR_PCH_AGCH);
209template (value) RslChannelNr ts_RslChanNr_Bm(uint3_t tn) := ts_RslChanNr0(tn, RSL_CHAN_NR_Bm_ACCH);
210template (value) RslChannelNr ts_RslChanNr_PDCH(uint3_t tn) := ts_RslChanNr0(tn, RSL_CHAN_NR_OSMO_PDCH);
211template (value) RslChannelNr ts_RslChanNr_CBCH4(uint3_t tn) := ts_RslChanNr0(tn, RSL_CHAN_NR_OSMO_CBCH4);
212template (value) RslChannelNr ts_RslChanNr_CBCH8(uint3_t tn) := ts_RslChanNr0(tn, RSL_CHAN_NR_OSMO_CBCH8);
213template (value) RslChannelNr ts_RslChanNr_Lm(uint3_t tn, uint1_t sub_slot) := {
214 u := { lm := { tag := '0001'B, sub_chan := sub_slot } },
215 tn := tn
216}
217template (value) RslChannelNr ts_RslChanNr_SDCCH4(uint3_t tn, uint2_t sub_slot) := {
218 u := { sdcch4 := { tag := '001'B, sub_chan := sub_slot } },
219 tn := tn
220}
221template (value) RslChannelNr ts_RslChanNr_SDCCH8(uint3_t tn, uint3_t sub_slot) := {
222 u := { sdcch8 := { tag := '01'B, sub_chan := sub_slot } },
223 tn := tn
224}
Neels Hofmeyr9c3b82f2021-05-27 00:38:59 +0200225template (value) RslChannelNr ts_RslChanNr_Osmo_VAMOS_Bm(uint3_t tn) := {
226 u := { ch0 := RSL_CHAN_NR_OSMO_VAMOS_Bm_ACCH },
227 tn := tn
228}
229template (value) RslChannelNr ts_RslChanNr_Osmo_VAMOS_Lm(uint3_t tn, uint1_t sub_slot) := {
230 u := { lm := { tag := '1111'B, sub_chan := sub_slot } },
231 tn := tn
232}
233
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200234
235/* TS 48.058 9.3.2 Link ID */
236type enumerated RslLinkIdC {
237 FACCH_SDCCH (0),
238 SACCH (1),
239 OSMO_PTCCH (2) /* Osmocom (trxcon) specific extension */
240} with { variant "FIELDLENGTH(2)" };
241
242type enumerated RslSapi0Prio {
243 SAPI0_PRIO_NORMAL (0),
244 SAPI0_PRIO_HIGH (1),
245 SAPI0_PRIO_LOW (2)
246} with { variant "FIELDLENGTH(2)" };
247
248type uint3_t GsmSapi;
249
250type record RslLinkId {
251 RslLinkIdC c,
252 boolean na,
253 RslSapi0Prio prio,
254 GsmSapi sapi
255} with { variant "" };
256
257template RslLinkId tr_RslLinkId := {
258 c := ?,
259 na := ?,
260 prio := ?,
261 sapi := ?
262};
263
264template RslLinkId tr_RslLinkID_DCCH(template GsmSapi sapi) modifies tr_RslLinkId := {
265 c := FACCH_SDCCH,
266 na := false,
267 sapi := sapi
268};
269
270template RslLinkId tr_RslLinkID_SACCH(template GsmSapi sapi) modifies tr_RslLinkId := {
271 c := SACCH,
272 na := false,
273 sapi := sapi
274};
275
276template RslLinkId tr_RslLinkID_OSMO_PTCCH(template GsmSapi sapi) modifies tr_RslLinkId := {
277 c := OSMO_PTCCH,
278 na := false,
279 sapi := sapi
280};
281
282template (value) RslLinkId ts_RslLinkID_DCCH(GsmSapi sapi) := {
283 c := FACCH_SDCCH,
284 na := false,
285 prio := SAPI0_PRIO_NORMAL,
286 sapi := sapi
287};
288
289template (value) RslLinkId ts_RslLinkID_SACCH(GsmSapi sapi) := {
290 c := SACCH,
291 na := false,
292 prio := SAPI0_PRIO_NORMAL,
293 sapi := sapi
294};
295
296template (value) RslLinkId ts_RslLinkID_OSMO_PTCCH(GsmSapi sapi) := {
297 c := OSMO_PTCCH,
298 na := false,
299 prio := SAPI0_PRIO_NORMAL,
300 sapi := sapi
301};
302
303function f_hex_is_odd_length(hexstring digits) return bitstring {
304 if (lengthof(digits) rem 2 == 1) {
305 return '1'B;
306 } else {
307 return '0'B;
Harald Welte0c8d5c02017-07-16 18:53:58 +0200308 }
Alexander Couzens5e6ae792020-09-11 19:49:22 +0200309}
Harald Welte23b774e2018-02-05 09:14:34 +0100310
Harald Weltef097a8b2018-09-16 18:11:26 +0200311
312/* TS 04.12 Section 3.3.1 Block type */
313type record CBCH_BlockType {
314 BIT1 spare,
315 BIT2 lpd,
316 boolean last_block,
317 uint4_t seq_nr
318};
319template (value) CBCH_BlockType ts_CBCH_BlockType(template (value) uint4_t seq_nr, template (value) boolean last_block) := {
320 spare := '0'B,
321 lpd := '01'B,
322 last_block := last_block,
323 seq_nr := seq_nr
324};
325template CBCH_BlockType tr_CBCH_BlockType(template uint4_t seq_nr := ?, template boolean last_block := ?) := {
326 spare := '0'B,
327 lpd := '01'B,
328 last_block := last_block,
329 seq_nr := seq_nr
330};
331
332/* TS 04.12 Section 3.3 */
333type record CBCH_Block {
334 CBCH_BlockType block_type,
335 OCT22 payload
336};
337template (value) CBCH_Block ts_CBCH_Block(template (value) uint4_t seq_nr, template (value) boolean last_block, template (value) OCT22 payload) := {
338 block_type := ts_CBCH_BlockType(seq_nr, last_block),
339 payload := payload
340};
341template CBCH_Block tr_CBCH_Block(template uint4_t seq_nr := ?, template boolean last_block := ?, template OCT22 payload := ?) := {
342 block_type := tr_CBCH_BlockType(seq_nr, last_block),
343 payload := payload
344};
345
346
347external function enc_CBCH_Block(in CBCH_Block msg) return octetstring
348 with { extension "prototype(convert) encode(RAW)" };
349external function dec_CBCH_Block(in octetstring stream) return CBCH_Block
350 with { extension "prototype(convert) decode(RAW)" };
351
352
Harald Welte5377d2f2018-02-24 01:01:19 +0100353/* Convert RF signal level in dBm to RxLev (TS 45.008 Chapter 8.1.4) */
354function dbm2rxlev(integer dbm) return uint6_t {
355 var integer rxlev := dbm + 110;
356 if (rxlev > 63) {
357 rxlev := 63;
358 } else if (rxlev < 0) {
359 rxlev := 0;
360 }
361 return rxlev;
362}
363
364function rxlev2dbm(uint6_t rxlev) return integer {
365 return -110 + rxlev;
366}
367
368/* convert BER to RxQual value (TS 45.008 Chapter 8.2.4 */
369function ber2rxqual(float ber) return uint3_t {
370 if (ber < 0.2) {
371 return 0;
372 } else if (ber < 0.4) {
373 return 1;
374 } else if (ber < 0.8) {
375 return 2;
376 } else if (ber < 1.6) {
377 return 3;
378 } else if (ber < 3.2) {
379 return 4;
380 } else if (ber < 6.4) {
381 return 5;
382 } else if (ber < 12.8) {
383 return 6;
384 } else {
385 return 7;
386 }
387}
388
389/* convert RxQual to BER (TS 45.008 Chapter 8.2.4 */
390function rxqual2ber(uint3_t rxqual) return float {
391 select (rxqual) {
392 case (0) { return 0.14 }
393 case (1) { return 0.28 }
394 case (2) { return 0.57 }
395 case (3) { return 1.13 }
396 case (4) { return 2.26 }
397 case (5) { return 4.53 }
398 case (6) { return 9.05 }
399 case (7) { return 18.10 }
400 case else { return 1000.0 }
401 }
402}
403
Harald Welte68e495b2018-02-25 00:05:57 +0100404const float GSM_FRAME_DURATION := 0.12/26.0; /* 4.615 ms */
405const float GSM51_MFRAME_DURATION := 51.0 * GSM_FRAME_DURATION; /* 235.365 ms */
406const float GSM51_MFRAMES_PER_SEC := 1.0 / GSM51_MFRAME_DURATION; /* 4.248 */
407
408/* number of downlink CCCH blocks per second */
409function f_ccch_blocks_per_mframe(boolean combined_ccch) return integer {
410 if (not combined_ccch) {
411 /* 9 blocks per 51 multiframe */
412 return 9;
413 } else {
414 /* 3 blocks per 51 multiframe */
415 return 3;
416 }
417}
418
419/* this ignores any possible paging combining! */
420function f_pch_block_rate_est(boolean combined_ccch, integer bs_ag_blks_res) return float {
421 var integer ccch_per_mframe := f_ccch_blocks_per_mframe(combined_ccch);
422 var integer pch_per_mframe := ccch_per_mframe - bs_ag_blks_res;
423 return GSM51_MFRAMES_PER_SEC * int2float(pch_per_mframe);
424}
425
426/* this ignores any possible imm.ass combining! */
427function f_agch_block_rate_est(boolean combined_ccch, integer bs_ag_blks_res) return float {
428 var integer ccch_per_mframe := f_ccch_blocks_per_mframe(combined_ccch);
429 return GSM51_MFRAMES_PER_SEC * int2float(bs_ag_blks_res);
430}
431
Harald Welte262f1222018-02-25 16:33:38 +0100432/* compute TC as per 45.002 6.3.1.3 */
433function f_gsm_compute_tc(integer fn) return integer {
434 return (fn / 51) mod 8;
435}
436
Harald Welte557c9f82020-09-12 21:30:17 +0200437type hexstring GsmBcdString with { variant "HEXORDER(low)" };
438type GsmBcdString BcdMccMnc with { variant "FIELDLENGTH(6)" };
439
Vadim Yanitskiy08c90212023-02-10 07:33:57 +0700440/* hex2oct()/oct2hex() do not respect the HEXORDER/FIELDLENGTH attributes,
441 * so better use the functions, employing TITAN's RAW codec, dclared below. */
442external function enc_BcdMccMnc(in BcdMccMnc str) return octetstring
443 with { extension "prototype(convert) encode(RAW)" };
444external function dec_BcdMccMnc(in octetstring str) return BcdMccMnc
445 with { extension "prototype(convert) decode(RAW)" };
446
Vadim Yanitskiy7a92d5f2023-02-08 00:27:12 +0700447/* Compute BcdMccMnc from a pair of GsmMcc/GsmMnc values */
Vadim Yanitskiy8cc1f722023-02-10 07:17:16 +0700448function f_build_BcdMccMnc(GsmMcc mcc, GsmMnc mnc) return BcdMccMnc {
Vadim Yanitskiy7a92d5f2023-02-08 00:27:12 +0700449 if (lengthof(mnc) == 2) {
450 mnc := mnc[0] & mnc[1] & 'F'H;
451 }
Vadim Yanitskiye9858ef2023-02-08 04:23:51 +0700452 /* 3GPP TS 24.008, Figure 10.5.13
453 * | MCC digit 2 | MCC digit 1 | octet 1
454 * | MNC digit 3 | MCC digit 3 | octet 2
Vadim Yanitskiy25cd0dc2023-02-10 05:21:29 +0700455 * | MNC digit 2 | MNC digit 1 | octet 3
456 *
457 * NOTE: TITAN takes care of swapping the nibbles in octets,
458 * so we use the normal (low-to-high) ordering here. */
459 return mcc[0] & mcc[1] & mcc[2] & mnc[2] & mnc[0] & mnc[1];
Vadim Yanitskiy7a92d5f2023-02-08 00:27:12 +0700460}
461
Pau Espin Pedrol0637ba02021-01-22 18:36:12 +0100462/* Compute BcdMccMnc from integer values */
Vadim Yanitskiy8cc1f722023-02-10 07:17:16 +0700463function f_build_BcdMccMnc_int(uint16_t mcc, uint16_t mnc, boolean mnc_3_digits) return BcdMccMnc {
Vadim Yanitskiy7a92d5f2023-02-08 00:27:12 +0700464 var hexstring mcc_str := str2hex(int2str(mcc));
465 var hexstring mnc_str := str2hex(int2str(mnc));
Pau Espin Pedrol0637ba02021-01-22 18:36:12 +0100466 if (mnc_3_digits == true) {
Vadim Yanitskiy8cc1f722023-02-10 07:17:16 +0700467 return f_build_BcdMccMnc(mcc_str[0] & mcc_str[1] & mcc_str[2],
468 mnc_str[0] & mnc_str[1] & mnc_str[2]);
Pau Espin Pedrol0637ba02021-01-22 18:36:12 +0100469 } else {
Vadim Yanitskiy8cc1f722023-02-10 07:17:16 +0700470 return f_build_BcdMccMnc(mcc_str[0] & mcc_str[1] & mcc_str[2],
471 mnc_str[0] & mnc_str[1]);
Pau Espin Pedrol0637ba02021-01-22 18:36:12 +0100472 }
473}
474
Vadim Yanitskiy8cc1f722023-02-10 07:17:16 +0700475testcase TC_selftest_BcdMccMnc() runs on Dummy_CT {
Vadim Yanitskiy25cd0dc2023-02-10 05:21:29 +0700476 if (not match('62F224'O, decmatch BcdMccMnc:'262F42'H)) { setverdict(fail); }
477 if (not match('21F354'O, decmatch BcdMccMnc:'123F45'H)) { setverdict(fail); }
478 if (not match('216354'O, decmatch BcdMccMnc:'123645'H)) { setverdict(fail); }
479
Vadim Yanitskiy08c90212023-02-10 07:33:57 +0700480 if (not match(enc_BcdMccMnc('262F42'H), '62F224'O)) { setverdict(fail); }
481 if (not match(enc_BcdMccMnc('123F45'H), '21F354'O)) { setverdict(fail); }
482 if (not match(enc_BcdMccMnc('123645'H), '216354'O)) { setverdict(fail); }
483
Vadim Yanitskiy8cc1f722023-02-10 07:17:16 +0700484 if (not match(f_build_BcdMccMnc('262'H, '42'H), BcdMccMnc:'262F42'H)) { setverdict(fail); }
485 if (not match(f_build_BcdMccMnc('123'H, '45'H), BcdMccMnc:'123F45'H)) { setverdict(fail); }
486 if (not match(f_build_BcdMccMnc('123'H, '456'H), BcdMccMnc:'123645'H)) { setverdict(fail); }
Vadim Yanitskiy25cd0dc2023-02-10 05:21:29 +0700487
Vadim Yanitskiy8cc1f722023-02-10 07:17:16 +0700488 if (not match(f_build_BcdMccMnc_int(262, 42, false), BcdMccMnc:'262F42'H)) { setverdict(fail); }
489 if (not match(f_build_BcdMccMnc_int(123, 45, false), BcdMccMnc:'123F45'H)) { setverdict(fail); }
490 if (not match(f_build_BcdMccMnc_int(123, 456, true), BcdMccMnc:'123645'H)) { setverdict(fail); }
Vadim Yanitskiy25cd0dc2023-02-10 05:21:29 +0700491
492 setverdict(pass);
493}
494
Harald Welte557c9f82020-09-12 21:30:17 +0200495/* 24.008 10.5.1.3 */
496type record LocationAreaIdentification {
497 BcdMccMnc mcc_mnc,
498 uint16_t lac
499} with { variant "" };
Pau Espin Pedrol8bd54cf2021-01-08 17:11:03 +0100500template (value) LocationAreaIdentification ts_LAI(BcdMccMnc mcc_mnc, uint16_t lac) := {
501 mcc_mnc := mcc_mnc,
502 lac := lac
503};
Harald Welte557c9f82020-09-12 21:30:17 +0200504
505/* 24.008 10.5.5.15 */
506type record RoutingAreaIdentification {
507 LocationAreaIdentification lai,
508 uint8_t rac
509} with { variant "" };
Pau Espin Pedrol8bd54cf2021-01-08 17:11:03 +0100510template (value) RoutingAreaIdentification ts_RAI(template (value) LocationAreaIdentification lai, uint8_t rac) := {
511 lai := lai,
512 rac := rac
513};
Harald Welte557c9f82020-09-12 21:30:17 +0200514
515external function enc_RoutingAreaIdentification(RoutingAreaIdentification rai) return octetstring
516with { extension "prototype(convert)" extension "encode(RAW)" }
517
518/* TS 24.008 10.5.1.1 */
519type uint16_t CellIdentity;
520
Harald Welte5377d2f2018-02-24 01:01:19 +0100521
Harald Weltea082a692017-07-15 15:58:13 +0200522} with { encode "RAW"; variant "FIELDORDER(msb)" }