blob: c370c4b691728ea156ebaa1c31bff53428413cac [file] [log] [blame]
Max51754b62019-03-13 17:14:13 +01001/* coding_scheme.c
2 *
3 * Copyright (C) 2019 by sysmocom s.f.m.c. GmbH
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Max51754b62019-03-13 17:14:13 +010014 */
15
16#include <stdint.h>
17#include <stdbool.h>
18
19#include <osmocom/core/utils.h>
20
21#include "coding_scheme.h"
22
Max136ebcc2019-03-05 14:59:03 +010023const struct value_string mcs_names[] = {
24 { UNKNOWN, "UNKNOWN" },
25 { CS1, "CS-1" },
26 { CS2, "CS-2" },
27 { CS3, "CS-3" },
28 { CS4, "CS-4" },
29 { MCS1, "MCS-1" },
30 { MCS2, "MCS-2" },
31 { MCS3, "MCS-3" },
32 { MCS4, "MCS-4" },
33 { MCS5, "MCS-5" },
34 { MCS6, "MCS-6" },
35 { MCS7, "MCS-7" },
36 { MCS8, "MCS-8" },
37 { MCS9, "MCS-9" },
38 { 0, NULL }
39};
40
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +020041enum Family {
42 FAMILY_INVALID,
43 FAMILY_A,
44 FAMILY_B,
45 FAMILY_C,
46};
47
48static struct {
49 struct {
50 uint8_t bytes;
51 uint8_t ext_bits;
52 uint8_t data_header_bits;
53 } uplink, downlink;
54 uint8_t data_bytes;
55 uint8_t optional_padding_bits;
56 enum HeaderType data_hdr;
57 enum Family family;
58} mcs_info[NUM_SCHEMES] = {
59 {{0, 0}, {0, 0}, 0, 0,
60 HEADER_INVALID, FAMILY_INVALID},
61 {{23, 0}, {23, 0}, 20, 0,
62 HEADER_GPRS_DATA, FAMILY_INVALID},
63 {{33, 7}, {33, 7}, 30, 0,
64 HEADER_GPRS_DATA, FAMILY_INVALID},
65 {{39, 3}, {39, 3}, 36, 0,
66 HEADER_GPRS_DATA, FAMILY_INVALID},
67 {{53, 7}, {53, 7}, 50, 0,
68 HEADER_GPRS_DATA, FAMILY_INVALID},
69
70 {{26, 1}, {26, 1}, 22, 0,
71 HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
72 {{32, 1}, {32, 1}, 28, 0,
73 HEADER_EGPRS_DATA_TYPE_3, FAMILY_B},
74 {{41, 1}, {41, 1}, 37, 48,
75 HEADER_EGPRS_DATA_TYPE_3, FAMILY_A},
76 {{48, 1}, {48, 1}, 44, 0,
77 HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
78
79 {{60, 7}, {59, 6}, 56, 0,
80 HEADER_EGPRS_DATA_TYPE_2, FAMILY_B},
81 {{78, 7}, {77, 6}, 74, 48,
82 HEADER_EGPRS_DATA_TYPE_2, FAMILY_A},
83 {{118, 2}, {117, 4}, 56, 0,
84 HEADER_EGPRS_DATA_TYPE_1, FAMILY_B},
85 {{142, 2}, {141, 4}, 68, 0,
86 HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
87 {{154, 2}, {153, 4}, 74, 0,
88 HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
89};
90
Max136ebcc2019-03-05 14:59:03 +010091const char *mcs_name(enum CodingScheme val) {
92 return get_value_string(mcs_names, val);
93}
94
Max8a8e0fb2019-03-25 16:32:50 +010095bool mcs_is_gprs(enum CodingScheme cs)
96{
97 return CS1 <= cs && cs <= CS4;
98}
99
100bool mcs_is_edge(enum CodingScheme cs)
101{
102 return MCS1 <= cs && cs <= MCS9;
103}
104
105bool mcs_is_edge_gmsk(enum CodingScheme cs)
106{
107 if (mcs_is_edge(cs))
108 return cs <= MCS4;
109
110 return false;
111}
112
Max898dddb2019-03-12 15:50:57 +0100113/* Return 3GPP TS 44.060 ยง12.10d (EDGE) or Table 11.2.28.2 (GPRS) Channel Coding Command value */
114uint8_t mcs_chan_code(enum CodingScheme cs)
115{
116 if (mcs_is_gprs(cs))
117 return cs - CS1;
118
119 if (mcs_is_edge(cs))
120 return cs - MCS1;
121
122 /* Defaults to (M)CS1 */
123 return 0;
124}
125
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200126enum CodingScheme mcs_get_by_size_ul(unsigned size)
127{
128 switch (size) {
129 case 23: return CS1;
130 case 27: return MCS1;
131 case 33: return MCS2;
132 case 34: return CS2;
133 case 40: return CS3;
134 case 42: return MCS3;
135 case 49: return MCS4;
136 case 54: return CS4;
137 case 61: return MCS5;
138 case 79: return MCS6;
139 case 119: return MCS7;
140 case 143: return MCS8;
141 case 155: return MCS9;
142 default: return UNKNOWN;
143 }
144}
145
146enum CodingScheme mcs_get_gprs_by_num(unsigned num)
147{
148 if (num < 1 || num > 4)
149 return UNKNOWN;
150 return CS1 + (num - 1);
151}
152
153enum CodingScheme mcs_get_egprs_by_num(unsigned num)
154{
155 if (num < 1 || num > 9)
156 return UNKNOWN;
157 return MCS1 + (num - 1);
158}
159
160bool mcs_is_valid(enum CodingScheme cs)
161{
Pau Espin Pedrolf1159c52020-11-04 17:53:07 +0100162 return UNKNOWN < cs && cs <= MCS9;
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200163}
164
165bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode)
166{
167 switch (mode) {
168 case GPRS: return mcs_is_gprs(cs);
169 case EGPRS_GMSK: return mcs_is_edge_gmsk(cs);
170 case EGPRS: return mcs_is_edge(cs);
171 }
172
173 return false;
174}
175
176bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o)
177{
178 return (mcs_is_gprs(cs) && mcs_is_gprs(o)) || (mcs_is_edge(cs) && mcs_is_edge(o));
179}
180
181uint8_t mcs_size_ul(enum CodingScheme cs)
182{
183 return mcs_info[cs].uplink.bytes + (mcs_spare_bits_ul(cs) ? 1 : 0);
184}
185
186uint8_t mcs_size_dl(enum CodingScheme cs)
187{
188 return mcs_info[cs].downlink.bytes + (mcs_spare_bits_dl(cs) ? 1 : 0);
189}
190
191uint8_t mcs_used_size_ul(enum CodingScheme cs)
192{
193 if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
194 return mcs_info[cs].uplink.bytes;
195 else
196 return mcs_size_ul(cs);
197}
198
199uint8_t mcs_used_size_dl(enum CodingScheme cs)
200{
201 if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
202 return mcs_info[cs].downlink.bytes;
203 else
204 return mcs_size_dl(cs);
205}
206
207uint8_t mcs_max_bytes_ul(enum CodingScheme cs)
208{
209 return mcs_info[cs].uplink.bytes;
210}
211
212uint8_t mcs_max_bytes_dl(enum CodingScheme cs)
213{
214 return mcs_info[cs].downlink.bytes;
215}
216
217uint8_t mcs_spare_bits_ul(enum CodingScheme cs)
218{
219 return mcs_info[cs].uplink.ext_bits;
220}
221
222uint8_t mcs_spare_bits_dl(enum CodingScheme cs)
223{
224 return mcs_info[cs].downlink.ext_bits;
225}
226
227uint8_t mcs_max_data_block_bytes(enum CodingScheme cs)
228{
229 return mcs_info[cs].data_bytes;
230}
231
232uint8_t mcs_opt_padding_bits(enum CodingScheme cs)
233{
234 return mcs_info[cs].optional_padding_bits;
235}
236
237void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode)
238{
239 if (!mcs_is_compat_kind(*cs, mode))
240 /* This should not happen. TODO: Use assert? */
241 return;
242
243 enum CodingScheme new_cs = *cs + 1;
244 if (!mcs_is_compat_kind(new_cs, mode))
245 /* Clipping, do not change the value */
246 return;
247
248 *cs = new_cs;
249}
250
251void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode)
252{
253 if (!mcs_is_compat_kind(*cs, mode))
254 /* This should not happen. TODO: Use assert? */
255 return;
256
257 enum CodingScheme new_cs = *cs - 1;
258 if (!mcs_is_compat_kind(new_cs, mode))
259 /* Clipping, do not change the value */
260 return;
261
262 *cs = new_cs;
263}
264
265void mcs_inc(enum CodingScheme *cs)
266{
267 if (mcs_is_gprs(*cs) && *cs == CS4)
268 return;
269
270 if (mcs_is_edge(*cs) && *cs == MCS9)
271 return;
272
273 if (!mcs_is_valid(*cs))
274 return;
275
276 *cs = *cs + 1;
277}
278
279void mcs_dec(enum CodingScheme *cs)
280{
281 if (mcs_is_gprs(*cs) && *cs == CS1)
282 return;
283
284 if (mcs_is_edge(*cs) && *cs == MCS1)
285 return;
286
287 if (!mcs_is_valid(*cs))
288 return;
289
290 *cs = *cs - 1;
291}
292
293bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o)
294{
295 if (cs == o)
296 return true;
297
298 if (mcs_info[cs].family == FAMILY_INVALID)
299 return false;
300
301 return mcs_info[cs].family == mcs_info[o].family;
302}
303
304void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing)
305{
306 switch (*cs) {
307 case MCS7: *need_stuffing = false; *cs = MCS5; break;
308 case MCS8: *need_stuffing = true; *cs = MCS6; break;
309 case MCS9: *need_stuffing = false; *cs = MCS6; break;
310 default: *need_stuffing = false; break;
311 }
312}
313
Max51754b62019-03-13 17:14:13 +0100314static struct {
315 struct {
316 uint8_t data_header_bits;
317 } uplink, downlink;
318 uint8_t data_block_header_bits;
319 uint8_t num_blocks;
320 const char *name;
321} hdr_type_info[NUM_HEADER_TYPES] = {
322 { { 0 }, { 0 }, 0, 0, "INVALID" },
323 { { 1 * 8 + 0 }, { 1 * 8 + 0 }, 0, 0, "CONTROL" },
324 { { 3 * 8 + 0 }, { 3 * 8 + 0 }, 0, 1, "GPRS_DATA" },
325 { { 5 * 8 + 6 }, { 5 * 8 + 0 }, 2, 2, "EGPRS_DATA_TYPE1" },
326 { { 4 * 8 + 5 }, { 3 * 8 + 4 }, 2, 1, "EGPRS_DATA_TYPE2" },
327 { { 3 * 8 + 7 }, { 3 * 8 + 7 }, 2, 1, "EGPRS_DATA_TYPE3" },
328};
329
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200330enum HeaderType mcs_header_type(enum CodingScheme mcs)
331{
332 return mcs_info[mcs].data_hdr;
333}
334
Max51754b62019-03-13 17:14:13 +0100335uint8_t num_data_blocks(enum HeaderType ht)
336{
337 OSMO_ASSERT(ht < NUM_HEADER_TYPES);
338 return hdr_type_info[ht].num_blocks;
339}
340
341uint8_t num_data_header_bits_UL(enum HeaderType ht)
342{
343 OSMO_ASSERT(ht < NUM_HEADER_TYPES);
344 return hdr_type_info[ht].uplink.data_header_bits;
345}
346
347uint8_t num_data_header_bits_DL(enum HeaderType ht)
348{
349 OSMO_ASSERT(ht < NUM_HEADER_TYPES);
350 return hdr_type_info[ht].downlink.data_header_bits;
351}
352
353uint8_t num_data_block_header_bits(enum HeaderType ht)
354{
355 OSMO_ASSERT(ht < NUM_HEADER_TYPES);
356 return hdr_type_info[ht].data_block_header_bits;
357}
Maxa4de02d2019-03-13 16:35:09 +0100358
359const struct value_string mode_names[] = {
360 { GPRS, "GPRS" },
361 { EGPRS_GMSK, "EGPRS_GMSK-only"},
362 { EGPRS, "EGPRS"},
363 { 0, NULL }
364};
365
366const char *mode_name(enum mcs_kind val) {
367 return get_value_string(mode_names, val);
368}
Max902e3e52019-03-25 16:38:53 +0100369
370/* FIXME: take into account padding and special cases of commanded MCS (MCS-6-9 and MCS-5-7) */
371enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit)
372{
373 OSMO_ASSERT(mcs_is_edge(initial_mcs));
374 OSMO_ASSERT(mcs_is_edge(commanded_mcs));
375 OSMO_ASSERT(NUM_SCHEMES - MCS1 == 9);
376
377 if (resegment_bit) { /* 3GPP TS 44.060 Table 8.1.1.1, reflected over antidiagonal */
378 enum CodingScheme egprs_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
379 { MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
380 { MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
381 { MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
382 { MCS1, MCS1, MCS1, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
383 { MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
384 { MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
385 { MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
386 { MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS8, MCS8 },
387 { MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
388 };
389 return egprs_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
390 } else { /* 3GPP TS 44.060 Table 8.1.1.2, reflected over antidiagonal */
391 enum CodingScheme egprs_no_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
392 { MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
393 { MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
394 { MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
395 { MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
396 { MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
397 { MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
398 { MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
399 { MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS8, MCS8 },
400 { MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
401 };
402 return egprs_no_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
403 }
404}