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