blob: 4e5b71d71d499ed12f52130c73203d1c97fbbe47 [file] [log] [blame]
Philipp Maier40def492017-12-16 03:42:15 +07001#include <stdio.h>
2#include <stdint.h>
3#include <string.h>
Pau Espin Pedrolbbccca62018-07-20 18:58:19 +02004#include <inttypes.h>
Philipp Maier40def492017-12-16 03:42:15 +07005
6#include <osmocom/core/bitvec.h>
7#include <osmocom/core/utils.h>
8
9#include <osmocom/codec/codec.h>
10#include <osmocom/codec/ecu.h>
Pau Espin Pedrolbbccca62018-07-20 18:58:19 +020011#include <osmocom/codec/gsm610_bits.h>
Philipp Maier40def492017-12-16 03:42:15 +070012
13/* Set with sample full-rate voice frames and some intentional dropouts */
14static const char *fr_frames_hex[] = {
15 "d9aa93ae63de00471a91b95b8660471392b4a2daa037628f391c624039258dc723",
16 "d8eb83699a66c036ec89b7246e6034dc8d48948620589b7256e3a6603b2371b8da",
17 "d967abaa1cbe4035238da6ace4c036d46ec69ba600391c4eb8a2b040591c6a3924",
18 "d8e8a42662c240472469b91bd2e0452291b6dba600495b8e38dcb020491a71c91b",
19 "da2aac1ddbb00036e46e26dcec6039138db923822047137248e3560048e38dc8e3",
20 "d929ab2a9b5240395b6dc72ba020469c8d551c5440349c9148e36a4036a372471b",
21 "d9eb93215bb8a0271c69c724682036db71c71a94a0372491b72bee4044eb71b923",
22 "d9ab9aa19abc40391b6e5ae2ee40471b91c6dbe820492291b8e4b84036e47238db",
23 "d96b9be9db782044e371b55cb200389491c69b8ea034e271c8d3808038ec6db8e3",
24 "d9aa9365e3f060375c6db6ebc4c02764b1c51b78a0571c91a723de6049248dc8dd",
25 "BAD",
26 "d9ea9c219ce60046e38d3724e0c034e56e36eb7e0038d471b8dcb260491b8dbb23",
27 "d9e89be9d9e0a0391b6dd6a4624029247138e3a2a04713922524de0036db69d91c",
28 "d9699422a2b6a048dd90c91c6a802b6259395c8880575b4a58e4ac20269d7248d4",
29 "d967ac5b1baae0371c71b8ab9c804a9e8e58a55a8038626ec8dcb640395c7244dc",
30 "d9e8a3e262e68027638db52b88a038634e471a7ec049136e3b1bc8402923adcad2",
31 "d8eab36e1bbe0046e34d491b608035137658d3524044e48e375cdac0472b9238d4",
32 "d9689ba5e3d260491b516adb5e4027256e27227ee0351c8e549a5c60492471971b",
33 "BAD",
34 "BAD",
35 "d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab",
36 "d9a87ba1a3982048eb8a471cac00472b4e391bbc40292489b71cc200495b8d3ae3",
37 "d9278b2a1ba4c0475b8dc722d6e0491b5228da70204ae36dc71d94a056a29236e3",
38 "d9ec9be2129520392335598c50c04b5bad3d4ba680789b69df5a5aa0469cd1b4da",
39 "d8ea932623e660669b8e4a9dd8a03aa32a76e466e028d396cc9bbe4047256dc8e5",
40 "d96a94215aa0403aab713f22e8e024e68db91ab6a027abd1a55b6e804aec9146e4",
41 "d867ac21e270a0350d6ac91a724037247246d2a6c0396c89d6dc562049244e48d5",
42 "d8a9b460d3b48026a4ad471b7c20452491b69bbc803ae48db722ee00292491a8db",
43 "d928a3e1d3b24036e37244abf02047634d371b74c047637148a29ac03b234e38e3",
44 "d9ab9b21d2e0c0471c693aec54e044dbae46dc7c20391badb724ee8038e469bb15",
45 "d9a99361a276403b1a6ad6dcd40026e489c8e3bc40371c4dc564e2c036e28eb963",
46 "BAD",
47 "BAD",
48 "BAD",
49 "BAD",
50 "BAD",
51 "BAD",
52 "d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25",
53 "d8e78b29e3c6c038dba9d91beca04723ad491cda80471471b6ec7ae03b1396b91b",
54 "d8a78b25e37a0022dd8a46dc68a0351bad391bde2046e56dd8dc96c038e396d89b",
55 "d8a88c255ab6e038e38e48dbde8038ad8dc8db8ec0376372b564b44038e49234dc",
56 "d9708ce6a39ce049646646a2c1a0272496b29a66c037db562863ace0795b55b2e3",
57 "d8ee9bea5ae4003ae371b713eae05adc91995a5ea064dcc9571e786026ed51c52c",
58 "d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763",
59 "d92aab696190c046e26e392cae0026a376a8dc662048d291b75b54c04ad3ae3b1b",
60 "d8e7a469627a6038e289cb1baca0569b8db6dddec026dc8e38e5dc803722722d23",
61 "d8a88c299b64c03a548a58e37420272c6dd76b92c0471c9236dbc0e0551c71c713",
62 "BAD",
63 "d7299c19a3be8024e58ea7a49f20a522963ad976e0a76ecd92b38500cb62aa4c94",
64 "d7eb6c6262eee02b2c42e79a60a0aa55aed68a7f00ad358e10fad960e55a39396d",
65 "d970858dd2ab61d91355ebc15ca1a6a7ca48a05cc0dae66f2523c2a1bad3825daa",
66 "d8f0844a23ad20da50d6de025e81c37392b9039cc0c764c1bd1e94c1b699736a98",
67 "d9708ce6a39ce049646646a2c1a0272496b29a66c037db562863ace0795b55b2e3",
68 "d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763",
69 "d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763",
70 "d9299421d2944036ed69b8e572a048e36d551cd480571d4ec95be680356c69c763",
71 "d2577a1cda50004924924924500049249249245000492492492450004923924924",
72 NULL
73};
74
75/* Example of a good frame */
Pau Espin Pedrol421068e2018-07-20 19:12:18 +020076static const char *sample_frame_hex[] = {
77 "d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da",
78 "d9ec9be212901d802335598c5013805bad3d4ba01f809b69df5a5019809cd1b4da",
79 NULL
80};
Philipp Maier40def492017-12-16 03:42:15 +070081
Pau Espin Pedrolbbccca62018-07-20 18:58:19 +020082#define GSM610_XMAXC_LEN 6
83static void parse_xmaxc_frame(uint8_t *frame, uint64_t xmaxc_res[4])
84{
85 unsigned int field_index, len;
86
87 struct bitvec *frame_bitvec = bitvec_alloc(GSM_FR_BYTES, NULL);
88 OSMO_ASSERT(frame_bitvec);
89 len = bitvec_unpack(frame_bitvec, frame);
90 OSMO_ASSERT(len == GSM_FR_BYTES);
91
92 field_index = GSM610_RTP_XMAXC00;
93 xmaxc_res[0] = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
94 field_index = GSM610_RTP_XMAXC10;
95 xmaxc_res[1] = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
96 field_index = GSM610_RTP_XMAXC20;
97 xmaxc_res[2] = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
98 field_index = GSM610_RTP_XMAXC30;
99 xmaxc_res[3] = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
100
101 bitvec_free(frame_bitvec);
102}
103
Philipp Maier40def492017-12-16 03:42:15 +0700104/**
105 * Start with a good voice frame and then simulate 20 consecutive bad frames,
106 * watching how the error concealment decreases the XMAXC parameters.
107 */
108void test_fr_concealment(void)
109{
110 struct osmo_ecu_fr_state state;
111 uint8_t frame[GSM_FR_BYTES];
Pau Espin Pedrolbbccca62018-07-20 18:58:19 +0200112 uint64_t xmaxc[4];
Philipp Maier40def492017-12-16 03:42:15 +0700113 int i, rc;
Pau Espin Pedrol421068e2018-07-20 19:12:18 +0200114 int j = 0;
Philipp Maier40def492017-12-16 03:42:15 +0700115
Harald Welte750d8312019-08-01 20:05:05 +0200116 printf("=> Testing FR concealment (simple, consecutive bad frames)\n");
117
Pau Espin Pedrol421068e2018-07-20 19:12:18 +0200118 while (sample_frame_hex[j] != NULL) {
119 /* Parse frame from string to hex */
120 osmo_hexparse(sample_frame_hex[j], frame, GSM_FR_BYTES);
Pau Espin Pedrolbbccca62018-07-20 18:58:19 +0200121 parse_xmaxc_frame(frame, xmaxc);
Pau Espin Pedrol421068e2018-07-20 19:12:18 +0200122 printf("Start with: %s, XMAXC: [%"PRIx64", %"PRIx64", %"PRIx64", %"PRIx64"]\n",
123 sample_frame_hex[j], xmaxc[0], xmaxc[1], xmaxc[2], xmaxc[3]);
Philipp Maier40def492017-12-16 03:42:15 +0700124
Pau Espin Pedrol421068e2018-07-20 19:12:18 +0200125 /* Reset the ECU with the proposed known good frame */
126 osmo_ecu_fr_reset(&state, frame);
127
128 /* Now pretend that we do not receive any good frames anymore */
129 for (i = 0; i < 20; i++) {
130
131 rc = osmo_ecu_fr_conceal(&state, frame);
132 OSMO_ASSERT(rc == 0);
133 parse_xmaxc_frame(frame, xmaxc);
134
135 printf("conceal: %02i, result: %s XMAXC: [%"PRIx64", %"PRIx64", %"PRIx64", %"PRIx64"]\n",
136 i, osmo_hexdump_nospc(frame, GSM_FR_BYTES),
137 xmaxc[0], xmaxc[1], xmaxc[2], xmaxc[3]);
138 }
139
140 /* Go to the next frame */
141 j++;
Philipp Maier40def492017-12-16 03:42:15 +0700142 }
143}
144
Philipp Maierae140bc2019-09-19 10:34:11 +0200145/* Same as test_fr_concealment() but using generic core */
146void test_fr_concealment_core(void)
147{
148 struct osmo_ecu_state *state = osmo_ecu_init(NULL, OSMO_ECU_CODEC_FR);
149 uint8_t frame[GSM_FR_BYTES];
150 uint64_t xmaxc[4];
151 int i, rc;
152 int j = 0;
153
Mychaela N. Falconia5eb356b2023-05-12 17:21:25 +0000154 printf("=> Testing FR concealment (simple, using ECU abstraction)\n");
Philipp Maierae140bc2019-09-19 10:34:11 +0200155
156 while (sample_frame_hex[j] != NULL) {
157 /* Parse frame from string to hex */
158 osmo_hexparse(sample_frame_hex[j], frame, GSM_FR_BYTES);
159 parse_xmaxc_frame(frame, xmaxc);
160 printf("Start with: %s, XMAXC: [%"PRIx64", %"PRIx64", %"PRIx64", %"PRIx64"]\n",
161 sample_frame_hex[j], xmaxc[0], xmaxc[1], xmaxc[2], xmaxc[3]);
162
163 /* Reset the ECU with the proposed known good frame */
164 osmo_ecu_frame_in(state, false, frame, GSM_FR_BYTES);
165
166 /* Now pretend that we do not receive any good frames anymore */
167 for (i = 0; i < 20; i++) {
168
169 rc = osmo_ecu_frame_out(state, frame);
170 OSMO_ASSERT(rc == GSM_FR_BYTES);
171 parse_xmaxc_frame(frame, xmaxc);
172
173 printf("conceal: %02i, result: %s XMAXC: [%"PRIx64", %"PRIx64", %"PRIx64", %"PRIx64"]\n",
174 i, osmo_hexdump_nospc(frame, GSM_FR_BYTES),
175 xmaxc[0], xmaxc[1], xmaxc[2], xmaxc[3]);
176 }
177
178 /* Go to the next frame */
179 j++;
180 }
181
182 osmo_ecu_destroy(state);
183}
184
Philipp Maier40def492017-12-16 03:42:15 +0700185/* Simulate a real life situation: voice frames with a few dropouts */
Harald Weltee61d4592022-11-03 11:05:58 +0100186void test_fr_concealment_realistic(void)
Philipp Maier40def492017-12-16 03:42:15 +0700187{
188 struct osmo_ecu_fr_state state;
189 uint8_t frame[GSM_FR_BYTES];
190 unsigned int frame_len;
191 int rc, i = 0;
192
Harald Welte750d8312019-08-01 20:05:05 +0200193 printf("\n=> Testing FR concealment (realistic, various bad frames)\n");
194
Philipp Maier40def492017-12-16 03:42:15 +0700195 while (fr_frames_hex[i] != NULL) {
196 /* Debug print */
197 printf("Frame No. %03i:\n", i);
198
199 /* Good or bad frame? */
200 frame_len = strlen(fr_frames_hex[i]) / 2;
201 if (frame_len == GSM_FR_BYTES) {
202 printf(" * input: %s\n", fr_frames_hex[i]);
203 osmo_hexparse(fr_frames_hex[i], frame, GSM_FR_BYTES);
204 osmo_ecu_fr_reset(&state, frame);
205 } else {
206 printf(" * input: (bad)\n");
207 memset(frame, 0x00, GSM_FR_BYTES);
208 rc = osmo_ecu_fr_conceal(&state, frame);
209 OSMO_ASSERT(rc == 0);
210 }
211
212 /* Print result */
213 printf(" * output: %s\n",
214 osmo_hexdump_nospc(frame, GSM_FR_BYTES));
215
216 /* Go to the next frame */
217 i++;
218 }
219}
220
Harald Welte750d8312019-08-01 20:05:05 +0200221/* Simulate a real life situation: voice frames with a few dropouts, using generic core */
Harald Weltee61d4592022-11-03 11:05:58 +0100222void test_fr_concealment_realistic_core(void)
Harald Welte750d8312019-08-01 20:05:05 +0200223{
224 struct osmo_ecu_state *state = osmo_ecu_init(NULL, OSMO_ECU_CODEC_FR);
225 uint8_t frame[GSM_FR_BYTES];
226 unsigned int frame_len;
227 int rc, i = 0;
228
229 printf("\n=> Testing FR concealment (realistic, using ECU abstraction)\n");
230
231 OSMO_ASSERT(state);
232
233 while (fr_frames_hex[i] != NULL) {
234 /* Debug print */
235 printf("Frame No. %03i:\n", i);
236
237 /* Good or bad frame? */
238 frame_len = strlen(fr_frames_hex[i]) / 2;
239 if (frame_len == GSM_FR_BYTES) {
240 printf(" * input: %s\n", fr_frames_hex[i]);
241 osmo_hexparse(fr_frames_hex[i], frame, GSM_FR_BYTES);
242 osmo_ecu_frame_in(state, false, frame, GSM_FR_BYTES);
243 } else {
244 printf(" * input: (bad)\n");
245 memset(frame, 0x00, GSM_FR_BYTES);
246 osmo_ecu_frame_in(state, true, frame, 0);
247 rc = osmo_ecu_frame_out(state, frame);
248 OSMO_ASSERT(rc == GSM_FR_BYTES);
249 }
250
251 /* Print result */
252 printf(" * output: %s\n",
253 osmo_hexdump_nospc(frame, GSM_FR_BYTES));
254
255 /* Go to the next frame */
256 i++;
257 }
258
259 osmo_ecu_destroy(state);
260}
261
262
Philipp Maier40def492017-12-16 03:42:15 +0700263int main(int argc, char **argv)
264{
265 /* Perform actual tests */
266 test_fr_concealment();
Philipp Maierae140bc2019-09-19 10:34:11 +0200267 test_fr_concealment_core();
Philipp Maier40def492017-12-16 03:42:15 +0700268 test_fr_concealment_realistic();
Harald Welte750d8312019-08-01 20:05:05 +0200269 test_fr_concealment_realistic_core();
Philipp Maier40def492017-12-16 03:42:15 +0700270
271 return 0;
272}