Compare commits
23 Commits
pre-commit
...
data_recor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3810e0a8e9 | ||
|
|
d737ccf7fe | ||
|
|
f9d3e0bf6b | ||
|
|
e3058d91bd | ||
|
|
8b90d7594f | ||
|
|
5845393359 | ||
|
|
3a853735bd | ||
|
|
daf8a45c40 | ||
|
|
b0d57a1670 | ||
|
|
52bba03584 | ||
|
|
d4c4f08de9 | ||
|
|
e9e2cac954 | ||
|
|
269e5ca3d5 | ||
|
|
b3ffc2d926 | ||
|
|
42104a9158 | ||
|
|
3f2d8a6815 | ||
|
|
09a7219b90 | ||
|
|
a97af9bf20 | ||
|
|
388c4b4df0 | ||
|
|
97480b474b | ||
|
|
94537d6aaf | ||
|
|
a746c6d742 | ||
|
|
dead291da0 |
@@ -821,6 +821,7 @@ set(PHY_SRC_COMMON
|
||||
${OPENAIR1_DIR}/PHY/TOOLS/sqrt.c
|
||||
${OPENAIR1_DIR}/PHY/TOOLS/lut.c
|
||||
${OPENAIR1_DIR}/PHY/TOOLS/simde_operations.c
|
||||
${OPENAIR1_DIR}/PHY/log_tools.c
|
||||
)
|
||||
|
||||
set(PHY_SRC
|
||||
|
||||
@@ -100,12 +100,14 @@ struct T_header;
|
||||
|
||||
/* T macro tricks */
|
||||
extern int T_stdout;
|
||||
#define TN(...) TN_N(__VA_ARGS__,39,38,37,36,35,34,33,32,31,30,29,28,27,26, \
|
||||
25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7, \
|
||||
6,5,4,3,2,1,0)(__VA_ARGS__)
|
||||
#define TN_N(n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12,n13,n14,n15,n16,n17, \
|
||||
n18,n19,n20,n21,n22,n23,n24,n25,n26,n27,n28,n29,n30,n31,n32, \
|
||||
n33,n34,n35,n36,n37,n38,n,...) T##n
|
||||
#define TN(...) TN_N(__VA_ARGS__,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,\
|
||||
56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,\
|
||||
37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,\
|
||||
18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)(__VA_ARGS__)
|
||||
#define TN_N(n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12,n13,n14,n15,n16,n17,n18,n19,n20,\
|
||||
n21,n22,n23,n24,n25,n26,n27,n28,n29,n30,n31,n32,n33,n34,n35,n36,n37,n38,\
|
||||
n39,n40,n41,n42,n43,n44,n45,n46,n47,n48,n49,n50,n51,n52,n53,n54,n55,n56,\
|
||||
n57,n58,n59,n60,n61,n62,n63,n64,n65,n66,n67,n68,n69,n70,n,...) T##n
|
||||
#define T(...) do { if (T_stdout == 0 || T_stdout == 2) TN(__VA_ARGS__); } while (0)
|
||||
|
||||
/* type used to send arbitrary buffer data */
|
||||
@@ -620,6 +622,633 @@ extern int *T_active;
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define T41(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T43(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T45(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T47(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T49(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T51(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T53(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T55(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T57(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26,t27,x27) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_PUT_##t27(29, x27); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T59(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26,t27,x27,t28,x28) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_PUT_##t27(29, x27); \
|
||||
T_PUT_##t28(30, x28); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T61(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26,t27,x27,t28,x28,t29,x29) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_PUT_##t27(29, x27); \
|
||||
T_PUT_##t28(30, x28); \
|
||||
T_PUT_##t29(31, x29); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T63(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26,t27,x27,t28,x28,t29,x29,t30,x30) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_PUT_##t27(29, x27); \
|
||||
T_PUT_##t28(30, x28); \
|
||||
T_PUT_##t29(31, x29); \
|
||||
T_PUT_##t30(32, x30); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T65(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26,t27,x27,t28,x28,t29,x29,t30,x30,t31,x31) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_PUT_##t27(29, x27); \
|
||||
T_PUT_##t28(30, x28); \
|
||||
T_PUT_##t29(31, x29); \
|
||||
T_PUT_##t30(32, x30); \
|
||||
T_PUT_##t31(33, x31); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T67(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26,t27,x27,t28,x28,t29,x29,t30,x30,t31,x31,t32,x32) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_PUT_##t27(29, x27); \
|
||||
T_PUT_##t28(30, x28); \
|
||||
T_PUT_##t29(31, x29); \
|
||||
T_PUT_##t30(32, x30); \
|
||||
T_PUT_##t31(33, x31); \
|
||||
T_PUT_##t32(34, x32); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T69(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26,t27,x27,t28,x28,t29,x29,t30,x30,t31,x31,t32,x32,t33,x33) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_PUT_##t27(29, x27); \
|
||||
T_PUT_##t28(30, x28); \
|
||||
T_PUT_##t29(31, x29); \
|
||||
T_PUT_##t30(32, x30); \
|
||||
T_PUT_##t31(33, x31); \
|
||||
T_PUT_##t32(34, x32); \
|
||||
T_PUT_##t33(35, x33); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define T71(t,t0,x0,t1,x1,t2,x2,t3,x3,t4,x4,t5,x5,t6,x6,t7,x7,t8,x8,t9,x9,\
|
||||
t10,x10,t11,x11,t12,x12,t13,x13,t14,x14,t15,x15,t16,x16,t17,x17,\
|
||||
t18,x18,t19,x19,t20,x20,t21,x21,t22,x22,t23,x23,t24,x24,t25,x25,\
|
||||
t26,x26,t27,x27,t28,x28,t29,x29,t30,x30,t31,x31,t32,x32,t33,x33,t34,x34) \
|
||||
do { \
|
||||
if (T_ACTIVE(t)) { \
|
||||
T_LOCAL_DATA \
|
||||
T_HEADER(t); \
|
||||
T_PUT_##t0(2, x0); \
|
||||
T_PUT_##t1(3, x1); \
|
||||
T_PUT_##t2(4, x2); \
|
||||
T_PUT_##t3(5, x3); \
|
||||
T_PUT_##t4(6, x4); \
|
||||
T_PUT_##t5(7, x5); \
|
||||
T_PUT_##t6(8, x6); \
|
||||
T_PUT_##t7(9, x7); \
|
||||
T_PUT_##t8(10, x8); \
|
||||
T_PUT_##t9(11, x9); \
|
||||
T_PUT_##t10(12, x10); \
|
||||
T_PUT_##t11(13, x11); \
|
||||
T_PUT_##t12(14, x12); \
|
||||
T_PUT_##t13(15, x13); \
|
||||
T_PUT_##t14(16, x14); \
|
||||
T_PUT_##t15(17, x15); \
|
||||
T_PUT_##t16(18, x16); \
|
||||
T_PUT_##t17(19, x17); \
|
||||
T_PUT_##t18(20, x18); \
|
||||
T_PUT_##t19(21, x19); \
|
||||
T_PUT_##t20(22, x20); \
|
||||
T_PUT_##t21(23, x21); \
|
||||
T_PUT_##t22(24, x22); \
|
||||
T_PUT_##t23(25, x23); \
|
||||
T_PUT_##t24(26, x24); \
|
||||
T_PUT_##t25(27, x25); \
|
||||
T_PUT_##t26(28, x26); \
|
||||
T_PUT_##t27(29, x27); \
|
||||
T_PUT_##t28(30, x28); \
|
||||
T_PUT_##t29(31, x29); \
|
||||
T_PUT_##t30(32, x30); \
|
||||
T_PUT_##t31(33, x31); \
|
||||
T_PUT_##t32(34, x32); \
|
||||
T_PUT_##t33(35, x33); \
|
||||
T_PUT_##t34(36, x34); \
|
||||
T_COMMIT(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define T_CALL_ERROR \
|
||||
do { \
|
||||
printf("%s:%d:%s: error calling T, you have to use T_INT() or T_XX()\n", \
|
||||
@@ -645,6 +1274,22 @@ extern int *T_active;
|
||||
#define T34(...) T_CALL_ERROR
|
||||
#define T36(...) T_CALL_ERROR
|
||||
#define T38(...) T_CALL_ERROR
|
||||
#define T40(...) T_CALL_ERROR
|
||||
#define T42(...) T_CALL_ERROR
|
||||
#define T44(...) T_CALL_ERROR
|
||||
#define T46(...) T_CALL_ERROR
|
||||
#define T48(...) T_CALL_ERROR
|
||||
#define T50(...) T_CALL_ERROR
|
||||
#define T52(...) T_CALL_ERROR
|
||||
#define T54(...) T_CALL_ERROR
|
||||
#define T56(...) T_CALL_ERROR
|
||||
#define T58(...) T_CALL_ERROR
|
||||
#define T60(...) T_CALL_ERROR
|
||||
#define T62(...) T_CALL_ERROR
|
||||
#define T64(...) T_CALL_ERROR
|
||||
#define T66(...) T_CALL_ERROR
|
||||
#define T68(...) T_CALL_ERROR
|
||||
#define T70(...) T_CALL_ERROR
|
||||
|
||||
/* special cases for VCD logs */
|
||||
|
||||
|
||||
@@ -111,6 +111,26 @@ ID = GNB_PHY_PUCCH_PUSCH_IQ
|
||||
DESC = gNodeB input data in the frequency domain for a slot where some PUCCH or PUSCH detection was done
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
|
||||
FORMAT = int,frame : int,slot : buffer,rxdataF
|
||||
ID = GNB_PHY_UL_FD_PUSCH_IQ
|
||||
DESC = gNodeB UL PUSCH IQ Data in the frequency domain
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
|
||||
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
ID = GNB_PHY_UL_FD_DMRS
|
||||
DESC = gNodeB UL DMRS Signal in the frequency domain
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
|
||||
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
ID = GNB_PHY_UL_FD_CHAN_EST_DMRS_POS
|
||||
DESC = gNodeB channel estimation in the frequency domain based on DMRS and at DMRS pilots location without any further interploation.
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
|
||||
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
ID = GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL
|
||||
DESC = gNodeB channel estimation in the frequency domain based on DMRS and interploation.
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
|
||||
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,dataI
|
||||
ID = GNB_PHY_UL_PAYLOAD_RX_BITS
|
||||
DESC = gNodeB slot payload after decoder
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
|
||||
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
ID = GNB_PHY_UL_FREQ_CHANNEL_ESTIMATE
|
||||
DESC = gNodeB channel estimation in the frequency domain
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
|
||||
@@ -1352,6 +1372,14 @@ ID = UE_PHY_MEAS
|
||||
DESC = UE PHY measurements
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:UE
|
||||
FORMAT = int,eNB_ID : int,frame : int,subframe : int,rsrp : int,rssi : int,snr: int,rx_power: int,noise_power: int,w_cqi: int,freq_offset
|
||||
ID = UE_PHY_UL_PAYLOAD_TX_BITS
|
||||
DESC = UE UL Tx bits in one slot before decoder and scrambler
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:UE
|
||||
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_tx : int,number_of_bits : buffer,data
|
||||
ID = UE_PHY_UL_SCRAMBLED_TX_BITS
|
||||
DESC = UE UL Tx bits in one slot after scrambler
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:UE
|
||||
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_tx : int,number_of_bits : buffer,data
|
||||
|
||||
#for debug/test - not used
|
||||
ID = first
|
||||
|
||||
@@ -8,7 +8,7 @@ XLIBS=-lX11 -lpng -lXft
|
||||
|
||||
all: record replay extract_config textlog enb ue vcd macpdu2wireshark \
|
||||
extract_input_subframe extract_output_subframe to_vcd extract multi \
|
||||
gnb
|
||||
gnb t_tracer_app_gnb t_tracer_app_ue
|
||||
|
||||
record: utils.o record.o database.o configuration.o
|
||||
$(CC) $(CFLAGS) -o record $^ $(LIBS)
|
||||
@@ -65,6 +65,14 @@ gnb: utils.o gnb.o database.o event.o handler.o configuration.o \
|
||||
filter/filter.a
|
||||
$(CC) $(CFLAGS) -o gnb $^ $(LIBS) $(XLIBS)
|
||||
|
||||
t_tracer_app_gnb: utils.o t_tracer_app_gnb.o database.o event.o handler.o configuration.o \
|
||||
logger/logger.a filter/filter.a
|
||||
$(CC) $(CFLAGS) -o t_tracer_app_gnb $^ $(LIBS) $(XLIBS)
|
||||
|
||||
t_tracer_app_ue: utils.o t_tracer_app_ue.o database.o event.o handler.o configuration.o \
|
||||
logger/logger.a filter/filter.a
|
||||
$(CC) $(CFLAGS) -o t_tracer_app_ue $^ $(LIBS) $(XLIBS)
|
||||
|
||||
multi.o: ../incgen/T_IDs.h
|
||||
|
||||
../incgen/T_IDs.h: ../T_messages.txt
|
||||
@@ -90,7 +98,7 @@ filter/filter.a:
|
||||
clean:
|
||||
rm -f *.o core tracer_remote textlog enb ue vcd record replay
|
||||
rm -f extract_config macpdu2wireshark extract_input_subframe
|
||||
rm -f extract_output_subframe to_vcd extract multi gnb
|
||||
rm -f extract_output_subframe to_vcd extract multi gnb t_tracer_app_gnb t_tracer_app_ue
|
||||
cd gui && $(MAKE) clean
|
||||
cd view && $(MAKE) clean
|
||||
cd logger && $(MAKE) clean
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#endif
|
||||
|
||||
/* maximum number of arguments for the T macro */
|
||||
#define T_MAX_ARGS 32
|
||||
#define T_MAX_ARGS 72
|
||||
|
||||
enum event_arg_type {
|
||||
EVENT_INT,
|
||||
|
||||
64
common/utils/T/tracer/shared_memory_config.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file common/utils/T/tracer/shared_memory_config.h
|
||||
* \brief shared memory to store data captured by T-Tracer services to be processed by data recording application
|
||||
* \author Abdo Gaber
|
||||
* \date 2024
|
||||
* \version 1.0
|
||||
* \company Emerson, NI Test and Measurement
|
||||
* \email:
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SHARED_MEMORY_CONFIG_H_
|
||||
#define SHARED_MEMORY_CONFIG_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define SHMSIZE ((122.88e6/(100*20)) * 1000*8) // Assume capture in 1000, each I+Q represented by 8 byts
|
||||
// 122.88e6: 10ms at 100 MHz BW of 5G NR
|
||||
// 20: 20 slots in 10ms
|
||||
// subcarrier spacing: 30 KHz
|
||||
|
||||
// for gNB T Tracer App
|
||||
#define GETKEYDIR1_gNB ("/tmp/gnb_app1")
|
||||
#define GETKEYDIR2_gNB ("/tmp/gnb_app2")
|
||||
#define PROJECTID_gNB (2335)
|
||||
|
||||
// for UET Tracer App
|
||||
#define GETKEYDIR1_UE ("/tmp/ue_app1")
|
||||
#define GETKEYDIR2_UE ("/tmp/ue_app2")
|
||||
#define PROJECTID_UE (2336)
|
||||
|
||||
void err_exit(char *buf);
|
||||
int create_shm(char **addrN, const char *pathname, int projectId);
|
||||
void del_shm(char *addr, int shm_id);
|
||||
#endif /* SHARED_MEMORY_CONFIG_H_ */
|
||||
951
common/utils/T/tracer/t_tracer_app_gnb.c
Normal file
@@ -0,0 +1,951 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file common/utils/T/tracer/t_tracer_app_gnb.c
|
||||
* \brief T-Tracer gnb service to capture tracee Messages from gNB, it is used by data recording application
|
||||
* \author Abdo Gaber
|
||||
* \date 2025
|
||||
* \version 1.0
|
||||
* \company Emerson, NI Test and Measurement
|
||||
* \email:
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include "database.h"
|
||||
#include "event.h"
|
||||
#include "handler.h"
|
||||
#include "logger/logger.h"
|
||||
#include "utils.h"
|
||||
#include "event_selector.h"
|
||||
#include "configuration.h"
|
||||
#include "shared_memory_config.h"
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <poll.h>
|
||||
#define MAX_FRAME_INDEX 1023
|
||||
#define MAX_SLOT_INDEX 19
|
||||
#define DEBUG_T_Tracer
|
||||
#define DEBUG_BUFFER
|
||||
// Duration to discard recording in milliseconds to mitigate reading data from
|
||||
// the buffer in the stack from the previous record
|
||||
#define DISCARD_RECORD_DURATION_MS 10
|
||||
|
||||
// Combine bytes in - little-endian format
|
||||
int combine_bytes(const uint8_t *bytes, size_t num_bytes)
|
||||
{
|
||||
int result = 0;
|
||||
for (size_t i = 0; i < num_bytes; ++i) {
|
||||
result |= (bytes[i] << (i * 8));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert an integer to an array of bytes - little-endian format
|
||||
void int_to_bytes(int num, uint8_t *bytes, size_t num_bytes)
|
||||
{
|
||||
for (size_t i = 0; i < num_bytes; ++i) {
|
||||
bytes[i] = (num >> (i * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the message is in the list of bits messages
|
||||
bool is_bits_messages(int traces_bits_support_data_Collection_format_idx[], int n_bits_msgs, int msg_id)
|
||||
{
|
||||
for (int i = 0; i < n_bits_msgs; i++) {
|
||||
if (msg_id == traces_bits_support_data_Collection_format_idx[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the current time
|
||||
struct timespec get_current_time()
|
||||
{
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
||||
return time;
|
||||
}
|
||||
|
||||
// Function to convert timespec to microseconds
|
||||
long long timespec_to_microseconds(struct timespec time)
|
||||
{
|
||||
return (time.tv_sec * 1000000LL) + (time.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
// Calculate the time difference in milliseconds
|
||||
double calculate_time_difference(struct timespec start, struct timespec end)
|
||||
{
|
||||
double start_ms = start.tv_sec * 1000.0 + start.tv_nsec / 1000000.0;
|
||||
double end_ms = end.tv_sec * 1000.0 + end.tv_nsec / 1000000.0;
|
||||
return end_ms - start_ms;
|
||||
}
|
||||
|
||||
// Get Time Stamp in microseconds in YYYYMMDDHHMMSSmmmuuu format
|
||||
char* get_time_stamp_usec(char time_stamp_str[])
|
||||
{
|
||||
// initialization to measure time stamp --This part should be moved to inilization part
|
||||
time_t my_time;
|
||||
struct tm * timeinfo;
|
||||
time (&my_time);
|
||||
struct timeval tv;
|
||||
|
||||
// get time stamp
|
||||
timeinfo = localtime (&my_time);
|
||||
gettimeofday(&tv,NULL);
|
||||
// Add time stamp: yyyy mm dd hh mm ss usec
|
||||
uint16_t year= timeinfo->tm_year+1900;
|
||||
uint8_t mon= timeinfo->tm_mon+1;
|
||||
uint8_t mday= timeinfo->tm_mday;
|
||||
uint8_t hour= timeinfo->tm_hour;
|
||||
uint8_t min= timeinfo->tm_min;
|
||||
uint8_t sec= timeinfo->tm_sec;
|
||||
uint16_t usec= (tv.tv_usec);
|
||||
//printf ("Time stamp: %d_%d_%d_%d_%d_%d_%d \n",year,mon,mday,hour,min,sec,usec);
|
||||
//sprintf(time_stamp_str, "%d_%d_%d_%d_%d_%d_%d",year,mon,mday,hour,min,sec,usec);
|
||||
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06d", year, mon, mday, hour, min, sec, usec);
|
||||
|
||||
return time_stamp_str;
|
||||
}
|
||||
|
||||
// Convert timestamp string to integer
|
||||
int convert_time_stamp_to_int(const char* timestamp)
|
||||
{
|
||||
return atoi(timestamp);
|
||||
}
|
||||
|
||||
// Split timestamp string and convert to integer
|
||||
int split_time_stamp_and_convert_to_int (char time_stamp_str[], int shift, int length)
|
||||
{
|
||||
char time_part[length+1]; // Buffer to hold the date part YYYYMMDD or HHMMSSmmm
|
||||
// Copy the first 8 or 9 characters (YYYYMMDD) to HHMMSSmmm
|
||||
strncpy(time_part, time_stamp_str + shift, length);
|
||||
time_part[length] = '\0'; // Null-terminate the string
|
||||
// Convert timestamp string to integer
|
||||
return convert_time_stamp_to_int(time_part);
|
||||
}
|
||||
|
||||
void err_exit(char *buf)
|
||||
{
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// create shared memory
|
||||
int create_shm(char **addrN, const char *shm_path, int projectId)
|
||||
{
|
||||
key_t key;
|
||||
if (-1 != open(shm_path, O_CREAT, 0777)) {
|
||||
key = ftok(shm_path, projectId);
|
||||
} else {
|
||||
err_exit("Error: open shared memory");
|
||||
}
|
||||
|
||||
if ( key < 0 ) {
|
||||
err_exit("Error: ftok error");
|
||||
}
|
||||
|
||||
int shm_id;
|
||||
shm_id = shmget(key, SHMSIZE, IPC_CREAT | IPC_EXCL | 0664);
|
||||
if ( shm_id == -1 ) {
|
||||
if ( errno == EEXIST ) {
|
||||
printf("Error: shared memeory already exist\n");
|
||||
shm_id = shmget(key ,0, 0);
|
||||
printf("reference shm_id = %d\n", shm_id);
|
||||
} else {
|
||||
perror("errno");
|
||||
err_exit("shmget error");
|
||||
}
|
||||
}
|
||||
char *addr;
|
||||
// address to attach - attach for read & write
|
||||
if ( (addr = shmat(shm_id, 0, 0) ) == (void*)-1) {
|
||||
if (shmctl(shm_id, IPC_RMID, NULL) == -1)
|
||||
err_exit("Error: shmctl error");
|
||||
else {
|
||||
printf("Attach shared memory failed\n");
|
||||
printf("remove shared memory identifier successful\n");
|
||||
}
|
||||
err_exit("shmat error");
|
||||
}
|
||||
*addrN = addr;
|
||||
return shm_id;
|
||||
}
|
||||
|
||||
// delete shared memory
|
||||
void del_shm(char *addr, int shm_id)
|
||||
{
|
||||
if ( shmdt(addr) < 0) err_exit("shmdt error");
|
||||
|
||||
if (shmctl(shm_id, IPC_RMID, NULL) == -1)
|
||||
err_exit("shmctl error");
|
||||
else {
|
||||
printf("Remove shared memory identifier successful\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* this function sends the activated traces to the nr-softmodem */
|
||||
void activate_traces(int socket, int number_of_events, int *is_on)
|
||||
{
|
||||
char t = 1;
|
||||
if (socket_send(socket, &t, 1) == -1 ||
|
||||
socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
|
||||
socket_send(socket, is_on, number_of_events * sizeof(int)) == -1)
|
||||
abort();
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf(
|
||||
"options:\n"
|
||||
" -d <database file> this option is mandatory\n"
|
||||
" -ip <host> connect to given IP address (default %s)\n"
|
||||
" -p <port> connect to given port (default %d)\n",
|
||||
DEFAULT_REMOTE_IP,
|
||||
DEFAULT_REMOTE_PORT
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// class to store the message and the action to enable capture selected data
|
||||
// 0: do not record message
|
||||
// 1: record message
|
||||
struct trace_struct {
|
||||
char on_off_name[100];
|
||||
int on_off_action;
|
||||
};
|
||||
|
||||
// struct for trace message based on Data Collection Trace Messages Structure
|
||||
// you need to define the vararibles of each message
|
||||
typedef struct {
|
||||
/* Data Collection Trace Message Structure */
|
||||
int frame;
|
||||
int slot;
|
||||
int datetime_yyyymmdd;
|
||||
int datetime_hhmmssmmm;
|
||||
int frame_type, freq_range, subcarrier_spacing, cyclic_prefix, symbols_per_slot;
|
||||
int Nid_cell, rnti;
|
||||
int rb_size, rb_start, start_symbol_index,nr_of_symbols;
|
||||
int qam_mod_order,mcs_index,mcs_table,nrOfLayers,transform_precoding;
|
||||
int dmrs_config_type,ul_dmrs_symb_pos,number_dmrs_symbols;
|
||||
int dmrs_port,dmrs_nscid,nb_antennas_rx,number_of_bits;
|
||||
int data_size, data;
|
||||
} event_trace_msg_data;
|
||||
|
||||
void setup_trace_msg_data(event_trace_msg_data *d, void *database)
|
||||
{
|
||||
database_event_format f;
|
||||
int i;
|
||||
|
||||
/* Data Collection Trace Message Structure */
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
|
||||
// int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
|
||||
// Initialize the data structure
|
||||
d->frame = -1;
|
||||
d->slot = -1;
|
||||
d->datetime_yyyymmdd = -1;
|
||||
d->datetime_hhmmssmmm = -1;
|
||||
d->frame_type = -1;
|
||||
d->freq_range = -1;
|
||||
d->subcarrier_spacing = -1;
|
||||
d->cyclic_prefix = -1;
|
||||
d->symbols_per_slot = -1;
|
||||
d->Nid_cell = -1;
|
||||
d->rnti = -1;
|
||||
d->rb_size = -1;
|
||||
d->rb_start = -1;
|
||||
d->start_symbol_index = -1;
|
||||
d->nr_of_symbols = -1;
|
||||
d->qam_mod_order = -1;
|
||||
d->mcs_index = -1;
|
||||
d->mcs_table = -1;
|
||||
d->nrOfLayers = -1;
|
||||
d->transform_precoding = -1;
|
||||
d->dmrs_config_type = -1;
|
||||
d->ul_dmrs_symb_pos = -1;
|
||||
d->number_dmrs_symbols = -1;
|
||||
d->dmrs_port = -1;
|
||||
d->dmrs_nscid = -1;
|
||||
d->nb_antennas_rx = -1;
|
||||
d->number_of_bits = -1;
|
||||
d->data = -1;
|
||||
|
||||
/* this macro looks for a particular element and checks its type */
|
||||
#define G(var_name, var_type, var) \
|
||||
if (!strcmp(f.name[i], var_name)) { \
|
||||
if (strcmp(f.type[i], var_type)) { printf("Error: Trace Message has a bad type for %s\n", var_name); exit(1); } \
|
||||
var = i; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
/* ------------------------------*/
|
||||
/* Create Macro for Data Collection Trace Message */
|
||||
/* ------------------------------*/
|
||||
// Data Collection Trace Message Structure
|
||||
// Example: GNB_PHY_UL_FD_PUSCH_IQ, GNB_PHY_UL_FD_DMRS_ID,
|
||||
// GNB_PHY_UL_FD_CHAN_EST_DMRS_POS, GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL
|
||||
// GNB_PHY_UL_PAYLOAD_RX_BITS
|
||||
// UE_PHY_UL_SCRAMBLED_TX_BITS
|
||||
// UE_PHY_UL_PAYLOAD_TX_BITS
|
||||
// Get a template of any message based on Data Collection Trace Messages Structure
|
||||
int Trace_MSG_ID = event_id_from_name(database, "GNB_PHY_UL_FD_PUSCH_IQ");
|
||||
f = get_format(database, Trace_MSG_ID);
|
||||
|
||||
/* get the elements of the trace
|
||||
* the value is an index in the event, see below */
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
|
||||
// int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
|
||||
for (i = 0; i < f.count; i++) {
|
||||
G("frame", "int", d->frame)
|
||||
G("slot", "int", d->slot)
|
||||
G("datetime_yyyymmdd", "int", d->datetime_yyyymmdd)
|
||||
G("datetime_hhmmssmmm", "int", d->datetime_hhmmssmmm)
|
||||
G("frame_type", "int", d->frame_type)
|
||||
G("freq_range", "int", d->freq_range)
|
||||
G("subcarrier_spacing", "int", d->subcarrier_spacing)
|
||||
G("cyclic_prefix", "int", d->cyclic_prefix)
|
||||
G("symbols_per_slot", "int", d->symbols_per_slot)
|
||||
G("Nid_cell", "int", d->Nid_cell)
|
||||
G("rnti", "int", d->rnti)
|
||||
G("rb_size", "int", d->rb_size)
|
||||
G("rb_start", "int", d->rb_start)
|
||||
G("start_symbol_index", "int", d->start_symbol_index)
|
||||
G("nr_of_symbols", "int", d->nr_of_symbols)
|
||||
G("qam_mod_order", "int", d->qam_mod_order)
|
||||
G("mcs_index", "int", d->mcs_index)
|
||||
G("mcs_table", "int", d->mcs_table)
|
||||
G("nrOfLayers", "int", d->nrOfLayers)
|
||||
G("transform_precoding", "int", d->transform_precoding)
|
||||
G("dmrs_config_type", "int", d->dmrs_config_type)
|
||||
G("ul_dmrs_symb_pos", "int", d->ul_dmrs_symb_pos)
|
||||
G("number_dmrs_symbols", "int", d->number_dmrs_symbols)
|
||||
G("dmrs_port", "int", d->dmrs_port)
|
||||
G("dmrs_nscid", "int", d->dmrs_nscid)
|
||||
G("nb_antennas_rx", "int", d->nb_antennas_rx)
|
||||
G("number_of_bits", "int", d->number_of_bits)
|
||||
G("data", "buffer", d->data)
|
||||
}
|
||||
//if (d->frame == -1 || d->slot == -1) goto error;
|
||||
#undef G
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to check if a value is in the array
|
||||
int isValueInArray(int value, int arr[], int size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (arr[i] == value) {
|
||||
return 1; // Value found
|
||||
}
|
||||
}
|
||||
return 0; // Value not found
|
||||
}
|
||||
|
||||
void reestablish_connection(int *socket, const char *ip, int port, int number_of_events, int *is_on)
|
||||
{
|
||||
clear_remote_config();
|
||||
if (*socket != -1) close(*socket);
|
||||
|
||||
/* connect to the nr-softmodem */
|
||||
*socket = connect_to(ip, port);
|
||||
if (*socket == -1) {
|
||||
printf("\n Failed to connect to nr-softmodem. Retrying...\n");
|
||||
return;
|
||||
}
|
||||
printf("\n Connected");
|
||||
|
||||
/* activate the traces in the nr-softmodem */
|
||||
activate_traces(*socket, number_of_events, is_on);
|
||||
printf("\n Activated Traces in nr-softmodem");
|
||||
}
|
||||
|
||||
int main(int n, char **v)
|
||||
{
|
||||
// Define and initialize an array of strings to list
|
||||
// trace_msgs_support_data_Collection_format
|
||||
// it is used to parse the requested messages if it is based
|
||||
// on Data Collection Trace Messages Structure and supported tracer messages indices
|
||||
const char *traces_iq_support_data_Collection_format[] = {
|
||||
"GNB_PHY_UL_FD_PUSCH_IQ",
|
||||
"GNB_PHY_UL_FD_DMRS",
|
||||
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
|
||||
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL"
|
||||
};
|
||||
const char *traces_bits_support_data_Collection_format[] = {
|
||||
"GNB_PHY_UL_PAYLOAD_RX_BITS",
|
||||
"UE_PHY_UL_SCRAMBLED_TX_BITS",
|
||||
"UE_PHY_UL_PAYLOAD_TX_BITS"
|
||||
};
|
||||
// extra number of records to simlify Sync between base station and UE synchronization records.
|
||||
// if you have network delay, you can increase the number of records to capture
|
||||
int max_sync_offset = 6; // 6 frames ~ 60 ms
|
||||
// all supported messages
|
||||
// Calculate the size of the combined array
|
||||
int n_iq_msgs = sizeof(traces_iq_support_data_Collection_format) / sizeof(traces_iq_support_data_Collection_format[0]);
|
||||
int n_bits_msgs = sizeof(traces_bits_support_data_Collection_format) / sizeof(traces_bits_support_data_Collection_format[0]);
|
||||
int n_msgs_based_data_Collection_format = n_iq_msgs + n_bits_msgs;
|
||||
|
||||
// Create the combined array
|
||||
const char *traces_support_data_Collection_format[n_msgs_based_data_Collection_format];
|
||||
// Copy IQ messages to the combined array
|
||||
for (int i = 0; i < n_iq_msgs; i++) {
|
||||
traces_support_data_Collection_format[i] = traces_iq_support_data_Collection_format[i];
|
||||
}
|
||||
|
||||
// Copy Bits messages to the combined array
|
||||
for (int i = 0; i < n_bits_msgs; i++) {
|
||||
traces_support_data_Collection_format[i + n_iq_msgs] = traces_bits_support_data_Collection_format[i];
|
||||
}
|
||||
|
||||
uint16_t msg_id = 0;
|
||||
uint16_t start_frame_number = 0;
|
||||
uint32_t number_records = 0; // number of records to capture, it is number of slots
|
||||
// array to store the requested tracer messages indices
|
||||
int req_tracer_msgs_indices[100] = {0};
|
||||
// define variables --> to do: add all of them to be class of pointers
|
||||
char *database_filename = NULL;
|
||||
void *database;
|
||||
char *ip = DEFAULT_REMOTE_IP;
|
||||
char ip_address[16]; // max IP address length is 15 + 1 for null terminator
|
||||
int port = DEFAULT_REMOTE_PORT;
|
||||
int *is_on;
|
||||
int number_of_events;
|
||||
int i;
|
||||
int socket = -1;
|
||||
char trace_time_stamp_str[30];
|
||||
|
||||
// data structure for the trace messages based on Data Collection Trace Messages Structure
|
||||
event_trace_msg_data trace_msg_data;
|
||||
|
||||
// initlization variables
|
||||
unsigned int bufIdx_wr = 0;
|
||||
unsigned int bufIdx_rd = 0;
|
||||
uint8_t num_req_tracer_msgs = 0;
|
||||
|
||||
// initilaze shared memory
|
||||
char *addr_wr,*addr_rd;
|
||||
printf("\n Data Collection Service: Initializing shared memory ...");
|
||||
printf("\n Directory 1: %s, Directory 2: %s", GETKEYDIR1_gNB, GETKEYDIR2_gNB);
|
||||
printf("\n Project ID: %d\n", PROJECTID_gNB);
|
||||
int shm_id_wr = create_shm(&addr_wr, GETKEYDIR1_gNB, PROJECTID_gNB);
|
||||
int shm_id_rd = create_shm(&addr_rd, GETKEYDIR2_gNB, PROJECTID_gNB);
|
||||
del_shm(addr_wr, shm_id_wr);
|
||||
del_shm(addr_rd, shm_id_rd);
|
||||
shm_id_wr = create_shm(&addr_wr, GETKEYDIR1_gNB, PROJECTID_gNB);
|
||||
shm_id_rd = create_shm(&addr_rd, GETKEYDIR2_gNB, PROJECTID_gNB);
|
||||
|
||||
/* write on a socket fails if the other end is closed and we get SIGPIPE */
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
|
||||
|
||||
/* parse command line options */
|
||||
// Port number and IP address are given via API and not via command line
|
||||
for (i = 1; i < n; i++) {
|
||||
if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
|
||||
if (!strcmp(v[i], "-d"))
|
||||
{ if (i > n-2) usage(); database_filename = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-p"))
|
||||
{ if (i > n-2) usage(); port = atoi(v[++i]); continue; }
|
||||
usage();
|
||||
}
|
||||
|
||||
if (database_filename == NULL) {
|
||||
printf("ERROR: Provide a database file (-d)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* load the database T_messages.txt */
|
||||
database = parse_database(database_filename);
|
||||
load_config_file(database_filename);
|
||||
|
||||
/* an array of int for all the events defined in the database is needed */
|
||||
number_of_events = number_of_ids(database);
|
||||
is_on = calloc(number_of_events, sizeof(int));
|
||||
if (is_on == NULL) abort();
|
||||
|
||||
// Set first byte of the shared memory to 0
|
||||
addr_rd[0] = 0; // important to check if we have new requested messages
|
||||
|
||||
// read requested tracer msg indices from memory
|
||||
printf("\n Data Collection Service: Waiting for messages request ...\n");
|
||||
// 0: Wait
|
||||
// 1: config
|
||||
// 2: record
|
||||
// 3: stop
|
||||
// Wait for Action Config
|
||||
while (1) {
|
||||
if ((uint8_t)(addr_rd[0]) == 0){
|
||||
usleep(50); // sleep for 50 us: 0.5 ms slot duration
|
||||
}
|
||||
// config state
|
||||
else if ((uint8_t)(addr_rd[0]) == 1) {
|
||||
get_time_stamp_usec(trace_time_stamp_str);
|
||||
printf("\n Received config message. Time Stamp: %s", trace_time_stamp_str);
|
||||
|
||||
// get the IP address length in bytes
|
||||
bufIdx_rd = 1;
|
||||
uint8_t ip_address_length = addr_rd[bufIdx_rd];
|
||||
bufIdx_rd +=1; // + 1 byte = IP address length
|
||||
|
||||
// read the IP address
|
||||
for (int i = 0; i < ip_address_length; i++) {
|
||||
ip_address[i] = addr_rd[bufIdx_rd];
|
||||
bufIdx_rd +=1;
|
||||
}
|
||||
|
||||
ip = ip_address;
|
||||
|
||||
// get the array bytes of the port number : 2 bytes
|
||||
uint8_t port_number_bytes[2] = {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd+1]};
|
||||
bufIdx_rd +=2; // + 2 bytes = start frame number
|
||||
|
||||
port = combine_bytes(port_number_bytes, 2);
|
||||
printf("\n Parameters: IP Address length: %d, IP Address: %s, Port Number: %d \n", ip_address_length, ip_address, port);
|
||||
addr_rd[0] = 0; // reset memory : to wait for record action
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Establish connection to the nr-softmodem
|
||||
clear_remote_config();
|
||||
if (socket != -1) close(socket);
|
||||
|
||||
/* connect to the nr-softmodem */
|
||||
socket = connect_to(ip, port);
|
||||
printf("\n Connected to nr-softmodem");
|
||||
|
||||
// Read Action record or stop
|
||||
printf("\n Data Collection Service: Waiting for record message ...\n");
|
||||
while (1) {
|
||||
// wait for Action record
|
||||
if ((uint8_t)(addr_rd[0]) == 0){
|
||||
usleep(50); // sleep for 50 us: 0.5 ms slot duration
|
||||
}
|
||||
|
||||
// quit state
|
||||
else if ((uint8_t)(addr_rd[0]) == 3) {
|
||||
printf("\n Received 'stop' command. Exiting...");
|
||||
printf("\n ");
|
||||
// Clean up and exit
|
||||
break;
|
||||
}
|
||||
// record state
|
||||
else if ((uint8_t)(addr_rd[0]) == 2){
|
||||
// clear remote buffer if there is
|
||||
clear_remote_config();
|
||||
get_time_stamp_usec(trace_time_stamp_str);
|
||||
printf("\n Received record message. Time Stamp: %s", trace_time_stamp_str);
|
||||
|
||||
// read number of requested messages
|
||||
bufIdx_rd = 1;
|
||||
num_req_tracer_msgs = addr_rd[bufIdx_rd];
|
||||
bufIdx_rd +=1;
|
||||
printf("\n Number of requested tracer messages: %d,", num_req_tracer_msgs);
|
||||
// reset memory : action to wait for next record action
|
||||
addr_rd[0] = 0;
|
||||
|
||||
// read tracer msg IDs - every message ID has been stored in two bytes
|
||||
for (uint8_t msg_n = 0; msg_n < num_req_tracer_msgs; msg_n++) {
|
||||
// get the array bytes of the tracer message ID: 2 bytes
|
||||
uint8_t msg_id_bytes [2]= {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd+1]};
|
||||
bufIdx_rd +=2; // + 2 bytes = message ID
|
||||
msg_id= combine_bytes(msg_id_bytes, 2);
|
||||
|
||||
req_tracer_msgs_indices[msg_n] = msg_id;
|
||||
printf(" msg_id: %d, ", msg_id);
|
||||
}
|
||||
// get the array bytes of the number of records: 4 bytes
|
||||
uint8_t number_records_bytes[4] = {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd+1], addr_rd[bufIdx_rd+2], addr_rd[bufIdx_rd+3]};
|
||||
bufIdx_rd +=4; // + 4 bytes = number of records
|
||||
|
||||
number_records = combine_bytes(number_records_bytes, 4);
|
||||
|
||||
printf("num_records: %d, ", number_records);
|
||||
|
||||
// get the array bytes of the start frame number : 2 bytes
|
||||
uint8_t start_frame_number_bytes[2] = {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd+1]};
|
||||
bufIdx_rd +=2; // + 2 bytes = start frame number
|
||||
|
||||
start_frame_number = combine_bytes(start_frame_number_bytes, 2);
|
||||
|
||||
printf("start_frame: %d\n", start_frame_number);
|
||||
|
||||
/* activate the trace in this array */
|
||||
printf("\n Activate Tracer messages in on_off array: ");
|
||||
for (i = 0; i < num_req_tracer_msgs; i++){
|
||||
char *on_off_name= event_name_from_id(database, req_tracer_msgs_indices[i]);
|
||||
on_off(database, on_off_name, is_on, 1);
|
||||
printf("%d, %s, ", req_tracer_msgs_indices[i], on_off_name);
|
||||
//on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
|
||||
}
|
||||
|
||||
// get the event IDs for the bit messages
|
||||
int traces_bits_support_data_Collection_format_idx [n_bits_msgs];
|
||||
for (int i = 0; i < n_bits_msgs; i++) {
|
||||
traces_bits_support_data_Collection_format_idx[i] =
|
||||
event_id_from_name(database, traces_bits_support_data_Collection_format[i]);
|
||||
}
|
||||
|
||||
// all supported messages
|
||||
int traces_support_data_Collection_format_idx [n_msgs_based_data_Collection_format];
|
||||
for (int i = 0; i < n_msgs_based_data_Collection_format; i++) {
|
||||
traces_support_data_Collection_format_idx[i] =
|
||||
event_id_from_name(database, traces_support_data_Collection_format[i]);
|
||||
}
|
||||
|
||||
// setup data for the trace messages
|
||||
setup_trace_msg_data(&trace_msg_data, database);
|
||||
printf("\n Setup Trace Data Done");
|
||||
|
||||
// Get the start time
|
||||
struct timespec start_time = get_current_time();
|
||||
|
||||
/* activate the tracee in the nr-softmodem */
|
||||
activate_traces(socket, number_of_events, is_on);
|
||||
printf("\n Activated Traces in nr-softmodem");
|
||||
|
||||
/* a buffer needed to receive events from the nr-softmodem */
|
||||
OBUF ebuf = { osize: 0, omaxsize: 0, obuf: NULL };
|
||||
|
||||
/* read events */
|
||||
int nrecord_idx = 0;
|
||||
bufIdx_wr = 0;
|
||||
// logic to be sure that we have a complete slot record
|
||||
bool start_recording = false;
|
||||
bool got_ref_frame_slot = false;
|
||||
int ref_slot = 0;
|
||||
int ref_frame = 0;
|
||||
int slot_difference = 0;
|
||||
int frame_difference = 0;
|
||||
int current_frame = 0, prev_frame = 0, current_slot = 0, prev_slot = 0;
|
||||
// offset to sync between base station and UE synchronization records or power measurements
|
||||
int sync_offset_index = 0; // increase the index only if the index of frame changes after getting all records
|
||||
// since we will use the frame differece to do extra records, we should be sure that the last slot is recorded completely
|
||||
printf("\n\n Data Collection Service: Start reading messages ...");
|
||||
struct pollfd event_poll_fd;
|
||||
event_poll_fd.fd = socket;
|
||||
event_poll_fd.events = POLLIN;
|
||||
while (1) {
|
||||
// stop if number of records is done
|
||||
if ((nrecord_idx >= number_records) && (sync_offset_index >= max_sync_offset)){
|
||||
// We added one to the number of records to capture the last record completely if
|
||||
// we have several messages enabled per slot
|
||||
break;
|
||||
}
|
||||
int poll_ret = poll(&event_poll_fd, 1, 1); // 1 ms timeout for poll
|
||||
if (poll_ret > 0 && (event_poll_fd.revents & POLLIN)) {
|
||||
event e = get_event(socket, &ebuf, database);
|
||||
|
||||
if (e.type == -1) {
|
||||
printf("\n Link broken or unexpected message received. Re-establishing connection...\n");
|
||||
reestablish_connection(&socket, ip, port, number_of_events, is_on);
|
||||
continue; // Skip further processing and retry
|
||||
}
|
||||
//-------------------------
|
||||
// GNB_PHY_UL_FD_PUSCH_IQ, GNB_PHY_UL_FD_DMRS_ID, GNB_PHY_UL_FD_CHAN_EST_DMRS_POS,
|
||||
// UE_PHY_UL_SCRAMBLED_TX_BITS, GNB_PHY_UL_PAYLOAD_RX_BITS, UE_PHY_UL_PAYLOAD_TX_BITS
|
||||
//-------------------------
|
||||
// is it a requested message
|
||||
if (isValueInArray(e.type, req_tracer_msgs_indices, num_req_tracer_msgs)) {
|
||||
// is it based on Data Collection Trace Messages Structure
|
||||
if (isValueInArray(e.type, traces_support_data_Collection_format_idx, n_msgs_based_data_Collection_format)) {
|
||||
// Start recording from the next slot to mitigate capturing partial data
|
||||
// check if the current frame and slot are different from the previous frame and slot
|
||||
// Then, increase the record index
|
||||
if (start_recording == false) {
|
||||
if (got_ref_frame_slot == false) {
|
||||
ref_frame = e.e[trace_msg_data.frame].i;
|
||||
ref_slot = e.e[trace_msg_data.slot].i;
|
||||
printf ("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type),e.type);
|
||||
got_ref_frame_slot = true;}
|
||||
|
||||
current_frame = e.e[trace_msg_data.frame].i;
|
||||
current_slot = e.e[trace_msg_data.slot].i;
|
||||
frame_difference = (current_frame - ref_frame + MAX_FRAME_INDEX + 1) % (MAX_FRAME_INDEX + 1);
|
||||
slot_difference = (current_slot - ref_slot + MAX_SLOT_INDEX + 1) % (MAX_SLOT_INDEX + 1);
|
||||
printf("\n First frame.slot: %d.%d, current frame.slot: %d.%d, diff frame.slot: %d.%d",
|
||||
ref_frame, ref_slot,
|
||||
current_frame, current_slot,
|
||||
frame_difference, slot_difference);
|
||||
|
||||
if ((ref_frame != current_frame) || (ref_slot != current_slot)) {
|
||||
start_recording = true;
|
||||
printf("\n Start recording from frame: %d, slot: %d ", e.e[trace_msg_data.frame].i, e.e[trace_msg_data.slot].i);}
|
||||
}
|
||||
// start recording from the next frame to mitigate capturing partial data
|
||||
if (start_recording == true) {
|
||||
/* this is how to access the elements of the Data Collection trace messages.
|
||||
* we use e.e[<element>] and then the correct suffix, here
|
||||
* it's .i for the integer and .b for the buffer and .bsize for the buffer size
|
||||
* see in event.h the structure event_arg
|
||||
*/
|
||||
unsigned char *buf = e.e[trace_msg_data.data].b;
|
||||
|
||||
printf("\n\nRecord number: %d", nrecord_idx);
|
||||
#ifdef DEBUG_BUFFER
|
||||
printf("\nBuffer index in bytes: %d", bufIdx_wr);
|
||||
#endif
|
||||
|
||||
// add general message header: message ID,
|
||||
// T-Tracer Message format
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
|
||||
// int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.type, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.frame].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.slot].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.datetime_yyyymmdd].i, sizeof(uint32_t));
|
||||
bufIdx_wr += sizeof(uint32_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.datetime_hhmmssmmm].i, sizeof(uint32_t));
|
||||
bufIdx_wr += sizeof(uint32_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.frame_type].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.freq_range].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.subcarrier_spacing].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.cyclic_prefix].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.symbols_per_slot].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.Nid_cell].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rnti].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rb_size].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rb_start].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.start_symbol_index].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nr_of_symbols].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.qam_mod_order].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.mcs_index].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.mcs_table].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nrOfLayers].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.transform_precoding].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_config_type].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.ul_dmrs_symb_pos].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.number_dmrs_symbols].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_port].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_nscid].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nb_antennas_rx].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.number_of_bits].i, sizeof(uint32_t));
|
||||
bufIdx_wr += sizeof(uint32_t);
|
||||
|
||||
printf("\nTime Stamp: %d_%d", e.e[trace_msg_data.datetime_yyyymmdd].i, e.e[trace_msg_data.datetime_hhmmssmmm].i);
|
||||
|
||||
// add message body: length in bytes + recorded data
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.data].bsize, sizeof(uint32_t));
|
||||
bufIdx_wr += sizeof(uint32_t);
|
||||
|
||||
// read data from buffer and convert from unsigned char * array to int 16 using the right endianness
|
||||
// T-tracer: Little Endian
|
||||
// For BITS Messages, example: UE_PHY_UL_SCRAMBLED_TX_BITS: data in bytes
|
||||
|
||||
if(is_bits_messages(traces_bits_support_data_Collection_format_idx, n_bits_msgs, e.type)) {
|
||||
for (int byte_idx = 0; byte_idx < e.e[trace_msg_data.data].bsize; byte_idx+=1) {
|
||||
//printf("%d, ", buf[byte_idx]);
|
||||
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx], sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
for (int byte_idx = 0; byte_idx < e.e[trace_msg_data.data].bsize; byte_idx+=2) {
|
||||
//For a little-endian system:
|
||||
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx], sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx+1], sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
}
|
||||
}
|
||||
/*
|
||||
for (int byte_idx_i = 0; byte_idx_i < e.e[d.nr_ul_fd_dmrs_data].bsize; byte_idx_i+=4) {
|
||||
int16_t I = buf[byte_idx_i] | (buf[byte_idx_i+1] << 8);
|
||||
int16_t Q = buf[byte_idx_i+2] | (buf[byte_idx_i+3] << 8);
|
||||
printf ("idx %d, ", byte_idx_i);
|
||||
printf ("\n%d", I);
|
||||
printf ("\n%d", Q);
|
||||
}
|
||||
*/
|
||||
// check if the current frame and slot are different from the previous frame and slot
|
||||
// Then, increase the record index
|
||||
current_frame = e.e[trace_msg_data.frame].i;
|
||||
current_slot = e.e[trace_msg_data.slot].i;
|
||||
|
||||
// increase sync offset index if the current frame is different from the previous frame
|
||||
if ((current_frame != prev_frame) && nrecord_idx >= number_records) {
|
||||
sync_offset_index++;
|
||||
}
|
||||
|
||||
if (current_frame != prev_frame || current_slot != prev_slot) {
|
||||
nrecord_idx++;
|
||||
// Update previous frame and slot numbers
|
||||
prev_frame = current_frame;
|
||||
prev_slot = current_slot;
|
||||
}
|
||||
#ifdef DEBUG_T_Tracer
|
||||
printf ("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type),e.type);
|
||||
printf("frame %d, slot %d, datetime %d_%d\n",
|
||||
e.e[trace_msg_data.frame].i,
|
||||
e.e[trace_msg_data.slot].i,
|
||||
e.e[trace_msg_data.datetime_yyyymmdd].i,
|
||||
e.e[trace_msg_data.datetime_hhmmssmmm].i);
|
||||
printf("frame_type %d, freq_range %d, subcarrier_spacing %d, cyclic_prefix %d, symbols_per_slot %d\n",
|
||||
e.e[trace_msg_data.frame_type].i,
|
||||
e.e[trace_msg_data.freq_range].i,
|
||||
e.e[trace_msg_data.subcarrier_spacing].i,
|
||||
e.e[trace_msg_data.cyclic_prefix].i,
|
||||
e.e[trace_msg_data.symbols_per_slot].i);
|
||||
printf("Nid_cell %d, rnti %d, rb_size %d, rb_start %d, start_symbol_index %d, nr_of_symbols %d\n",
|
||||
e.e[trace_msg_data.Nid_cell].i,
|
||||
e.e[trace_msg_data.rnti].i,
|
||||
e.e[trace_msg_data.rb_size].i,
|
||||
e.e[trace_msg_data.rb_start].i,
|
||||
e.e[trace_msg_data.start_symbol_index].i,
|
||||
e.e[trace_msg_data.nr_of_symbols].i);
|
||||
printf("qam_mod_order %d, mcs_index %d, mcs_table %d, nrOfLayers %d, transform_precoding %d\n",
|
||||
e.e[trace_msg_data.qam_mod_order].i,
|
||||
e.e[trace_msg_data.mcs_index].i,
|
||||
e.e[trace_msg_data.mcs_table].i,
|
||||
e.e[trace_msg_data.nrOfLayers].i,
|
||||
e.e[trace_msg_data.transform_precoding].i);
|
||||
printf("dmrs_config_type %d, ul_dmrs_symb_pos %d, number_dmrs_symbols %d, dmrs_port %d, dmrs_nscid %d\n",
|
||||
e.e[trace_msg_data.dmrs_config_type].i,
|
||||
e.e[trace_msg_data.ul_dmrs_symb_pos].i,
|
||||
e.e[trace_msg_data.number_dmrs_symbols].i,
|
||||
e.e[trace_msg_data.dmrs_port].i,
|
||||
e.e[trace_msg_data.dmrs_nscid].i);
|
||||
printf("nb_antennas_rx %d, number_of_bits %d, data size %d\n",
|
||||
e.e[trace_msg_data.nb_antennas_rx].i,
|
||||
e.e[trace_msg_data.number_of_bits].i,
|
||||
e.e[trace_msg_data.data].bsize);
|
||||
#endif
|
||||
} // End of start recording flag
|
||||
} // end of if statement for the supported messages based on Data Collection Trace Messages Structure
|
||||
else {
|
||||
printf("ERROR: Requested Message is not based on Data Collection Trace Messages Structure\n");
|
||||
printf("ERROR: Requested Message ID: %d\n", e.type);
|
||||
}
|
||||
} // end of if statement for the requested messages
|
||||
} // end while loop of reading events
|
||||
else {
|
||||
// No data, just loop and check time
|
||||
usleep(100); // optional: avoid busy-waiting
|
||||
}
|
||||
} // End of while loop to read events
|
||||
|
||||
// de-activate the tracee in the nr-softmodem
|
||||
printf("\n De-activated Tracer message:\n");
|
||||
for (i = 0; i < num_req_tracer_msgs; i++){
|
||||
char *on_off_name= event_name_from_id(database, req_tracer_msgs_indices[i]);
|
||||
on_off(database, on_off_name, is_on, 0);
|
||||
//printf("\n %d, %s, ", req_tracer_msgs_indices[i], on_off_name);
|
||||
//on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
|
||||
}
|
||||
// De-activate the tracee in the nr-softmodem
|
||||
activate_traces(socket, number_of_events, is_on);
|
||||
printf("\n De-activated Traces");
|
||||
// Get the end time
|
||||
struct timespec end_time = get_current_time();
|
||||
// Calculate the time difference
|
||||
double time_diff = calculate_time_difference(start_time, end_time);
|
||||
printf("Total Time difference: %.2f ms\n", time_diff);
|
||||
printf("Time difference per record: %.2f ms\n", time_diff/(number_records + max_sync_offset));
|
||||
|
||||
// discard stale or previous record data for the first DISCARD_RECORD_DURATION_MS
|
||||
struct timespec record_start, record_now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &record_start);
|
||||
|
||||
while (1) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &record_now);
|
||||
double elapsed_ms = calculate_time_difference(record_start, record_now);
|
||||
if (elapsed_ms >= DISCARD_RECORD_DURATION_MS) {
|
||||
break; // Stop after 10ms
|
||||
}
|
||||
int poll_ret = poll(&event_poll_fd, 1, 1); // 1 ms timeout for poll
|
||||
if (poll_ret > 0 && (event_poll_fd.revents & POLLIN)) {
|
||||
event e = get_event(socket, &ebuf, database);
|
||||
printf("%d", e.type);
|
||||
if (e.type == -1) {
|
||||
printf("\n Link broken or unexpected message received. Re-establishing connection...\n");
|
||||
reestablish_connection(&socket, ip, port, number_of_events, is_on);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// No data, just loop and check time
|
||||
usleep(100); // optional: avoid busy-waiting
|
||||
}
|
||||
} // End of while loop to discard stale records
|
||||
}
|
||||
} // End a while loop to check for the "stop" command
|
||||
// de-activate the tracee in the nr-softmodem
|
||||
|
||||
free_database(database);
|
||||
free(is_on);
|
||||
close(socket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
954
common/utils/T/tracer/t_tracer_app_ue.c
Normal file
@@ -0,0 +1,954 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file common/utils/T/tracer/t_tracer_app_ue.c
|
||||
* \brief T-Tracer UE service to capture tracee Messages from UE, it is used by data recording application
|
||||
* \author Abdo Gaber
|
||||
* \date 2025
|
||||
* \version 1.0
|
||||
* \company Emerson, NI Test and Measurement
|
||||
* \email:
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include "database.h"
|
||||
#include "event.h"
|
||||
#include "handler.h"
|
||||
#include "logger/logger.h"
|
||||
#include "utils.h"
|
||||
#include "event_selector.h"
|
||||
#include "configuration.h"
|
||||
#include "shared_memory_config.h"
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <poll.h>
|
||||
#define MAX_FRAME_INDEX 1023
|
||||
#define MAX_SLOT_INDEX 19
|
||||
#define DEBUG_T_Tracer
|
||||
#define DEBUG_BUFFER
|
||||
// Duration to discard recording in milliseconds to mitigate stale or previous record data
|
||||
#define DISCARD_RECORD_DURATION_MS 10
|
||||
//#include <linux/time.h>
|
||||
|
||||
// Combine bytes in little-endian format
|
||||
int combine_bytes(const uint8_t *bytes, size_t num_bytes)
|
||||
{
|
||||
int result = 0;
|
||||
for (size_t i = 0; i < num_bytes; ++i) {
|
||||
result |= (bytes[i] << (i * 8));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert an integer to an array of bytes in little-endian format
|
||||
void int_to_bytes(int num, uint8_t *bytes, size_t num_bytes)
|
||||
{
|
||||
for (size_t i = 0; i < num_bytes; ++i) {
|
||||
bytes[i] = (num >> (i * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the message is in the list of bits messages
|
||||
bool is_bits_messages(int traces_bits_support_data_Collection_format_idx[], int n_bits_msgs, int msg_id)
|
||||
{
|
||||
for (int i = 0; i < n_bits_msgs; i++) {
|
||||
if (msg_id == traces_bits_support_data_Collection_format_idx[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the current time
|
||||
struct timespec get_current_time()
|
||||
{
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
||||
return time;
|
||||
}
|
||||
|
||||
// Function to convert timespec to microseconds
|
||||
long long timespec_to_microseconds(struct timespec time)
|
||||
{
|
||||
return (time.tv_sec * 1000000LL) + (time.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
// Calculate the time difference in milliseconds
|
||||
double calculate_time_difference(struct timespec start, struct timespec end)
|
||||
{
|
||||
double start_ms = start.tv_sec * 1000.0 + start.tv_nsec / 1000000.0;
|
||||
double end_ms = end.tv_sec * 1000.0 + end.tv_nsec / 1000000.0;
|
||||
return end_ms - start_ms;
|
||||
}
|
||||
|
||||
// Get Time Stamp in microseconds in YYYYMMDDHHMMSSmmmuuu format
|
||||
char* get_time_stamp_usec(char time_stamp_str[])
|
||||
{
|
||||
// initialization to measure time stamp --This part should be moved to inilization part
|
||||
time_t my_time;
|
||||
struct tm * timeinfo;
|
||||
time (&my_time);
|
||||
struct timeval tv;
|
||||
|
||||
// get time stamp
|
||||
timeinfo = localtime (&my_time);
|
||||
gettimeofday(&tv,NULL);
|
||||
// Add time stamp: yyyy mm dd hh mm ss usec
|
||||
uint16_t year= timeinfo->tm_year+1900;
|
||||
uint8_t mon= timeinfo->tm_mon+1;
|
||||
uint8_t mday= timeinfo->tm_mday;
|
||||
uint8_t hour= timeinfo->tm_hour;
|
||||
uint8_t min= timeinfo->tm_min;
|
||||
uint8_t sec= timeinfo->tm_sec;
|
||||
uint16_t usec= (tv.tv_usec);
|
||||
//printf ("Time stamp: %d_%d_%d_%d_%d_%d_%d \n",year,mon,mday,hour,min,sec,usec);
|
||||
//sprintf(time_stamp_str, "%d_%d_%d_%d_%d_%d_%d",year,mon,mday,hour,min,sec,usec);
|
||||
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06d", year, mon, mday, hour, min, sec, usec);
|
||||
|
||||
return time_stamp_str;
|
||||
}
|
||||
|
||||
// Convert timestamp string to integer
|
||||
int convert_time_stamp_to_int(const char* timestamp)
|
||||
{
|
||||
return atoi(timestamp);
|
||||
}
|
||||
|
||||
// Split timestamp string and convert to integer
|
||||
int split_time_stamp_and_convert_to_int (char time_stamp_str[], int shift, int length)
|
||||
{
|
||||
char time_part[length+1]; // Buffer to hold the date part YYYYMMDD or HHMMSSmmm
|
||||
// Copy the first 8 or 9 characters (YYYYMMDD) to HHMMSSmmm
|
||||
strncpy(time_part, time_stamp_str + shift, length);
|
||||
time_part[length] = '\0'; // Null-terminate the string
|
||||
// Convert timestamp string to integer
|
||||
return convert_time_stamp_to_int(time_part);
|
||||
}
|
||||
|
||||
void err_exit(char *buf)
|
||||
{
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// create shared memory
|
||||
int create_shm(char **addrN, const char *shm_path, int projectId)
|
||||
{
|
||||
key_t key;
|
||||
if (-1 != open(shm_path, O_CREAT, 0777)) {
|
||||
key = ftok(shm_path, projectId);
|
||||
} else {
|
||||
err_exit("Error: open shared memory");
|
||||
}
|
||||
|
||||
if ( key < 0 ) {
|
||||
err_exit("Error: ftok error");
|
||||
}
|
||||
|
||||
int shm_id;
|
||||
shm_id = shmget(key, SHMSIZE, IPC_CREAT | IPC_EXCL | 0664);
|
||||
if ( shm_id == -1 ) {
|
||||
if ( errno == EEXIST ) {
|
||||
printf("Error: shared memeory already exist\n");
|
||||
shm_id = shmget(key ,0, 0);
|
||||
printf("reference shm_id = %d\n", shm_id);
|
||||
} else {
|
||||
perror("errno");
|
||||
err_exit("shmget error");
|
||||
}
|
||||
}
|
||||
char *addr;
|
||||
// address to attach - attach for read & write
|
||||
if ( (addr = shmat(shm_id, 0, 0) ) == (void*)-1) {
|
||||
if (shmctl(shm_id, IPC_RMID, NULL) == -1)
|
||||
err_exit("Error: shmctl error");
|
||||
else {
|
||||
printf("Attach shared memory failed\n");
|
||||
printf("remove shared memory identifier successful\n");
|
||||
}
|
||||
err_exit("shmat error");
|
||||
}
|
||||
*addrN = addr;
|
||||
return shm_id;
|
||||
}
|
||||
|
||||
// delete shared memory
|
||||
void del_shm(char *addr, int shm_id)
|
||||
{
|
||||
if ( shmdt(addr) < 0) err_exit("shmdt error");
|
||||
|
||||
if (shmctl(shm_id, IPC_RMID, NULL) == -1)
|
||||
err_exit("shmctl error");
|
||||
else {
|
||||
printf("Remove shared memory identifier successful\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* this function sends the activated traces to the nr-softmodem */
|
||||
void activate_traces(int socket, int number_of_events, int *is_on)
|
||||
{
|
||||
char t = 1;
|
||||
if (socket_send(socket, &t, 1) == -1 ||
|
||||
socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
|
||||
socket_send(socket, is_on, number_of_events * sizeof(int)) == -1)
|
||||
abort();
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf(
|
||||
"options:\n"
|
||||
" -d <database file> this option is mandatory\n"
|
||||
" -ip <host> connect to given IP address (default %s)\n"
|
||||
" -p <port> connect to given port (default %d)\n",
|
||||
DEFAULT_REMOTE_IP,
|
||||
DEFAULT_REMOTE_PORT
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// class to store the message and the action to enable capture selected data
|
||||
// 0: do not record message
|
||||
// 1: record message
|
||||
struct trace_struct {
|
||||
char on_off_name[100];
|
||||
int on_off_action;
|
||||
};
|
||||
|
||||
// struct for trace message based on Data Collection Trace Messages Structure
|
||||
// you need to define the vararibles of each message
|
||||
typedef struct {
|
||||
/* Data Collection Trace Message Structure */
|
||||
int frame;
|
||||
int slot;
|
||||
int datetime_yyyymmdd;
|
||||
int datetime_hhmmssmmm;
|
||||
int frame_type, freq_range, subcarrier_spacing, cyclic_prefix, symbols_per_slot;
|
||||
int Nid_cell, rnti;
|
||||
int rb_size, rb_start, start_symbol_index,nr_of_symbols;
|
||||
int qam_mod_order,mcs_index,mcs_table,nrOfLayers,transform_precoding;
|
||||
int dmrs_config_type,ul_dmrs_symb_pos,number_dmrs_symbols;
|
||||
int dmrs_port,dmrs_nscid,nb_antennas_tx,number_of_bits;
|
||||
int data_size, data;
|
||||
} event_trace_msg_data;
|
||||
|
||||
void setup_trace_msg_data(event_trace_msg_data *d, void *database)
|
||||
{
|
||||
database_event_format f;
|
||||
int i;
|
||||
|
||||
/* Data Collection Trace Message Structure */
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
|
||||
// int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_tx : int,number_of_bits : buffer,data
|
||||
|
||||
// Initialize the data structure
|
||||
d->frame = -1;
|
||||
d->slot = -1;
|
||||
d->datetime_yyyymmdd = -1;
|
||||
d->datetime_hhmmssmmm = -1;
|
||||
d->frame_type = -1;
|
||||
d->freq_range = -1;
|
||||
d->subcarrier_spacing = -1;
|
||||
d->cyclic_prefix = -1;
|
||||
d->symbols_per_slot = -1;
|
||||
d->Nid_cell = -1;
|
||||
d->rnti = -1;
|
||||
d->rb_size = -1;
|
||||
d->rb_start = -1;
|
||||
d->start_symbol_index = -1;
|
||||
d->nr_of_symbols = -1;
|
||||
d->qam_mod_order = -1;
|
||||
d->mcs_index = -1;
|
||||
d->mcs_table = -1;
|
||||
d->nrOfLayers = -1;
|
||||
d->transform_precoding = -1;
|
||||
d->dmrs_config_type = -1;
|
||||
d->ul_dmrs_symb_pos = -1;
|
||||
d->number_dmrs_symbols = -1;
|
||||
d->dmrs_port = -1;
|
||||
d->dmrs_nscid = -1;
|
||||
d->nb_antennas_tx = -1;
|
||||
d->number_of_bits = -1;
|
||||
d->data = -1;
|
||||
|
||||
/* this macro looks for a particular element and checks its type */
|
||||
#define G(var_name, var_type, var) \
|
||||
if (!strcmp(f.name[i], var_name)) { \
|
||||
if (strcmp(f.type[i], var_type)) { printf("Error: Trace Message has a bad type for %s\n", var_name); exit(1); } \
|
||||
var = i; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
/* ------------------------------*/
|
||||
/* Create Macro for Data Collection Trace Message */
|
||||
/* ------------------------------*/
|
||||
// Data Collection Trace Message Structure
|
||||
// Example: GNB_PHY_UL_FD_PUSCH_IQ, GNB_PHY_UL_FD_DMRS_ID,
|
||||
// GNB_PHY_UL_FD_CHAN_EST_DMRS_POS, GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL
|
||||
// GNB_PHY_UL_PAYLOAD_RX_BITS
|
||||
// UE_PHY_UL_SCRAMBLED_TX_BITS
|
||||
// UE_PHY_UL_PAYLOAD_TX_BITS
|
||||
// Get a template of any message based on Data Collection Trace Messages Structure
|
||||
int Trace_MSG_ID = event_id_from_name(database, "GNB_PHY_UL_FD_PUSCH_IQ");
|
||||
f = get_format(database, Trace_MSG_ID);
|
||||
|
||||
/* get the elements of the trace
|
||||
* the value is an index in the event, see below */
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
|
||||
// int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_tx : int,number_of_bits : buffer,data
|
||||
|
||||
for (i = 0; i < f.count; i++) {
|
||||
G("frame", "int", d->frame)
|
||||
G("slot", "int", d->slot)
|
||||
G("datetime_yyyymmdd", "int", d->datetime_yyyymmdd)
|
||||
G("datetime_hhmmssmmm", "int", d->datetime_hhmmssmmm)
|
||||
G("frame_type", "int", d->frame_type)
|
||||
G("freq_range", "int", d->freq_range)
|
||||
G("subcarrier_spacing", "int", d->subcarrier_spacing)
|
||||
G("cyclic_prefix", "int", d->cyclic_prefix)
|
||||
G("symbols_per_slot", "int", d->symbols_per_slot)
|
||||
G("Nid_cell", "int", d->Nid_cell)
|
||||
G("rnti", "int", d->rnti)
|
||||
G("rb_size", "int", d->rb_size)
|
||||
G("rb_start", "int", d->rb_start)
|
||||
G("start_symbol_index", "int", d->start_symbol_index)
|
||||
G("nr_of_symbols", "int", d->nr_of_symbols)
|
||||
G("qam_mod_order", "int", d->qam_mod_order)
|
||||
G("mcs_index", "int", d->mcs_index)
|
||||
G("mcs_table", "int", d->mcs_table)
|
||||
G("nrOfLayers", "int", d->nrOfLayers)
|
||||
G("transform_precoding", "int", d->transform_precoding)
|
||||
G("dmrs_config_type", "int", d->dmrs_config_type)
|
||||
G("ul_dmrs_symb_pos", "int", d->ul_dmrs_symb_pos)
|
||||
G("number_dmrs_symbols", "int", d->number_dmrs_symbols)
|
||||
G("dmrs_port", "int", d->dmrs_port)
|
||||
G("dmrs_nscid", "int", d->dmrs_nscid)
|
||||
// bug in T-Tracer Framework, need to be checked that is why we are getting
|
||||
// the right tx antennas only if we set to rx "nb_antennas_rx"
|
||||
G("nb_antennas_rx", "int", d->nb_antennas_tx)
|
||||
G("number_of_bits", "int", d->number_of_bits)
|
||||
G("data", "buffer", d->data)
|
||||
}
|
||||
//if (d->frame == -1 || d->slot == -1) goto error;
|
||||
#undef G
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to check if a value is in the array
|
||||
int isValueInArray(int value, int arr[], int size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (arr[i] == value) {
|
||||
return 1; // Value found
|
||||
}
|
||||
}
|
||||
return 0; // Value not found
|
||||
}
|
||||
|
||||
void reestablish_connection(int *socket, const char *ip, int port, int number_of_events, int *is_on)
|
||||
{
|
||||
clear_remote_config();
|
||||
if (*socket != -1) close(*socket);
|
||||
|
||||
/* connect to the nr-softmodem */
|
||||
*socket = connect_to(ip, port);
|
||||
if (*socket == -1) {
|
||||
printf("\n Failed to connect to nr-softmodem. Retrying...\n");
|
||||
return;
|
||||
}
|
||||
printf("\n Connected");
|
||||
|
||||
/* activate the traces in the nr-softmodem */
|
||||
activate_traces(*socket, number_of_events, is_on);
|
||||
printf("\n Activated Traces in nr-softmodem");
|
||||
}
|
||||
|
||||
int main(int n, char **v)
|
||||
{
|
||||
// Define and initialize an array of strings to list
|
||||
// trace_msgs_support_data_Collection_format
|
||||
// it is used to parse the requested messages if it is based
|
||||
// on Data Collection Trace Messages Structure and supported tracer messages indices
|
||||
const char *traces_iq_support_data_Collection_format[] = {
|
||||
"GNB_PHY_UL_FD_PUSCH_IQ",
|
||||
"GNB_PHY_UL_FD_DMRS",
|
||||
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
|
||||
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL"
|
||||
};
|
||||
const char *traces_bits_support_data_Collection_format[] = {
|
||||
"GNB_PHY_UL_PAYLOAD_RX_BITS",
|
||||
"UE_PHY_UL_SCRAMBLED_TX_BITS",
|
||||
"UE_PHY_UL_PAYLOAD_TX_BITS"
|
||||
};
|
||||
// extra number of records to simlify Sync between base station and UE synchronization records.
|
||||
// if you have network delay, you can increase the number of records to capture
|
||||
int max_sync_offset = 6; // 6 frames ~ 60 ms
|
||||
// all supported messages
|
||||
// Calculate the size of the combined array
|
||||
int n_iq_msgs = sizeof(traces_iq_support_data_Collection_format) / sizeof(traces_iq_support_data_Collection_format[0]);
|
||||
int n_bits_msgs = sizeof(traces_bits_support_data_Collection_format) / sizeof(traces_bits_support_data_Collection_format[0]);
|
||||
int n_msgs_based_data_Collection_format = n_iq_msgs + n_bits_msgs;
|
||||
|
||||
// Create the combined array
|
||||
const char *traces_support_data_Collection_format[n_msgs_based_data_Collection_format];
|
||||
// Copy IQ messages to the combined array
|
||||
for (int i = 0; i < n_iq_msgs; i++) {
|
||||
traces_support_data_Collection_format[i] = traces_iq_support_data_Collection_format[i];
|
||||
}
|
||||
|
||||
// Copy Bits messages to the combined array
|
||||
for (int i = 0; i < n_bits_msgs; i++) {
|
||||
traces_support_data_Collection_format[i + n_iq_msgs] = traces_bits_support_data_Collection_format[i];
|
||||
}
|
||||
|
||||
uint16_t msg_id = 0;
|
||||
uint16_t start_frame_number = 0;
|
||||
uint32_t number_records = 0; // number of records to capture, it is number of slots
|
||||
// array to store the requested tracer messages indices
|
||||
int req_tracer_msgs_indices[100] = {0};
|
||||
// define variables --> to do: add all of them to be class of pointers
|
||||
char *database_filename = NULL;
|
||||
void *database;
|
||||
char *ip = DEFAULT_REMOTE_IP;
|
||||
char ip_address[16]; // max IP address length is 15 + 1 for null terminator
|
||||
int port = DEFAULT_REMOTE_PORT;
|
||||
int *is_on;
|
||||
int number_of_events;
|
||||
int i;
|
||||
int socket = -1;
|
||||
char trace_time_stamp_str[30];
|
||||
|
||||
// data structure for the trace messages based on Data Collection Trace Messages Structure
|
||||
event_trace_msg_data trace_msg_data;
|
||||
|
||||
// initlization variables
|
||||
unsigned int bufIdx_wr = 0;
|
||||
unsigned int bufIdx_rd = 0;
|
||||
uint8_t num_req_tracer_msgs = 0;
|
||||
|
||||
// initilaze shared memory
|
||||
char *addr_wr,*addr_rd;
|
||||
printf("\n Data Collection Service: Initializing shared memory ...");
|
||||
printf("\n Directory 1: %s, Directory 2: %s", GETKEYDIR1_UE, GETKEYDIR2_UE);
|
||||
printf("\n Project ID: %d\n", PROJECTID_UE);
|
||||
int shm_id_wr = create_shm(&addr_wr, GETKEYDIR1_UE, PROJECTID_UE);
|
||||
int shm_id_rd = create_shm(&addr_rd, GETKEYDIR2_UE, PROJECTID_UE);
|
||||
del_shm(addr_wr, shm_id_wr);
|
||||
del_shm(addr_rd, shm_id_rd);
|
||||
shm_id_wr = create_shm(&addr_wr, GETKEYDIR1_UE, PROJECTID_UE);
|
||||
shm_id_rd = create_shm(&addr_rd, GETKEYDIR2_UE, PROJECTID_UE);
|
||||
|
||||
/* write on a socket fails if the other end is closed and we get SIGPIPE */
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
|
||||
|
||||
/* parse command line options */
|
||||
// Port number and IP address are given via API and not via command line
|
||||
for (i = 1; i < n; i++) {
|
||||
if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
|
||||
if (!strcmp(v[i], "-d"))
|
||||
{ if (i > n-2) usage(); database_filename = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-p"))
|
||||
{ if (i > n-2) usage(); port = atoi(v[++i]); continue; }
|
||||
usage();
|
||||
}
|
||||
|
||||
if (database_filename == NULL) {
|
||||
printf("ERROR: Provide a database file (-d)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* load the database T_messages.txt */
|
||||
database = parse_database(database_filename);
|
||||
load_config_file(database_filename);
|
||||
|
||||
/* an array of int for all the events defined in the database is needed */
|
||||
number_of_events = number_of_ids(database);
|
||||
is_on = calloc(number_of_events, sizeof(int));
|
||||
if (is_on == NULL) abort();
|
||||
|
||||
// Set first byte of the shared memory to 0
|
||||
addr_rd[0] = 0; // important to check if we have new requested messages
|
||||
|
||||
// read requested tracer msg indices from memory
|
||||
printf("\n Data Collection Service: Waiting for messages request ...\n");
|
||||
// 0: Wait
|
||||
// 1: config
|
||||
// 2: record
|
||||
// 3: stop
|
||||
// Wait for Action Config
|
||||
while (1) {
|
||||
if ((uint8_t)(addr_rd[0]) == 0){
|
||||
usleep(50); // sleep for 50 us: 0.5 ms slot duration
|
||||
}
|
||||
// config state
|
||||
else if ((uint8_t)(addr_rd[0]) == 1) {
|
||||
get_time_stamp_usec(trace_time_stamp_str);
|
||||
printf("\n Received config message. Time Stamp: %s", trace_time_stamp_str);
|
||||
|
||||
// get the IP address length in bytes
|
||||
bufIdx_rd = 1;
|
||||
uint8_t ip_address_length = addr_rd[bufIdx_rd];
|
||||
bufIdx_rd +=1; // + 1 byte = IP address length
|
||||
|
||||
// read the IP address
|
||||
for (int i = 0; i < ip_address_length; i++) {
|
||||
ip_address[i] = addr_rd[bufIdx_rd];
|
||||
bufIdx_rd +=1;
|
||||
}
|
||||
|
||||
ip = ip_address;
|
||||
|
||||
// get the array bytes of the port number : 2 bytes
|
||||
uint8_t port_number_bytes[2] = {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd+1]};
|
||||
bufIdx_rd +=2; // + 2 bytes = start frame number
|
||||
|
||||
port = combine_bytes(port_number_bytes, 2);
|
||||
printf("\n Parameters: IP Address length: %d, IP Address: %s, Port Number: %d \n", ip_address_length, ip_address, port);
|
||||
addr_rd[0] = 0; // reset memory : to wait for record action
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Establish connection to the nr-softmodem
|
||||
clear_remote_config();
|
||||
if (socket != -1) close(socket);
|
||||
|
||||
/* connect to the nr-softmodem */
|
||||
socket = connect_to(ip, port);
|
||||
printf("\n Connected to nr-UEsoftmodem");
|
||||
|
||||
// Read Action record or stop
|
||||
printf("\n Data Collection Service: Waiting for record message ...\n");
|
||||
while (1) {
|
||||
// wait for Action record
|
||||
if ((uint8_t)(addr_rd[0]) == 0){
|
||||
usleep(50); // sleep for 50 us: 0.5 ms slot duration
|
||||
}
|
||||
|
||||
// quit state
|
||||
else if ((uint8_t)(addr_rd[0]) == 3) {
|
||||
printf("\n Received 'stop' command. Exiting...");
|
||||
printf("\n ");
|
||||
// Clean up and exit
|
||||
break;
|
||||
}
|
||||
// record state
|
||||
else if ((uint8_t)(addr_rd[0]) == 2){
|
||||
// clear remote buffer if there is
|
||||
clear_remote_config();
|
||||
get_time_stamp_usec(trace_time_stamp_str);
|
||||
printf("\n Received record message. Time Stamp: %s", trace_time_stamp_str);
|
||||
|
||||
// read number of requested messages
|
||||
bufIdx_rd = 1;
|
||||
num_req_tracer_msgs = addr_rd[bufIdx_rd];
|
||||
bufIdx_rd +=1;
|
||||
printf("\n Number of requested tracer messages: %d,", num_req_tracer_msgs);
|
||||
// reset memory : action to wait for next record action
|
||||
addr_rd[0] = 0;
|
||||
|
||||
// read tracer msg IDs - every message ID has been stored in two bytes
|
||||
for (uint8_t msg_n = 0; msg_n < num_req_tracer_msgs; msg_n++) {
|
||||
// get the array bytes of the tracer message ID: 2 bytes
|
||||
uint8_t msg_id_bytes [2]= {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd+1]};
|
||||
bufIdx_rd +=2; // + 2 bytes = message ID
|
||||
msg_id= combine_bytes(msg_id_bytes, 2);
|
||||
|
||||
req_tracer_msgs_indices[msg_n] = msg_id;
|
||||
printf(" msg_id: %d, ", msg_id);
|
||||
}
|
||||
// get the array bytes of the number of records: 4 bytes
|
||||
uint8_t number_records_bytes[4] = {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd+1], addr_rd[bufIdx_rd+2], addr_rd[bufIdx_rd+3]};
|
||||
bufIdx_rd +=4; // + 4 bytes = number of records
|
||||
|
||||
number_records = combine_bytes(number_records_bytes, 4);
|
||||
|
||||
printf("num_records: %d, ", number_records);
|
||||
|
||||
// get the array bytes of the start frame number : 2 bytes
|
||||
uint8_t start_frame_number_bytes[2] = {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd+1]};
|
||||
bufIdx_rd +=2; // + 2 bytes = start frame number
|
||||
|
||||
start_frame_number = combine_bytes(start_frame_number_bytes, 2);
|
||||
|
||||
printf("start_frame: %d\n", start_frame_number);
|
||||
|
||||
/* activate the trace in this array */
|
||||
printf("\n Activate Tracer messages in on_off array: \n");
|
||||
for (i = 0; i < num_req_tracer_msgs; i++){
|
||||
char *on_off_name= event_name_from_id(database, req_tracer_msgs_indices[i]);
|
||||
on_off(database, on_off_name, is_on, 1);
|
||||
printf("%d, %s, ", req_tracer_msgs_indices[i], on_off_name);
|
||||
//on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
|
||||
}
|
||||
|
||||
// get the event IDs for the bit messages
|
||||
int traces_bits_support_data_Collection_format_idx [n_bits_msgs];
|
||||
for (int i = 0; i < n_bits_msgs; i++) {
|
||||
traces_bits_support_data_Collection_format_idx[i] =
|
||||
event_id_from_name(database, traces_bits_support_data_Collection_format[i]);
|
||||
}
|
||||
|
||||
// all supported messages
|
||||
int traces_support_data_Collection_format_idx [n_msgs_based_data_Collection_format];
|
||||
for (int i = 0; i < n_msgs_based_data_Collection_format; i++) {
|
||||
traces_support_data_Collection_format_idx[i] =
|
||||
event_id_from_name(database, traces_support_data_Collection_format[i]);
|
||||
}
|
||||
|
||||
// setup data for the trace messages
|
||||
setup_trace_msg_data(&trace_msg_data, database);
|
||||
printf("\n Setup Trace Data Done");
|
||||
|
||||
// Get the start time
|
||||
struct timespec start_time = get_current_time();
|
||||
|
||||
/* activate the tracee in the nr-softmodem */
|
||||
activate_traces(socket, number_of_events, is_on);
|
||||
printf("\n Activated Traces in nr-UEsoftmodem");
|
||||
|
||||
/* a buffer needed to receive events from the nr-softmodem */
|
||||
OBUF ebuf = { osize: 0, omaxsize: 0, obuf: NULL };
|
||||
/* read events */
|
||||
int nrecord_idx = 0;
|
||||
bufIdx_wr = 0;
|
||||
// logic to be sure that we have a complete slot record
|
||||
bool start_recording = false;
|
||||
bool got_ref_frame_slot = false;
|
||||
int ref_slot = 0;
|
||||
int ref_frame = 0;
|
||||
int slot_difference = 0;
|
||||
int frame_difference = 0;
|
||||
int current_frame = 0, prev_frame = 0, current_slot = 0, prev_slot = 0;
|
||||
// offset to sync between base station and UE synchronization records or power measurements
|
||||
int sync_offset_index = 0; // increase the index only if the index of frame changes after getting all records
|
||||
// since we will use the frame differece to do extra records, we should be sure that the last slot is recorded completely
|
||||
printf("\n\n Data Collection Service: Start reading messages ...");
|
||||
struct pollfd event_poll_fd;
|
||||
event_poll_fd.fd = socket;
|
||||
event_poll_fd.events = POLLIN;
|
||||
while (1) {
|
||||
// stop if number of records is done
|
||||
if ((nrecord_idx >= number_records) && (sync_offset_index >= max_sync_offset)){
|
||||
// We added one to the number of records to capture the last record completely
|
||||
// if we have several messages enabled per slot
|
||||
break;
|
||||
}
|
||||
int poll_ret = poll(&event_poll_fd, 1, 1); // 1 ms timeout for poll
|
||||
if (poll_ret > 0 && (event_poll_fd.revents & POLLIN)) {
|
||||
event e = get_event(socket, &ebuf, database);
|
||||
if (e.type == -1) {
|
||||
printf("\n Link broken or unexpected message received. Re-establishing connection...\n");
|
||||
reestablish_connection(&socket, ip, port, number_of_events, is_on);
|
||||
continue; // Skip further processing and retry
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// GNB_PHY_UL_FD_PUSCH_IQ, GNB_PHY_UL_FD_DMRS_ID, GNB_PHY_UL_FD_CHAN_EST_DMRS_POS,
|
||||
// UE_PHY_UL_SCRAMBLED_TX_BITS, GNB_PHY_UL_PAYLOAD_RX_BITS, UE_PHY_UL_PAYLOAD_TX_BITS
|
||||
//-------------------------
|
||||
// is it a requested message
|
||||
if (isValueInArray(e.type, req_tracer_msgs_indices, num_req_tracer_msgs)) {
|
||||
// is it based on Data Collection Trace Messages Structure
|
||||
if (isValueInArray(e.type, traces_support_data_Collection_format_idx, n_msgs_based_data_Collection_format)) {
|
||||
// Start recording from the next slot to mitigate capturing partial data
|
||||
// check if the current frame and slot are different from the previous frame and slot
|
||||
// Then, increase the record index
|
||||
if (start_recording == false) {
|
||||
if (got_ref_frame_slot == false) {
|
||||
ref_frame = e.e[trace_msg_data.frame].i;
|
||||
ref_slot = e.e[trace_msg_data.slot].i;
|
||||
printf ("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type),e.type);
|
||||
got_ref_frame_slot = true;}
|
||||
|
||||
current_frame = e.e[trace_msg_data.frame].i;
|
||||
current_slot = e.e[trace_msg_data.slot].i;
|
||||
frame_difference = (current_frame - ref_frame + MAX_FRAME_INDEX + 1) % (MAX_FRAME_INDEX + 1);
|
||||
slot_difference = (current_slot - ref_slot + MAX_SLOT_INDEX + 1) % (MAX_SLOT_INDEX + 1);
|
||||
printf("\n First frame.slot: %d.%d, current frame.slot: %d.%d, diff frame.slot: %d.%d",
|
||||
ref_frame, ref_slot,
|
||||
current_frame, current_slot,
|
||||
frame_difference, slot_difference);
|
||||
|
||||
if ((ref_frame != current_frame) || (ref_slot != current_slot)) {
|
||||
start_recording = true;
|
||||
printf("\n Start recording from frame: %d, slot: %d ", e.e[trace_msg_data.frame].i, e.e[trace_msg_data.slot].i);}
|
||||
}
|
||||
// start recording from the next frame to mitigate capturing partial data
|
||||
if (start_recording == true) {
|
||||
/* this is how to access the elements of the Data Collection trace messages.
|
||||
* we use e.e[<element>] and then the correct suffix, here
|
||||
* it's .i for the integer and .b for the buffer and .bsize for the buffer size
|
||||
* see in event.h the structure event_arg
|
||||
*/
|
||||
unsigned char *buf = e.e[trace_msg_data.data].b;
|
||||
|
||||
printf("\n\nRecord number: %d", nrecord_idx);
|
||||
#ifdef DEBUG_BUFFER
|
||||
printf("\nBuffer index in bytes: %d", bufIdx_wr);
|
||||
#endif
|
||||
|
||||
// add general message header: message ID,
|
||||
// T-Tracer Message format
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
|
||||
// int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_tx : int,number_of_bits : buffer,data
|
||||
//printf("\nTX : %d", e.e[trace_msg_data.nb_antennas_tx].i);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.type, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.frame].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.slot].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.datetime_yyyymmdd].i, sizeof(uint32_t));
|
||||
bufIdx_wr += sizeof(uint32_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.datetime_hhmmssmmm].i, sizeof(uint32_t));
|
||||
bufIdx_wr += sizeof(uint32_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.frame_type].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.freq_range].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.subcarrier_spacing].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.cyclic_prefix].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.symbols_per_slot].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.Nid_cell].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rnti].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rb_size].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rb_start].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.start_symbol_index].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nr_of_symbols].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.qam_mod_order].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.mcs_index].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.mcs_table].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nrOfLayers].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.transform_precoding].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_config_type].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.ul_dmrs_symb_pos].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.number_dmrs_symbols].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_port].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_nscid].i, sizeof(uint16_t));
|
||||
bufIdx_wr += sizeof(uint16_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nb_antennas_tx].i, sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.number_of_bits].i, sizeof(uint32_t));
|
||||
bufIdx_wr += sizeof(uint32_t);
|
||||
|
||||
printf("\nTime Stamp: %d_%d", e.e[trace_msg_data.datetime_yyyymmdd].i, e.e[trace_msg_data.datetime_hhmmssmmm].i);
|
||||
|
||||
// add message body: length in bytes + recorded data
|
||||
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.data].bsize, sizeof(uint32_t));
|
||||
bufIdx_wr += sizeof(uint32_t);
|
||||
|
||||
// read data from buffer and convert from unsigned char * array to int 16 using the right endianness
|
||||
// T-tracer: Little Endian
|
||||
// For BITS Messages, example: UE_PHY_UL_SCRAMBLED_TX_BITS: data in bytes
|
||||
|
||||
if(is_bits_messages(traces_bits_support_data_Collection_format_idx, n_bits_msgs, e.type)) {
|
||||
for (int byte_idx = 0; byte_idx < e.e[trace_msg_data.data].bsize; byte_idx+=1) {
|
||||
//printf("%d, ", buf[byte_idx]);
|
||||
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx], sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
for (int byte_idx = 0; byte_idx < e.e[trace_msg_data.data].bsize; byte_idx+=2) {
|
||||
//For a little-endian system:
|
||||
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx], sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx+1], sizeof(uint8_t));
|
||||
bufIdx_wr += sizeof(uint8_t);
|
||||
}
|
||||
}
|
||||
/*
|
||||
for (int byte_idx_i = 0; byte_idx_i < e.e[d.nr_ul_fd_dmrs_data].bsize; byte_idx_i+=4) {
|
||||
int16_t I = buf[byte_idx_i] | (buf[byte_idx_i+1] << 8);
|
||||
int16_t Q = buf[byte_idx_i+2] | (buf[byte_idx_i+3] << 8);
|
||||
printf ("idx %d, ", byte_idx_i);
|
||||
printf ("\n%d", I);
|
||||
printf ("\n%d", Q);
|
||||
}
|
||||
*/
|
||||
// check if the current frame and slot are different from the previous frame and slot
|
||||
// Then, increase the record index
|
||||
current_frame = e.e[trace_msg_data.frame].i;
|
||||
current_slot = e.e[trace_msg_data.slot].i;
|
||||
|
||||
// increase sync offset index if the current frame is different from the previous frame
|
||||
if ((current_frame != prev_frame) && nrecord_idx >= number_records) {
|
||||
sync_offset_index++;
|
||||
}
|
||||
|
||||
if (current_frame != prev_frame || current_slot != prev_slot) {
|
||||
nrecord_idx++;
|
||||
// Update previous frame and slot numbers
|
||||
prev_frame = current_frame;
|
||||
prev_slot = current_slot;
|
||||
}
|
||||
#ifdef DEBUG_T_Tracer
|
||||
printf ("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type),e.type);
|
||||
printf("frame %d, slot %d, datetime %d_%d\n",
|
||||
e.e[trace_msg_data.frame].i,
|
||||
e.e[trace_msg_data.slot].i,
|
||||
e.e[trace_msg_data.datetime_yyyymmdd].i,
|
||||
e.e[trace_msg_data.datetime_hhmmssmmm].i);
|
||||
printf("frame_type %d, freq_range %d, subcarrier_spacing %d, cyclic_prefix %d, symbols_per_slot %d\n",
|
||||
e.e[trace_msg_data.frame_type].i,
|
||||
e.e[trace_msg_data.freq_range].i,
|
||||
e.e[trace_msg_data.subcarrier_spacing].i,
|
||||
e.e[trace_msg_data.cyclic_prefix].i,
|
||||
e.e[trace_msg_data.symbols_per_slot].i);
|
||||
printf("Nid_cell %d, rnti %d, rb_size %d, rb_start %d, start_symbol_index %d, nr_of_symbols %d\n",
|
||||
e.e[trace_msg_data.Nid_cell].i,
|
||||
e.e[trace_msg_data.rnti].i,
|
||||
e.e[trace_msg_data.rb_size].i,
|
||||
e.e[trace_msg_data.rb_start].i,
|
||||
e.e[trace_msg_data.start_symbol_index].i,
|
||||
e.e[trace_msg_data.nr_of_symbols].i);
|
||||
printf("qam_mod_order %d, mcs_index %d, mcs_table %d, nrOfLayers %d, transform_precoding %d\n",
|
||||
e.e[trace_msg_data.qam_mod_order].i,
|
||||
e.e[trace_msg_data.mcs_index].i,
|
||||
e.e[trace_msg_data.mcs_table].i,
|
||||
e.e[trace_msg_data.nrOfLayers].i,
|
||||
e.e[trace_msg_data.transform_precoding].i);
|
||||
printf("dmrs_config_type %d, ul_dmrs_symb_pos %d, number_dmrs_symbols %d, dmrs_port %d, dmrs_nscid %d\n",
|
||||
e.e[trace_msg_data.dmrs_config_type].i,
|
||||
e.e[trace_msg_data.ul_dmrs_symb_pos].i,
|
||||
e.e[trace_msg_data.number_dmrs_symbols].i,
|
||||
e.e[trace_msg_data.dmrs_port].i,
|
||||
e.e[trace_msg_data.dmrs_nscid].i);
|
||||
printf("nb_antennas_tx %d, number_of_bits %d, data size %d\n",
|
||||
e.e[trace_msg_data.nb_antennas_tx].i,
|
||||
e.e[trace_msg_data.number_of_bits].i,
|
||||
e.e[trace_msg_data.data].bsize);
|
||||
#endif
|
||||
} // End of start recording flag
|
||||
} // end of if statement for the supported messages based on Data Collection Trace Messages Structure
|
||||
else {
|
||||
printf("ERROR: Requested Message is not based on Data Collection Trace Messages Structure\n");
|
||||
printf("ERROR: Requested Message ID: %d\n", e.type);
|
||||
}
|
||||
} // end of if statement for the requested messages
|
||||
} // end while loop of reading events
|
||||
else {
|
||||
// No data, just loop and check time
|
||||
usleep(100); // optional: avoid busy-waiting
|
||||
}
|
||||
} // End of while loop to read messages
|
||||
|
||||
// de-activate the tracee in the nr-softmodem
|
||||
printf("\n De-activated Tracer message:\n");
|
||||
for (i = 0; i < num_req_tracer_msgs; i++){
|
||||
char *on_off_name= event_name_from_id(database, req_tracer_msgs_indices[i]);
|
||||
on_off(database, on_off_name, is_on, 0);
|
||||
//printf("\n %d, %s, ", req_tracer_msgs_indices[i], on_off_name);
|
||||
//on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
|
||||
}
|
||||
// De-activate the tracee in the nr-softmodem
|
||||
activate_traces(socket, number_of_events, is_on);
|
||||
printf("\n De-activated Traces");
|
||||
|
||||
// Get the end time
|
||||
struct timespec end_time = get_current_time();
|
||||
// Calculate the time difference
|
||||
double time_diff = calculate_time_difference(start_time, end_time);
|
||||
printf("Total Time difference: %.2f ms\n", time_diff);
|
||||
printf("Time difference per record: %.2f ms\n", time_diff/(number_records+ max_sync_offset));
|
||||
|
||||
// discard stale or previous record data for the first DISCARD_RECORD_DURATION_MS
|
||||
struct timespec record_start, record_now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &record_start);
|
||||
|
||||
while (1) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &record_now);
|
||||
double elapsed_ms = calculate_time_difference(record_start, record_now);
|
||||
if (elapsed_ms >= DISCARD_RECORD_DURATION_MS) {
|
||||
break; // Stop after 10ms
|
||||
}
|
||||
int poll_ret = poll(&event_poll_fd, 1, 1); // 1 ms timeout for poll
|
||||
if (poll_ret > 0 && (event_poll_fd.revents & POLLIN)) {
|
||||
event e = get_event(socket, &ebuf, database);
|
||||
printf("%d", e.type);
|
||||
if (e.type == -1) {
|
||||
printf("\n Link broken or unexpected message received. Re-establishing connection...\n");
|
||||
reestablish_connection(&socket, ip, port, number_of_events, is_on);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// No data, just loop and check time
|
||||
usleep(100); // optional: avoid busy-waiting
|
||||
}
|
||||
} // End of while loop to discard stale records
|
||||
}
|
||||
} // End a while loop to check for the "stop" command
|
||||
// de-activate the tracee in the nr-softmodem
|
||||
|
||||
//free_database(database); //Do on one app, for example on gNB App
|
||||
free(is_on);
|
||||
close(socket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"id": 0,
|
||||
"test_config": {
|
||||
"test_scenario_id": "",
|
||||
"test_name": "phy_ul_bler_test",
|
||||
"test_mode": "rf_real_time",
|
||||
"dut_type": "trad_rx"
|
||||
},
|
||||
"environment_emulation": {
|
||||
"target_link_config": {
|
||||
"ran_config": {
|
||||
"uplink": {
|
||||
"ul_used_signal_BW_MHz": 2.16
|
||||
}
|
||||
},
|
||||
"wireless_channel": {
|
||||
"downlink": {
|
||||
"type": "rf_cable"
|
||||
},
|
||||
"uplink": {
|
||||
"type": "statistical_var",
|
||||
"statistical": {
|
||||
"var_power_delay_profile": "tdl_d",
|
||||
"speed_mps": 5.0,
|
||||
"delay_spread_ns": 50.0,
|
||||
"snr_db": 20.0,
|
||||
"predef_channel_profile": null
|
||||
}
|
||||
},
|
||||
"carrierFreqValueList_hz": [
|
||||
3319680000.0
|
||||
],
|
||||
"ul_used_signal_BW_MHz": 2.16
|
||||
}
|
||||
}
|
||||
},
|
||||
"data_recording_config": {
|
||||
"data_storage_path": "/home/user/workarea/oai_recorded_data/",
|
||||
"data_file_format": "SigMF",
|
||||
"enable_saving_tracer_messages_sigmf": true,
|
||||
"num_records": 5,
|
||||
"t_tracer_message_definition_file": "../T/T_messages.txt",
|
||||
"parameter_map_file": "config/wireless_link_parameter_map.yaml",
|
||||
"start_frame_number": 5,
|
||||
"base_station": {
|
||||
"requested_tracer_messages": [
|
||||
"GNB_PHY_UL_FD_PUSCH_IQ",
|
||||
"GNB_PHY_UL_FD_DMRS",
|
||||
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
|
||||
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL",
|
||||
"GNB_PHY_UL_PAYLOAD_RX_BITS"
|
||||
],
|
||||
"meta_data":{
|
||||
"num_rx_antennas": 1,
|
||||
"tx_gain": 48.0,
|
||||
"rx_gain": 30.0,
|
||||
"hw_type": "USRP X410",
|
||||
"hw_subtype": "ZBX",
|
||||
"seid": "328AB35"
|
||||
}
|
||||
},
|
||||
"user_equipment": {
|
||||
"requested_tracer_messages": [
|
||||
"UE_PHY_UL_SCRAMBLED_TX_BITS",
|
||||
"UE_PHY_UL_PAYLOAD_TX_BITS"
|
||||
],
|
||||
"meta_data":{
|
||||
"num_tx_antennas": 1,
|
||||
"tx_gain": 48.0,
|
||||
"rx_gain": 40.0,
|
||||
"hw_type": "USRP X410",
|
||||
"hw_subtype": "ZBX",
|
||||
"seid": "323F75F"
|
||||
}
|
||||
},
|
||||
"common_meta_data": {
|
||||
"sample_rate": 61440000.0,
|
||||
"bandwidth": 40000000.0,
|
||||
"clock_reference": "external"
|
||||
},
|
||||
"tracer_service_baseStation_address": "127.0.0.1:2021",
|
||||
"tracer_service_userEquipment_address": "10.88.136.68:2023"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/config/wireless_link_parameter_map.yaml
|
||||
# brief Wireless Link Parameter Map Dictionary
|
||||
# author Abdo Gaber
|
||||
# date 2024
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
|
||||
# Wireless Link Parameter Map
|
||||
# Components:
|
||||
# Transmitter: list of waveform configuration based on standard
|
||||
# Parameters pair is given via this parameter map dictionary (Parameter name in Waveform creator vs that in the API ~ SigMF meta-data)
|
||||
# Having a separate dictionary ease of adoption in case of:
|
||||
# - Adding new parameters
|
||||
# - Use different waveform creator that has Different naming scheme
|
||||
# - Define your own customized standard or new wireless system
|
||||
# - All changes are limited to parameter map dictionary
|
||||
# the parameter name of waveform generator has the following style:
|
||||
# StandardName_WaveformGenerator_parameter, i.e.
|
||||
# if standard = 5gnr, WaveformGenerator: ni_rfmx_rfws, then the parameter name: 5gnr_ni_rfmx_rfws_parameter
|
||||
# =====================================================================================
|
||||
# ======== list of parameter pairs between waveform config and API ~ SigMF Meta-Data ===
|
||||
# =====================================================================================
|
||||
|
||||
# List of waveform generators that generate waveforms
|
||||
# key:
|
||||
# standard: "name"
|
||||
# generator: "link or text ... etc"
|
||||
# key: Used to create parameter names in wireless_link_parameter_map
|
||||
# Recommended name for each waveform generator to be given as: standard_WaveformGenerator
|
||||
# Example: 5g NR waveform created using RFmx with RFWS config file: "5gnr_ni_rfmx_rfws"
|
||||
# Note: The suffix "_parameter" will be added automatically
|
||||
# standard name: occur in meta-data
|
||||
# generator: generator description to occur in meta-data
|
||||
|
||||
waveform_generator:
|
||||
# waveform_source: standard
|
||||
5gnr_oai: # Generated using OAI
|
||||
standard: "5gnr"
|
||||
generator: "OpenAirInterface: https://www.openairinterface.org/"
|
||||
5gnr_ni_rfmx_rfws: # Generated using NI RFmx Tool
|
||||
standard: "5gnr"
|
||||
generator: "NI RFmx Waveform Creator: https://www.ni.com/en-ca/shop/wireless-design-test/application-software-for-wireless-design-test-category/what-is-rfmx.html"
|
||||
transmitter:
|
||||
# 5G NR subfield
|
||||
5gnr:
|
||||
# frame number
|
||||
- sigmf_parameter_name: "frame"
|
||||
description: "5G NR frame number"
|
||||
5gnr_oai_parameter:
|
||||
name: "frame"
|
||||
# slot number
|
||||
- sigmf_parameter_name: "slot"
|
||||
description: "5G NR slot number"
|
||||
5gnr_oai_parameter:
|
||||
name: "slot"
|
||||
# bandwidth
|
||||
- sigmf_parameter_name: "bandwidth"
|
||||
description: "5G NR bandwidth"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "Bandwidth (Hz)"
|
||||
5gnr_oai_parameter:
|
||||
name: "bandwidth"
|
||||
# Frame structure
|
||||
- sigmf_parameter_name: "frame_structure"
|
||||
description: "5G NR duplexing scheme FDD, TDD, user defined"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "Duplex Scheme" # not exact, since it depends if the waveform is for UL or DL
|
||||
5gnr_oai_parameter:
|
||||
name: "frame_type"
|
||||
value_map: # simu value : SigMF value
|
||||
0: "fdd"
|
||||
1: "tdd"
|
||||
# Link direction
|
||||
- sigmf_parameter_name: "link_direction"
|
||||
description: "Link direction"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "Link Direction"
|
||||
# Frequency Range
|
||||
- sigmf_parameter_name: "frequency_range"
|
||||
description: "Frequency Range"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "Frequency Range"
|
||||
value_map: # simu value : SigMF value
|
||||
"Range 1": "fr1"
|
||||
"Range 2": "fr2"
|
||||
5gnr_oai_parameter:
|
||||
name: "freq_range"
|
||||
value_map: # simu value : SigMF value
|
||||
0: "fr1"
|
||||
1: "fr2"
|
||||
# Subcarrier spacing in Hz
|
||||
- sigmf_parameter_name: "subcarrier_spacing"
|
||||
description: "Subcarrier spacing in Hz"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: Subcarrier Spacing (Hz)
|
||||
5gnr_oai_parameter:
|
||||
name: subcarrier_spacing
|
||||
value_map: # simu value : SigMF value
|
||||
0: 15000
|
||||
1: 30000
|
||||
2: 60000
|
||||
3: 120000
|
||||
# Sampling rate in Hz
|
||||
- sigmf_parameter_name: "sample_rate"
|
||||
description: "Sampling rate in Hz"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "sample_rate_hz"
|
||||
5gnr_oai_parameter:
|
||||
name: "sample_rate"
|
||||
# Cyclic prefix mode
|
||||
- sigmf_parameter_name: "cp_mode"
|
||||
description: "Cyclic prefix mode"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "Cyclic Prefix Mode"
|
||||
5gnr_oai_parameter:
|
||||
name: "cyclic_prefix"
|
||||
value_map: # simu value : SigMF value
|
||||
0: "normal"
|
||||
1: "extended"
|
||||
# cell ID
|
||||
- sigmf_parameter_name: "cell_id"
|
||||
description: "cell ID"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "Cell ID"
|
||||
5gnr_oai_parameter:
|
||||
name: "Nid_cell"
|
||||
# rnti
|
||||
- sigmf_parameter_name: "rnti"
|
||||
description: "RNTI"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "rnti"
|
||||
5gnr_oai_parameter:
|
||||
name: "rnti"
|
||||
# Symbols per slot
|
||||
- sigmf_parameter_name: "symbols_per_slot"
|
||||
description: "Symbols per slot"
|
||||
5gnr_oai_parameter:
|
||||
name: "symbols_per_slot"
|
||||
# PUSCH: content
|
||||
- sigmf_parameter_name: "pusch:content"
|
||||
description: "PUSCH: content"
|
||||
# PUSCH: channel code
|
||||
- sigmf_parameter_name: "pusch:channel_code"
|
||||
description: "PUSCH: channel code"
|
||||
# PUSCH: number of PRBs
|
||||
- sigmf_parameter_name: "pusch:num_prb"
|
||||
description: "PUSCH Resource Block Size"
|
||||
5gnr_oai_parameter:
|
||||
name: "rb_size"
|
||||
# PUSCH RB Start index
|
||||
- sigmf_parameter_name: "pusch:start_prb"
|
||||
description: "PUSCH Resource Block Start"
|
||||
5gnr_oai_parameter:
|
||||
name: "rb_start"
|
||||
# PUSCH MAPPING type
|
||||
- sigmf_parameter_name: "pusch:mapping_type"
|
||||
description: "PUSCH Mapping Type"
|
||||
5gnr_oai_parameter:
|
||||
name: "mappingType"
|
||||
value_map: # simu value : SigMF value
|
||||
"NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA": "A"
|
||||
"NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeB": "B"
|
||||
# PUSCH: OFDM symbol start index within slot
|
||||
- sigmf_parameter_name: "pusch:start_ofdm_symbol"
|
||||
description: "PUSCH: OFDM symbol start index within slot"
|
||||
5gnr_oai_parameter:
|
||||
name: "start_symbol_index"
|
||||
# PUSCH: number of OFDM symbols in one slot
|
||||
- sigmf_parameter_name: "pusch:num_ofdm_symbols"
|
||||
description: "PUSCH: number of OFDM symbols in one slot"
|
||||
5gnr_oai_parameter:
|
||||
name: "nr_of_symbols"
|
||||
# PUSCH: modulation
|
||||
- sigmf_parameter_name: "pusch:modulation"
|
||||
description: "PUSCH: modulation"
|
||||
5gnr_ni_rfmx_rfws_parameter:
|
||||
name: "UL Modulation Type"
|
||||
value_map: # simu value : SigMF value
|
||||
"8PSK": "8_psk"
|
||||
"QPSK": "qpsk"
|
||||
"QAM16": "16_qam"
|
||||
"QAM64": "64_qam"
|
||||
"QAM256": "256_qam"
|
||||
"QAM1024": "1024_qam"
|
||||
"Standard": ""
|
||||
5gnr_oai_parameter:
|
||||
name: "qam_mod_order"
|
||||
value_map: # simu value : SigMF value
|
||||
2 : "qpsk"
|
||||
4 : "16_qam"
|
||||
6 : "64_qam"
|
||||
8 : "256_qam"
|
||||
"Standard": ""
|
||||
# PUSCH MCS Index
|
||||
- sigmf_parameter_name: "pusch:mcs"
|
||||
description: "PUSCH MCS Index"
|
||||
5gnr_oai_parameter:
|
||||
name: "mcs_index"
|
||||
# PUSCH MCS Index Table
|
||||
- sigmf_parameter_name: "pusch:mcs_table_index"
|
||||
description: "PUSCH MCS Table Index"
|
||||
5gnr_oai_parameter:
|
||||
name: "mcs_table"
|
||||
# PUSCH: number of transmission layers
|
||||
- sigmf_parameter_name: "pusch:num_layer"
|
||||
description: "PUSCH: number of transmission layers"
|
||||
5gnr_oai_parameter:
|
||||
name: "nrOfLayers"
|
||||
# Transform Precoding
|
||||
- sigmf_parameter_name: "pusch:transform_precoding"
|
||||
description: "PUSCH Transform Precoding"
|
||||
5gnr_oai_parameter:
|
||||
name: "transform_precoding"
|
||||
value_map: # simu value : SigMF value
|
||||
0: "enabled"
|
||||
1: "disabled"
|
||||
# PUSCH DMRS: content
|
||||
- sigmf_parameter_name: "pusch_dmrs:content"
|
||||
description: "PUSCH DMRS: content"
|
||||
#"none": "none"
|
||||
#"random_qpsk": "random_qpsk"
|
||||
#"compliant": "compliant"
|
||||
# PUSCH DMRS: resource map configuration
|
||||
- sigmf_parameter_name: "pusch_dmrs:resource_map_config"
|
||||
description: "PUSCH DMRS: resource map configuration"
|
||||
5gnr_oai_parameter:
|
||||
name: "dmrs_config_type"
|
||||
value_map:
|
||||
0: "5g nr type 1"
|
||||
1: "5g nr type 2"
|
||||
# PUSCH DMRS: OFDM symbol start index within slot
|
||||
- sigmf_parameter_name: "pusch_dmrs:start_ofdm_symbol"
|
||||
description: "PUSCH DMRS: OFDM symbol start index within slot"
|
||||
5gnr_oai_parameter:
|
||||
# to be derived from this OAI parameter
|
||||
name: "ul_dmrs_symb_pos"
|
||||
# PUSCH DMRS: DMRS duration in number of OFDM symbols
|
||||
- sigmf_parameter_name: "pusch_dmrs:duration_num_ofdm_symbols"
|
||||
description: "PUSCH DMRS: DMRS duration in number of OFDM symbols"
|
||||
5gnr_oai_parameter:
|
||||
# to be derived from this OAI parameter
|
||||
name: "ul_dmrs_symb_pos"
|
||||
# PUSCH DMRS: OFDM symbol indices within resource grid
|
||||
- sigmf_parameter_name: "pusch_dmrs:ofdm_symbol_idx"
|
||||
description: "PUSCH DMRS: OFDM symbol indices within resource grid"
|
||||
5gnr_oai_parameter:
|
||||
# to be derived from this OAI parameter
|
||||
name: "ul_dmrs_symb_pos"
|
||||
# PUSCH DMRS: number of additional PUSCH DMRS positions
|
||||
- sigmf_parameter_name: "pusch_dmrs:num_add_positions"
|
||||
description: "PUSCH DMRS: number of additional PUSCH DMRS positions"
|
||||
5gnr_oai_parameter:
|
||||
# to be derived from this OAI parameter
|
||||
name: "ul_dmrs_symb_pos"
|
||||
# PUSCH DMRS: antenna port
|
||||
- sigmf_parameter_name: "pusch_dmrs:antennna_port"
|
||||
description: "PUSCH DMRS: antenna port"
|
||||
5gnr_oai_parameter:
|
||||
name: "dmrs_port"
|
||||
# PUSCH DMRS: scrambler initialization
|
||||
- sigmf_parameter_name: "pusch_dmrs:nscid"
|
||||
description: "PUSCH DMRS: scrambler initialization"
|
||||
5gnr_oai_parameter:
|
||||
name: "dmrs_nscid"
|
||||
# number of rx antennas
|
||||
- sigmf_parameter_name: "num_rx_antennas"
|
||||
description: "Number of Rx antennas"
|
||||
5gnr_oai_parameter:
|
||||
name: "nb_antennas_rx"
|
||||
# number of tx antennas
|
||||
- sigmf_parameter_name: "num_tx_antennas"
|
||||
description: "Number of Tx antennas"
|
||||
5gnr_oai_parameter:
|
||||
name: "nb_antennas_tx"
|
||||
|
||||
channel:
|
||||
# Signal-to-noise ratio (Es/N0) in dB
|
||||
- sigmf_parameter_name: "snr_esn0_db"
|
||||
description: "Signal-to-noise ratio (Es/N0) in dB"
|
||||
# Signal-to-noise ratio (Eb/N0) in dB
|
||||
- sigmf_parameter_name: "snr_ebn0_db"
|
||||
description: "Signal-to-noise ratio (Eb/N0) in dB"
|
||||
# Channel model
|
||||
- sigmf_parameter_name: "channel_model"
|
||||
description: "Fading channel model"
|
||||
# Number of channel taps
|
||||
- sigmf_parameter_name: "num_channel_taps"
|
||||
description: "Number of channel taps"
|
||||
# Channel attenuation level in dB
|
||||
- sigmf_parameter_name: "channel_attenuation_db"
|
||||
description: "Channel attenuation level in dB"
|
||||
# Nominal delay spread in s
|
||||
- sigmf_parameter_name: "delay_spread"
|
||||
description: "Nominal delay spread in s"
|
||||
# UE speed in m/s
|
||||
- sigmf_parameter_name: "speed"
|
||||
description: "UE speed in m/s"
|
||||
# Carrier frequency in Hz
|
||||
- sigmf_parameter_name: "carrier_frequency"
|
||||
description: "Carrier frequency in Hz"
|
||||
receiver:
|
||||
# Frequency domain interpolation method for channel estimation
|
||||
- sigmf_parameter_name: "channel_estimation_freq_interpolation"
|
||||
description: "Frequency domain interpolation method for channel estimation"
|
||||
# Time domain interpolation method for channel estimation
|
||||
- sigmf_parameter_name: "channel_estimation_time_interpolation"
|
||||
description: "Time domain interpolation method for channel estimation"
|
||||
# Channel equalization method
|
||||
- sigmf_parameter_name: "channel_equalization_method"
|
||||
description: "Channel equalization method"
|
||||
# Symbol demapping method
|
||||
- sigmf_parameter_name: "symbol_demapping_method"
|
||||
description: "Symbol demapping method"
|
||||
# Manufacturer of the hardware used to receive the signal
|
||||
- sigmf_parameter_name: "manufacturer"
|
||||
description: "Manufacturer"
|
||||
# Unique ID of the emitter, USRP serial number --> usrp_mboard_serial
|
||||
- sigmf_parameter_name: "seid"
|
||||
description: "Emitter ID (USRP serial number)"
|
||||
# For USRP, use “daughterboard ID, a string read out from the device shortened by the hex ID e.g. ‘(0x0085)’
|
||||
- sigmf_parameter_name: "hw_subtype"
|
||||
description: "Hardware subtype (for USRP: daughterboard ID)"
|
||||
# RX sync reference source ("internal", "external", "gpsdo")
|
||||
- sigmf_parameter_name: "clock_reference"
|
||||
description: "RX clock reference"
|
||||
required_field: false
|
||||
# Estimated SNR in dB
|
||||
- sigmf_parameter_name: "snr_estimate_db"
|
||||
description: "SNR estimate in dB"
|
||||
required_field: false
|
||||
# Result of coarse PSS-based timing estimation (used for synchronization)
|
||||
- sigmf_parameter_name: "timing_estimate_coarse"
|
||||
description: "Coarse timing estimate in samples"
|
||||
required_field: false"
|
||||
# Result of coarse PSS-based frequency offset estimation (used for synchronization)
|
||||
- sigmf_parameter_name: "cfo_estimate_coarse"
|
||||
description: "Coarse frequency estimate in Hz"
|
||||
required_field: false
|
||||
# Result of fine CRS-based frequency offset estimation (used for synchronization)
|
||||
- sigmf_parameter_name: "cfo_estimate_fine"
|
||||
description: "Fine frequency estimate in Hz"
|
||||
required_field: false
|
||||
# Number of transmitted payload bits
|
||||
- sigmf_parameter_name: "num_payload_bits"
|
||||
description: "Number of transmitted payload bits"
|
||||
required_field: false
|
||||
# Number of bit errors when applying traditional RX processing
|
||||
- sigmf_parameter_name: "bit_errors_trad_rx"
|
||||
description: "Bit errors"
|
||||
required_field: false
|
||||
832
common/utils/data_recording/data_recording_app_v1.0.py
Normal file
@@ -0,0 +1,832 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/data_recording_app_v1.0.py
|
||||
# brief main application of synchronized real-time data recording
|
||||
# author Abdo Gaber
|
||||
# date 2025
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
import sysv_ipc as ipc
|
||||
import struct
|
||||
import time
|
||||
from datetime import datetime
|
||||
from termcolor import colored
|
||||
import numpy as np
|
||||
import json
|
||||
import concurrent.futures
|
||||
# from concurrent import futures
|
||||
import threading
|
||||
# import related functions
|
||||
from lib import sigmf_interface
|
||||
# import library functions
|
||||
from lib import sync_service
|
||||
from lib import data_recording_messages_def
|
||||
from lib import common_utils
|
||||
from lib import config_interface
|
||||
|
||||
DEBUG_WIRELESS_RECORDED_DATA = True
|
||||
DEBUG_BUFFER_READING = False
|
||||
|
||||
# globally applicable metadata
|
||||
global_info = {
|
||||
"author": "Abdo Gaber",
|
||||
"description": "Synchronized Real-Time Data Recording",
|
||||
"timestamp": 0,
|
||||
"collection_file_prefix": "data-collection", # collection file name prefix "deap-rx + str(...)"
|
||||
"collection_file": "", # Reserved to be created in the code: “data-collection_rec-0_TIME-STAMP”
|
||||
"datetime_offset": "", # datetime offset between current location and UTC/Zulu timezone
|
||||
# Example: "+01:00" for Berlin, Germany
|
||||
"save_config_data_recording_app_json": True,
|
||||
"waveform_generator": "5gnr_oai",
|
||||
"extensions": {},
|
||||
}
|
||||
|
||||
# Supported OAI Trace messages
|
||||
# UL receiver messages
|
||||
# gNB IQ Msgs: "GNB_PHY_UL_FD_PUSCH_IQ", "GNB_PHY_UL_FD_DMRS", "GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
|
||||
# "GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL"
|
||||
# gNB BITS Msgs: "GNB_PHY_UL_PAYLOAD_RX_BITS"
|
||||
# UE BITS Msgs: "UE_PHY_UL_SCRAMBLED_TX_BITS", "UE_PHY_UL_PAYLOAD_TX_BITS"
|
||||
|
||||
supported_oai_tracer_messages = {
|
||||
# gNB messages
|
||||
"GNB_PHY_UL_FD_PUSCH_IQ": {
|
||||
"file_name_prefix": "rx-fd-data",
|
||||
"scope": "gNB",
|
||||
"description": "Frequency-domain RX data",
|
||||
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
|
||||
},
|
||||
"GNB_PHY_UL_FD_DMRS": {
|
||||
"file_name_prefix": "tx-pilots-fd-data",
|
||||
"scope": "gNB",
|
||||
"description": "Frequency-domain TX PUSCH DMRS data",
|
||||
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
|
||||
},
|
||||
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS": {
|
||||
"file_name_prefix": "raw-ce-fd-data",
|
||||
"scope": "gNB",
|
||||
"description": "Frequency-domain raw channel estimates (at DMRS positions)",
|
||||
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
|
||||
},
|
||||
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL": {
|
||||
"file_name_prefix": "raw-inter-ce-fd-data",
|
||||
"scope": "gNB",
|
||||
"description": "Interpolcated Frequency-domain raw channel estimates",
|
||||
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
|
||||
},
|
||||
"GNB_PHY_UL_PAYLOAD_RX_BITS": {
|
||||
"file_name_prefix": "rx-payload-bits",
|
||||
"scope": "gNB",
|
||||
"description": "Received PUSCH payload bits",
|
||||
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
|
||||
},
|
||||
# UE messages
|
||||
"UE_PHY_UL_SCRAMBLED_TX_BITS": {
|
||||
"file_name_prefix": "tx-scrambled-bits",
|
||||
"scope": "UE",
|
||||
"description": "Transmitted scrambled PUSCH bits",
|
||||
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
|
||||
},
|
||||
"UE_PHY_UL_PAYLOAD_TX_BITS": {
|
||||
"file_name_prefix": "tx-payload-bits",
|
||||
"scope": "UE",
|
||||
"description": "Transmitted PUSCH payload bits",
|
||||
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
|
||||
},
|
||||
}
|
||||
# -------------------------------------------
|
||||
# System configuration: gNB
|
||||
project_id_gnb = 2335
|
||||
read_shm_path_gnb = "/tmp/gnb_app1"
|
||||
write_shm_path_gnb = "/tmp/gnb_app2"
|
||||
|
||||
# System configuration: UE
|
||||
project_id_ue = 2336
|
||||
read_shm_path_ue = "/tmp/ue_app1"
|
||||
write_shm_path_ue = "/tmp/ue_app2"
|
||||
|
||||
|
||||
# initialize shared memory
|
||||
def attach_shm(shm_path, project_id):
|
||||
key = ipc.ftok(shm_path, project_id)
|
||||
shm = ipc.SharedMemory(key, 0, 0)
|
||||
# I found if we do not attach ourselves
|
||||
# it will attach as ReadOnly.
|
||||
shm.attach(0, 0)
|
||||
return shm
|
||||
|
||||
|
||||
def detach_shm(shm):
|
||||
try:
|
||||
shm.detach()
|
||||
print("Shared memory detached successfully.")
|
||||
except ipc.ExistentialError:
|
||||
print("Shared memory segment does not exist.")
|
||||
|
||||
|
||||
def remove_shm(shm):
|
||||
try:
|
||||
shm.remove()
|
||||
print("Shared memory removed successfully.")
|
||||
except ipc.ExistentialError:
|
||||
print("Shared memory segment does not exist.")
|
||||
|
||||
|
||||
# check data if avalible in the shared memory
|
||||
def is_data_available_in_memory(shm, bufIdx, general_message_header_length, timeout=20):
|
||||
start_time = time.time()
|
||||
while True:
|
||||
buf = shm.read(bufIdx + general_message_header_length)
|
||||
n_bytes = sum(buf)
|
||||
print("Data Recording App: Waiting for Measurements!")
|
||||
if n_bytes > 0:
|
||||
print("There is data in memory, n_bytes: ", n_bytes)
|
||||
return True
|
||||
if (time.time() - start_time) > timeout:
|
||||
break
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
|
||||
# Read data from Shared memory based Data Conversion Service message structure
|
||||
def read_data_from_shm(shm, bufIdx, tracer_msgs_identities):
|
||||
# print buffer index
|
||||
if DEBUG_BUFFER_READING:
|
||||
print("Buffer Index: ", bufIdx)
|
||||
# get general message header list
|
||||
general_msg_header_list, general_message_header_length = data_recording_messages_def.get_general_msg_header_list()
|
||||
buf = shm.read(bufIdx + general_message_header_length)
|
||||
n_bytes = sum(buf)
|
||||
if n_bytes == 0:
|
||||
raise Exception('ERROR: No data available in memory')
|
||||
msg_id = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("msg_id")])[0]
|
||||
bufIdx += general_msg_header_list.get("msg_id")
|
||||
frame = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("frame")])[0]
|
||||
bufIdx += general_msg_header_list.get("frame")
|
||||
slot = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("slot")])[0]
|
||||
bufIdx += general_msg_header_list.get("slot")
|
||||
# get time stamp: yyyy mm dd hh mm ss msec
|
||||
nr_trace_time_stamp_yyymmdd = \
|
||||
struct.unpack('<i', buf[bufIdx:bufIdx+general_msg_header_list.get("datetime_yyyymmdd")])[0]
|
||||
bufIdx += general_msg_header_list.get("datetime_yyyymmdd")
|
||||
nr_trace_time_stamp_hhmmssmmm = \
|
||||
struct.unpack('<i', buf[bufIdx:bufIdx+general_msg_header_list.get("datetime_hhmmssmmm")])[0]
|
||||
bufIdx += general_msg_header_list.get("datetime_hhmmssmmm")
|
||||
time_stamp_milli_sec = str(nr_trace_time_stamp_yyymmdd)+"_"+str(nr_trace_time_stamp_hhmmssmmm)
|
||||
# get frame type
|
||||
frame_type = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("frame_type")])[0]
|
||||
bufIdx += general_msg_header_list.get("frame_type")
|
||||
# get frequency range
|
||||
freq_range = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("freq_range")])[0]
|
||||
bufIdx += general_msg_header_list.get("freq_range")
|
||||
# get subcarrier spacing
|
||||
subcarrier_spacing = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("subcarrier_spacing")])[0]
|
||||
bufIdx += general_msg_header_list.get("subcarrier_spacing")
|
||||
# get cyclic prefix
|
||||
cyclic_prefix = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("cyclic_prefix")])[0]
|
||||
bufIdx += general_msg_header_list.get("cyclic_prefix")
|
||||
# get symbols per slot
|
||||
symbols_per_slot = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("symbols_per_slot")])[0]
|
||||
bufIdx += general_msg_header_list.get("symbols_per_slot")
|
||||
# get Nid cell
|
||||
Nid_cell = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("Nid_cell")])[0]
|
||||
bufIdx += general_msg_header_list.get("Nid_cell")
|
||||
# get rnti
|
||||
rnti = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rnti")])[0]
|
||||
bufIdx += general_msg_header_list.get("rnti")
|
||||
# get rb size
|
||||
rb_size = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rb_size")])[0]
|
||||
bufIdx += general_msg_header_list.get("rb_size")
|
||||
# get rb start
|
||||
rb_start = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rb_start")])[0]
|
||||
bufIdx += general_msg_header_list.get("rb_start")
|
||||
# get start symbol index
|
||||
start_symbol_index = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("start_symbol_index")])[0]
|
||||
bufIdx += general_msg_header_list.get("start_symbol_index")
|
||||
# get number of symbols
|
||||
nr_of_symbols = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nr_of_symbols")])[0]
|
||||
bufIdx += general_msg_header_list.get("nr_of_symbols")
|
||||
# get qam modulation order
|
||||
qam_mod_order = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("qam_mod_order")])[0]
|
||||
bufIdx += general_msg_header_list.get("qam_mod_order")
|
||||
# get mcs index
|
||||
mcs_index = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("mcs_index")])[0]
|
||||
bufIdx += general_msg_header_list.get("mcs_index")
|
||||
# get mcs table
|
||||
mcs_table = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("mcs_table")])[0]
|
||||
bufIdx += general_msg_header_list.get("mcs_table")
|
||||
# get number of layers
|
||||
nrOfLayers = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nrOfLayers")])[0]
|
||||
bufIdx += general_msg_header_list.get("nrOfLayers")
|
||||
# get transform precoding
|
||||
transform_precoding = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("transform_precoding")])[0]
|
||||
bufIdx += general_msg_header_list.get("transform_precoding")
|
||||
# get dmrs config type
|
||||
dmrs_config_type = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_config_type")])[0]
|
||||
bufIdx += general_msg_header_list.get("dmrs_config_type")
|
||||
# get ul dmrs symb pos
|
||||
ul_dmrs_symb_pos = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("ul_dmrs_symb_pos")])[0]
|
||||
bufIdx += general_msg_header_list.get("ul_dmrs_symb_pos")
|
||||
# get number dmrs symbols
|
||||
number_dmrs_symbols = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("number_dmrs_symbols")])[0]
|
||||
bufIdx += general_msg_header_list.get("number_dmrs_symbols")
|
||||
# get dmrs port
|
||||
dmrs_port = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_port")])[0]
|
||||
bufIdx += general_msg_header_list.get("dmrs_port")
|
||||
# get dmrs scid
|
||||
dmrs_scid = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_scid")])[0]
|
||||
bufIdx += general_msg_header_list.get("dmrs_scid")
|
||||
# get nb antennas rx for gNB or nb antennas tx for UE
|
||||
nb_antennas = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nb_antennas")])[0]
|
||||
bufIdx += general_msg_header_list.get("nb_antennas")
|
||||
# get number of bits
|
||||
number_of_bits = struct.unpack('<I', buf[bufIdx:bufIdx+general_msg_header_list.get("number_of_bits")])[0]
|
||||
bufIdx += general_msg_header_list.get("number_of_bits")
|
||||
# get length of bytes
|
||||
length_bytes = struct.unpack('<I', buf[bufIdx:bufIdx+general_msg_header_list.get("length_bytes")])[0]
|
||||
bufIdx += general_msg_header_list.get("length_bytes")
|
||||
|
||||
# print all captured data
|
||||
if DEBUG_WIRELESS_RECORDED_DATA:
|
||||
print(" ")
|
||||
print(f"Time stamp: {time_stamp_milli_sec}")
|
||||
print(f"MSG ID: {msg_id:<5} MSG Name: {tracer_msgs_identities[msg_id]}")
|
||||
print(f"Frame: {frame:<5} Slot: {slot:<5}")
|
||||
print(f"Frame Type: {frame_type:<5} Frequency Range: {freq_range:<5}"
|
||||
f"Subcarrier Spacing: {subcarrier_spacing:<5} Cyclic Prefix: {cyclic_prefix:<5} "
|
||||
f"Symbols per Slot: {symbols_per_slot:<5}")
|
||||
print(f"Nid Cell: {Nid_cell:<5} RNTI: {rnti:<5}")
|
||||
print(f"RB Size: {rb_size:<5} RB Start: {rb_start:<5} Start Symbol Index: {start_symbol_index:<5} "
|
||||
f"Number of Symbols: {nr_of_symbols:<5}")
|
||||
print(f"QAM Modulation Order: {qam_mod_order:<5} MCS Index: {mcs_index:<5} "
|
||||
f"MCS Table: {mcs_table:<5}")
|
||||
print(f"Number of Layers: {nrOfLayers:<5} Transform Precoding: {transform_precoding:<5}")
|
||||
print(f"DMRS Config Type: {dmrs_config_type:<5} UL DMRS Symbol Position: {ul_dmrs_symb_pos:<5} "
|
||||
f"Number of DMRS Symbols: {number_dmrs_symbols:<5}")
|
||||
print(f"DMRS Port: {dmrs_port:<5} DMRS SCID: {dmrs_scid:<5} "
|
||||
f"Number of Antennas: {nb_antennas:<5}")
|
||||
print(f"Number of bits: {number_of_bits:<5} Length of bytes: {length_bytes:<5}")
|
||||
# raise exception if time stamp is zero, it means that the data is not recorded yet
|
||||
if nr_trace_time_stamp_yyymmdd == 0 and nr_trace_time_stamp_hhmmssmmm == 0:
|
||||
raise Exception("ERROR: Time stamp is zero, data is not recorded yet or something wrong, check logs!")
|
||||
# get recorded data
|
||||
buf = shm.read(bufIdx + length_bytes)
|
||||
# bit_msg_index = get_index_of_id(tracer_msgs_identities, "GNB_PHY_UL_PAYLOAD_RX_BITS")
|
||||
captured_data = {}
|
||||
# If message is bit message, store data in bytes
|
||||
# then the field number_of_bits should be not zero
|
||||
|
||||
if "_BITS" in tracer_msgs_identities[msg_id]:
|
||||
# recorded_data = buf[bufIdx:bufIdx + length_bytes]
|
||||
recorded_data = struct.unpack("<" + int(length_bytes) * 'B', buf[bufIdx:bufIdx + length_bytes])
|
||||
bufIdx += length_bytes
|
||||
# convert data in bytes to bits
|
||||
bits_vector = []
|
||||
for byte in recorded_data:
|
||||
bits_vector.extend([int(bit) for bit in format(int(byte), '08b')])
|
||||
captured_data["sigmf_data_type"] = "ri8_le"
|
||||
# convert to uint8
|
||||
captured_data["recorded_data"] = np.asarray(bits_vector).astype(np.uint8)
|
||||
# recorded_data_formated = recorded_data.astype(np.complex64) # convert to complex64
|
||||
else:
|
||||
recorded_data = struct.unpack("<" + int(length_bytes/2) * 'h', buf[bufIdx:bufIdx + length_bytes])
|
||||
bufIdx += length_bytes
|
||||
# print("IQ data I/Q: ", recorded_data)
|
||||
# Convert real data to complext data
|
||||
# converting list to array
|
||||
recorded_data = np.asarray(recorded_data)
|
||||
# recorded_data_complex = recorded_data
|
||||
recorded_data_complex = common_utils.real_to_complex(recorded_data)
|
||||
captured_data["sigmf_data_type"] = "cf32_le"
|
||||
# convert to complex64
|
||||
captured_data["recorded_data"] = recorded_data_complex.astype(np.complex64)
|
||||
# print("Recorded Data: ", captured_data["recorded_data"])
|
||||
# store data in dictonary
|
||||
captured_data["message_id"] = msg_id
|
||||
captured_data["message_type"] = tracer_msgs_identities[msg_id]
|
||||
captured_data["frame"] = frame
|
||||
captured_data["slot"] = slot
|
||||
captured_data["time_stamp"] = time_stamp_milli_sec
|
||||
captured_data["frame_type"] = frame_type
|
||||
captured_data["freq_range"] = freq_range
|
||||
captured_data["subcarrier_spacing"] = subcarrier_spacing
|
||||
captured_data["cyclic_prefix"] = cyclic_prefix
|
||||
# captured_data["symbols_per_slot"] = symbols_per_slot ... not used
|
||||
captured_data["Nid_cell"] = Nid_cell
|
||||
captured_data["rnti"] = rnti
|
||||
captured_data["rb_size"] = rb_size
|
||||
captured_data["rb_start"] = rb_start
|
||||
captured_data["start_symbol_index"] = start_symbol_index
|
||||
captured_data["nr_of_symbols"] = nr_of_symbols
|
||||
captured_data["qam_mod_order"] = qam_mod_order
|
||||
captured_data["mcs_index"] = mcs_index
|
||||
captured_data["mcs_table"] = mcs_table
|
||||
captured_data["nrOfLayers"] = nrOfLayers
|
||||
captured_data["transform_precoding"] = transform_precoding
|
||||
captured_data["dmrs_config_type"] = dmrs_config_type
|
||||
captured_data["ul_dmrs_symb_pos"] = ul_dmrs_symb_pos
|
||||
captured_data["number_dmrs_symbols"] = number_dmrs_symbols
|
||||
captured_data["dmrs_port"] = dmrs_port
|
||||
captured_data["dmrs_scid"] = dmrs_scid
|
||||
captured_data["nb_antennas"] = nb_antennas
|
||||
captured_data["number_of_bits"] = number_of_bits
|
||||
return captured_data, bufIdx
|
||||
|
||||
|
||||
# Synchronize data between gNB and UE
|
||||
def sync_data_conversion_service(
|
||||
shm_reading_gnb, shm_reading_ue, sync_info, config_meta_data, gnb_args, ue_args):
|
||||
# Initialize variables
|
||||
record_idx = 0
|
||||
prev_frame = -1
|
||||
prev_slot = -1
|
||||
ue_bufIdx = 0
|
||||
gnb_bufIdx = 0
|
||||
|
||||
gnb_args.num_requested_tracer_msgs = len(
|
||||
config_meta_data["data_recording_config"]["base_station"][
|
||||
"requested_tracer_messages"])
|
||||
ue_args.num_requested_tracer_msgs = len(
|
||||
config_meta_data["data_recording_config"]["user_equipment"][
|
||||
"requested_tracer_messages"])
|
||||
tracer_msgs_identities = config_meta_data["data_recording_config"][
|
||||
"tracer_msgs_identities"]
|
||||
|
||||
global_info = config_meta_data["data_recording_config"]["global_info"]
|
||||
|
||||
# Get UE data based on the sync data
|
||||
bufIdx = 0
|
||||
timeout_sync = time.time() + 5 # 5 seconds if no sync data found, stop the process
|
||||
while True:
|
||||
# wait for the next record
|
||||
# To do: check if we need to add exta waiting times between different events in case of
|
||||
# data streaming via network such as on UE side or gNB side
|
||||
time.sleep(0.0035) # 2.3 ms = latency of T tracer to capture data from the RAN
|
||||
ue_bufIdx = bufIdx
|
||||
captured_data, bufIdx = read_data_from_shm(shm_reading_ue, bufIdx, tracer_msgs_identities)
|
||||
if (captured_data["frame"] == sync_info["frame"]
|
||||
and captured_data["slot"] == sync_info["slot"]):
|
||||
break
|
||||
if time.time() > timeout_sync:
|
||||
raise Exception(
|
||||
"ERROR: Data Recording NO Sync Found, check Tracer Services if they are connected!")
|
||||
|
||||
# Get gNB data based on the sync data
|
||||
bufIdx = 0
|
||||
while True:
|
||||
time.sleep(0.0035)
|
||||
gnb_bufIdx = bufIdx
|
||||
captured_data, bufIdx = read_data_from_shm(
|
||||
shm_reading_gnb, bufIdx, tracer_msgs_identities)
|
||||
if (captured_data["frame"] == sync_info["frame"]
|
||||
and captured_data["slot"] == sync_info["slot"]):
|
||||
break
|
||||
|
||||
# Read Synchronized data between gNB and UE
|
||||
while True: # read all records
|
||||
print("\nRecord number: ", record_idx)
|
||||
if DEBUG_BUFFER_READING:
|
||||
print(f"Buffer Index gNB: {gnb_bufIdx}, Buffer Index UE: {ue_bufIdx}")
|
||||
# wait for the next record
|
||||
# To do: check if we need to add exta waiting times between different events in case of
|
||||
# data streaming via network such as on UE side or gNB side
|
||||
time.sleep(0.0035) # 2.3 ms = latency of T tracer to capture data from the RAN
|
||||
|
||||
collected_metafiles = []
|
||||
# Read data from gNB T-tracer Application
|
||||
for idx in range(gnb_args.num_requested_tracer_msgs):
|
||||
time.sleep(0.0015)
|
||||
if DEBUG_WIRELESS_RECORDED_DATA:
|
||||
print(f"\nRecord number: {record_idx}, Reading MSG data ", idx)
|
||||
captured_data, gnb_bufIdx = read_data_from_shm(
|
||||
shm_reading_gnb, gnb_bufIdx, tracer_msgs_identities)
|
||||
|
||||
# drive the collection file time stamp from the first message per record
|
||||
if idx == 0:
|
||||
# Get time stamp
|
||||
time_stamp_ms, time_stamp_ms_file_name = (
|
||||
sigmf_interface.time_stamp_formating(
|
||||
captured_data["time_stamp"], global_info["datetime_offset"]))
|
||||
global_info["collection_file"] = (
|
||||
global_info["collection_file_prefix"]
|
||||
+ "-rec-"
|
||||
+ str(record_idx)
|
||||
+ "-"
|
||||
+ str(time_stamp_ms_file_name)
|
||||
)
|
||||
global_info["timestamp"] = time_stamp_ms
|
||||
|
||||
# Write data into files with the given format
|
||||
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
|
||||
collected_metafiles.append(
|
||||
sigmf_interface.write_recorded_data_to_sigmf(
|
||||
captured_data, config_meta_data, global_info, record_idx))
|
||||
|
||||
# Read data from UE T-tracer Application
|
||||
for idx in range(ue_args.num_requested_tracer_msgs):
|
||||
time.sleep(0.0015)
|
||||
if DEBUG_WIRELESS_RECORDED_DATA:
|
||||
print(f"\nRecord number: {record_idx}, Reading MSG data ", idx)
|
||||
captured_data, ue_bufIdx = read_data_from_shm(
|
||||
shm_reading_ue, ue_bufIdx, tracer_msgs_identities)
|
||||
|
||||
# Write data into files with the given format
|
||||
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
|
||||
collected_metafiles.append(
|
||||
sigmf_interface.write_recorded_data_to_sigmf(
|
||||
captured_data, config_meta_data, global_info, record_idx))
|
||||
|
||||
# generate SigMF collection file
|
||||
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
|
||||
data_storage_path = config_meta_data["data_recording_config"][
|
||||
"data_storage_path"]
|
||||
description = global_info["description"]
|
||||
sigmf_interface.save_sigmf_collection(
|
||||
collected_metafiles, global_info, description, data_storage_path)
|
||||
|
||||
frame = captured_data["frame"]
|
||||
slot = captured_data["slot"]
|
||||
|
||||
# Check for changes in frame or slot
|
||||
if frame != prev_frame or slot != prev_slot:
|
||||
record_idx += 1
|
||||
# We have reached the end of the data. Break the loop
|
||||
if record_idx >= config_meta_data["data_recording_config"]["num_records"]:
|
||||
break
|
||||
# Update previous frame and slot
|
||||
prev_frame = frame
|
||||
prev_slot = slot
|
||||
|
||||
|
||||
# data conversion service
|
||||
def data_conversion_service(shm_reading, config_meta_data, args, sync_info, do_sync):
|
||||
# Initialize variables
|
||||
record_idx = 0
|
||||
prev_frame = -1
|
||||
prev_slot = -1
|
||||
bufIdx = 0
|
||||
# Read data from T-tracer Application
|
||||
print("Data Conversion Service: Reading data from T-tracer Application")
|
||||
print("Requested Tracer Messages: ", args.num_requested_tracer_msgs)
|
||||
if args.num_requested_tracer_msgs > 0:
|
||||
num_requested_tracer_msgs = args.num_requested_tracer_msgs
|
||||
else:
|
||||
raise Exception("ERROR: No requested tracer messages found!")
|
||||
|
||||
tracer_msgs_identities = config_meta_data["data_recording_config"][
|
||||
"tracer_msgs_identities"]
|
||||
global_info = config_meta_data["data_recording_config"]["global_info"]
|
||||
|
||||
if do_sync:
|
||||
print(" Find Memory Index of NR MSGs based on Sync info")
|
||||
while True:
|
||||
time.sleep(0.0035)
|
||||
station_bufIdx = bufIdx
|
||||
captured_data, bufIdx = read_data_from_shm(
|
||||
shm_reading, bufIdx, tracer_msgs_identities)
|
||||
print("*Sync Status: NR Captured Data (Frame, slot): (", captured_data["frame"],
|
||||
", ", captured_data["slot"], "), Sync Info (Frame, slot): (", sync_info["frame"],
|
||||
", ", sync_info["slot"]," )")
|
||||
if (captured_data["frame"] == sync_info["frame"]
|
||||
and captured_data["slot"] == sync_info["slot"]):
|
||||
print("*sync Pass")
|
||||
break
|
||||
print("*sync Fail")
|
||||
bufIdx = station_bufIdx
|
||||
|
||||
# Read data from T-tracer Application
|
||||
while True: # read all records
|
||||
print("\nRecord number: ", record_idx)
|
||||
if DEBUG_BUFFER_READING:
|
||||
print(f"Buffer Index: {bufIdx}")
|
||||
# wait for the next record
|
||||
# To do: check if we need to add exta waiting times between different events in case of
|
||||
# data streaming via network such as on UE side or gNB side
|
||||
time.sleep(0.0035) # 2.3 ms = latency of T tracer to capture data from the RAN
|
||||
|
||||
collected_metafiles = []
|
||||
for idx in range(num_requested_tracer_msgs):
|
||||
time.sleep(0.0015)
|
||||
if DEBUG_WIRELESS_RECORDED_DATA:
|
||||
print(f"\nRecord number: {record_idx}, Reading MSG data ", idx)
|
||||
captured_data, bufIdx = read_data_from_shm(
|
||||
shm_reading, bufIdx, tracer_msgs_identities)
|
||||
|
||||
# derive the collection file time stamp from the first message per record
|
||||
if idx == 0:
|
||||
# Get time stamp
|
||||
time_stamp_ms, time_stamp_ms_file_name = (
|
||||
sigmf_interface.time_stamp_formating(
|
||||
captured_data["time_stamp"], global_info["datetime_offset"]))
|
||||
global_info["collection_file"] = (
|
||||
global_info["collection_file_prefix"]
|
||||
+ "-rec-"
|
||||
+ str(record_idx)
|
||||
+ "-"
|
||||
+ str(time_stamp_ms_file_name))
|
||||
global_info["timestamp"] = time_stamp_ms
|
||||
# Write data into files with the given format
|
||||
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
|
||||
collected_metafiles.append(
|
||||
sigmf_interface.write_recorded_data_to_sigmf(
|
||||
captured_data, config_meta_data, global_info, record_idx))
|
||||
|
||||
frame = captured_data["frame"]
|
||||
slot = captured_data["slot"]
|
||||
|
||||
# generate SigMF collection file
|
||||
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
|
||||
data_storage_path = config_meta_data["data_recording_config"][
|
||||
"data_storage_path"]
|
||||
description = global_info["description"]
|
||||
sigmf_interface.save_sigmf_collection(
|
||||
collected_metafiles, global_info, description, data_storage_path)
|
||||
|
||||
# Check for changes in frame or slot
|
||||
if frame != prev_frame or slot != prev_slot:
|
||||
record_idx += 1
|
||||
# We have reached the end of the data. Break the loop
|
||||
if record_idx >= config_meta_data["data_recording_config"]["num_records"]:
|
||||
break
|
||||
# Update previous frame and slot
|
||||
prev_frame = frame
|
||||
prev_slot = slot
|
||||
|
||||
|
||||
# Write Tracer Control Message
|
||||
def write_shm(shm, args):
|
||||
# Note: Big Endian >, Little Endian <
|
||||
# Note: unsigned char B, signed char b, short h, int I, long long q
|
||||
# Note: float f, double d, string s, char c, bool ?
|
||||
# 1: config
|
||||
# 2: record
|
||||
# 3: quit
|
||||
print("Write Shared Memory: ", args.action)
|
||||
if args.action == "config":
|
||||
# Determine the length of the IP address
|
||||
ip_length = len(args.bytes_IPaddress) + 1 # String terminator
|
||||
# Construct the format string dynamically
|
||||
format_string = f"{ip_length}s"
|
||||
shm.write(
|
||||
# Config action
|
||||
struct.pack("<B", 1) +
|
||||
struct.pack("<B", ip_length) +
|
||||
struct.pack(format_string, args.bytes_IPaddress) +
|
||||
struct.pack("<h", int(args.port))
|
||||
)
|
||||
print("T-Tracer Config IP: ", args.bytes_IPaddress, " port: ", args.port)
|
||||
elif args.action == "record":
|
||||
shm.write(
|
||||
# Record action
|
||||
struct.pack("<B", int(2)) +
|
||||
struct.pack("<B", args.num_requested_tracer_msgs) +
|
||||
struct.pack(
|
||||
"<{}h".format(len(args.req_tracer_msgs_indices)),
|
||||
*args.req_tracer_msgs_indices,) +
|
||||
struct.pack("<I", args.num_records) +
|
||||
struct.pack("<h", args.start_frame_number)
|
||||
)
|
||||
print("T-Tracer Record: N Messags: ", args.num_requested_tracer_msgs,
|
||||
", Msg IDs: ", args.req_tracer_msgs_indices,
|
||||
", Num records: ", args.num_records,
|
||||
", Start Frame: ", args.start_frame_number,
|
||||
)
|
||||
elif args.action == "quit":
|
||||
shm.write(struct.pack("<B", int(3))) # Quit action
|
||||
print("T-Tracer Quit")
|
||||
else:
|
||||
print("Unknown action for data recording system!")
|
||||
|
||||
|
||||
# write shared memory task
|
||||
def write_shm_task(barrier, shm_id, args):
|
||||
# Wait for threads to be ready
|
||||
barrier.wait()
|
||||
# send request to related T-Tracer Application
|
||||
write_shm(shm_id, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# -------------------------------------------
|
||||
# ------------- Configuration --------------
|
||||
# ------------------------------------------
|
||||
# Data Recording Configuration
|
||||
data_recording_config_file = "config/config_data_recording.json"
|
||||
# -------------------------------------------
|
||||
# Configuration
|
||||
# -------------------------------------------
|
||||
# First: get the configuration mode either local or remote
|
||||
# Second: get data recording configuration
|
||||
# Read and parse the JSON file
|
||||
with open(data_recording_config_file, "r") as file:
|
||||
config_meta_data = json.load(file)
|
||||
|
||||
# get Configuration parameters
|
||||
config_meta_data, gnb_args, ue_args = config_interface.get_data_recording_config(config_meta_data)
|
||||
|
||||
# check if lists of requested tracer messages IDs are not empty
|
||||
if not gnb_args.requested_tracer_messages and \
|
||||
not ue_args.requested_tracer_messages:
|
||||
raise Exception("ERROR: No requested tracer messages are provided")
|
||||
|
||||
# check if gnb_requested_tracer_messages is not empty, attach to the shared memory
|
||||
if gnb_args.requested_tracer_messages:
|
||||
# attach to the shared memory
|
||||
shm_writing_gnb = attach_shm(write_shm_path_gnb, project_id_gnb)
|
||||
shm_reading_gnb = attach_shm(read_shm_path_gnb, project_id_gnb)
|
||||
|
||||
# check if ue_requested_tracer_messages is not empty, attach to the shared memory
|
||||
if ue_args.requested_tracer_messages:
|
||||
# attach to the shared memory
|
||||
shm_writing_ue = attach_shm(write_shm_path_ue, project_id_ue)
|
||||
shm_reading_ue = attach_shm(read_shm_path_ue, project_id_ue)
|
||||
|
||||
# get general message header list
|
||||
general_msg_header_list, general_message_header_length = \
|
||||
data_recording_messages_def.get_general_msg_header_list()
|
||||
|
||||
# Add supported OAI Tracer Messages
|
||||
config_meta_data["data_recording_config"]["supported_oai_tracer_messages"] = supported_oai_tracer_messages
|
||||
# Add global info
|
||||
config_meta_data["data_recording_config"]["global_info"] = global_info
|
||||
|
||||
# -------------------------------------------
|
||||
# Initialization
|
||||
# -------------------------------------------
|
||||
# -------------------------------------------
|
||||
# send Tracer Control Message request to T-Tracers Apps
|
||||
# -------------------------------------------
|
||||
# It consists of the following fields:
|
||||
# Config action
|
||||
# IP address length
|
||||
# IP address
|
||||
# Port Number
|
||||
# check if gnb_requested_tracer_messages is not empty, config T-Tracer gNB via shared memory
|
||||
if gnb_args.requested_tracer_messages:
|
||||
# Config T-Tracer via shared memory
|
||||
gnb_args.action = "config"
|
||||
write_shm(shm_writing_gnb, gnb_args)
|
||||
|
||||
# check if ue_requested_tracer_messages is not empty, config T-Tracer UE via shared memory
|
||||
if ue_args.requested_tracer_messages:
|
||||
# Config T-Tracer via shared memory
|
||||
ue_args.action = "config"
|
||||
write_shm(shm_writing_ue, ue_args)
|
||||
|
||||
time.sleep(0.5) # wait for the config to be applied
|
||||
|
||||
# -------------------------------------------
|
||||
# Execution
|
||||
# -------------------------------------------
|
||||
# send Tracer Control Message request to T-Tracers Apps
|
||||
# -------------------------------------------
|
||||
# It consists of the following fields:
|
||||
# Record action
|
||||
# Number of requested Tracer Messages
|
||||
# Requested Tracer Messages ID 1, …, ID N
|
||||
# Number of records to be recorded in slots
|
||||
# Start SFN: Frame Index to start data collection from it, useful for future
|
||||
# data sync between gNB and UE but not yet used
|
||||
print("Args:")
|
||||
if gnb_args.requested_tracer_messages:
|
||||
gnb_args.action = "record"
|
||||
print("gnb_args: ", gnb_args)
|
||||
if ue_args.requested_tracer_messages:
|
||||
ue_args.action = "record"
|
||||
print("ue_args: ", ue_args)
|
||||
|
||||
start_time = time.time()
|
||||
print("Send data logging request us:", datetime.now().strftime("%Y%m%d-%H%M%S%f"))
|
||||
|
||||
# if requested: gNB and UE Tracer Messages
|
||||
# gNB + UE Tracer Messages
|
||||
if gnb_args.requested_tracer_messages and ue_args.requested_tracer_messages:
|
||||
# Create a barrier to synchronize the threads
|
||||
barrier = threading.Barrier(2)
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
tracer_ue = executor.submit(write_shm_task, barrier, shm_writing_ue, ue_args)
|
||||
tracer_gnb = executor.submit(write_shm_task, barrier, shm_writing_gnb, gnb_args)
|
||||
# Wait for both functions to complete
|
||||
concurrent.futures.wait([tracer_ue, tracer_gnb])
|
||||
|
||||
# gNB Tracer Messages
|
||||
elif gnb_args.requested_tracer_messages:
|
||||
write_shm(shm_writing_gnb, gnb_args)
|
||||
|
||||
# UE Tracer Messages
|
||||
elif ue_args.requested_tracer_messages:
|
||||
write_shm(shm_writing_ue, ue_args)
|
||||
else:
|
||||
raise Exception("ERROR: No requested tracer messages IDs are provided")
|
||||
|
||||
# -------------------------------------------
|
||||
# Read data from gNB and UE T-tracer Application
|
||||
# -------------------------------------------
|
||||
# Check if data is available in memory
|
||||
# Initialize variables
|
||||
bufIdx = 0
|
||||
timeout = 10 # 10 seconds from now
|
||||
|
||||
# If gNB MSGs are requested
|
||||
if gnb_args.requested_tracer_messages:
|
||||
# Check if data is available in gNB memory
|
||||
is_gnb_data_in_memory = is_data_available_in_memory(
|
||||
shm_reading_gnb, bufIdx, general_message_header_length, timeout)
|
||||
# Report the status of gNB T-Tracer APP locally
|
||||
if not is_gnb_data_in_memory:
|
||||
print("Error: gNB: Check t-Tracer APP of gNB, check IPs and Ports")
|
||||
print("Error: gNB: If IPs and Ports are correct, re-run the hanging app.")
|
||||
print("It seems the socket was not closed properly")
|
||||
raise Exception("ERROR: Time out, check if gNB T-Tracer APP connected to stack")
|
||||
# If UE MSGs are requested
|
||||
if ue_args.requested_tracer_messages:
|
||||
# Check if data is available in UE memory
|
||||
is_ue_data_in_memory = is_data_available_in_memory(
|
||||
shm_reading_ue, bufIdx, general_message_header_length, timeout)
|
||||
# Report the status of UE T-Tracer APP locally
|
||||
if not is_ue_data_in_memory:
|
||||
print("Error: UE: Check t-Tracer APP of UE, check IPs and Ports")
|
||||
print("Error: UE: If IPs and Ports are correct, re-run the hanging app.")
|
||||
print("It seems the socket was not closed properly")
|
||||
raise Exception("ERROR: Time out, check if UE T-Tracer APP connected to stack")
|
||||
|
||||
# -------------------------------------------
|
||||
# Sync data between gNB and UE
|
||||
# -------------------------------------------
|
||||
# write JSON file
|
||||
common_utils.write_config_data_recording_app_json(config_meta_data)
|
||||
sync_info = {}
|
||||
if gnb_args.requested_tracer_messages and ue_args.requested_tracer_messages:
|
||||
# Sync data between gNB and UE
|
||||
sync_info = sync_service.sync_gnb_ue_captured_data(shm_reading_gnb, shm_reading_ue)
|
||||
print("\n***Sync data between gNB and UE: ", sync_info)
|
||||
# Read data from gNB and UE T-tracer Applications
|
||||
sync_data_conversion_service(
|
||||
shm_reading_gnb, shm_reading_ue, sync_info, config_meta_data, gnb_args, ue_args)
|
||||
elif gnb_args.requested_tracer_messages:
|
||||
# Read data from gNB T-tracer Application
|
||||
data_conversion_service(
|
||||
shm_reading_gnb, config_meta_data, gnb_args, sync_info, do_sync=False)
|
||||
elif ue_args.requested_tracer_messages:
|
||||
# Read data from UE T-tracer Application
|
||||
data_conversion_service(
|
||||
shm_reading_ue, config_meta_data, ue_args, sync_info, do_sync=False)
|
||||
else:
|
||||
raise Exception("ERROR: No requested tracer messages IDs are provided")
|
||||
|
||||
# measure Elapsed time
|
||||
time_elapsed = time.time() - start_time
|
||||
time_elapsed_ms = int(time_elapsed * 1000)
|
||||
print(
|
||||
"Elapsed time of getting Requested Messages and writing data and meta data files:",
|
||||
colored(time_elapsed_ms, "yellow"), "ms",)
|
||||
|
||||
# Stop T-Tracer Application function
|
||||
if gnb_args.requested_tracer_messages:
|
||||
gnb_args.action = "quit"
|
||||
write_shm(shm_writing_gnb, gnb_args)
|
||||
# Add Sleep time to ensure that the message sent to the UE T-tracer application is received
|
||||
# before the shared memory is detached
|
||||
time.sleep(0.5)
|
||||
# Clean shared memory
|
||||
detach_shm(shm_reading_gnb)
|
||||
detach_shm(shm_writing_gnb)
|
||||
remove_shm(shm_reading_gnb)
|
||||
remove_shm(shm_writing_gnb)
|
||||
if ue_args.requested_tracer_messages:
|
||||
ue_args.action = "quit"
|
||||
write_shm(shm_writing_ue, ue_args)
|
||||
# Add Sleep time to ensure that the message sent to the UE T-tracer application is received
|
||||
# before the shared memory is detached
|
||||
time.sleep(0.5)
|
||||
# Clean shared memory
|
||||
detach_shm(shm_reading_ue)
|
||||
detach_shm(shm_writing_ue)
|
||||
remove_shm(shm_reading_ue)
|
||||
remove_shm(shm_writing_ue)
|
||||
|
||||
print("End of the RF Data Recording API")
|
||||
pass
|
||||
32
common/utils/data_recording/lib/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/lib/__init__.py
|
||||
# brief for package initialization and code execution
|
||||
# author Abdo Gaber
|
||||
# date 2024
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
# This file is important in python to call functions from the lib folder.
|
||||
74
common/utils/data_recording/lib/common_utils.py
Normal file
@@ -0,0 +1,74 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/lib/common_utils.py
|
||||
# brief Data Recording common utilities
|
||||
# author Abdo Gaber
|
||||
# date 2025
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
def real_to_complex(real_vector):
|
||||
# Ensure the length of the real vector is even
|
||||
if len(real_vector) % 2 != 0:
|
||||
raise ValueError("The length of the real vector must be even.")
|
||||
|
||||
# Split the real vector into real and imaginary parts
|
||||
real_part = real_vector[::2]
|
||||
imag_part = real_vector[1::2]
|
||||
|
||||
# Combine the real and imaginary parts to form a complex vector
|
||||
complex_vector = real_part + 1j * imag_part
|
||||
|
||||
return complex_vector
|
||||
|
||||
|
||||
def write_config_data_recording_app_json(config_meta_data):
|
||||
if config_meta_data["data_recording_config"]["global_info"][
|
||||
"save_config_data_recording_app_json"
|
||||
]:
|
||||
try:
|
||||
json.dumps(config_meta_data)
|
||||
except (TypeError, ValueError) as e:
|
||||
print(f"data_recording_config_meta_json is not JSON serializable: {e}")
|
||||
|
||||
# Specify the file name
|
||||
output_file = (
|
||||
config_meta_data["data_recording_config"]["data_storage_path"]
|
||||
+ "config_data_recording_app.json"
|
||||
)
|
||||
# Ensure the directory exists
|
||||
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
||||
|
||||
# Write the JSON data to the file
|
||||
with open(output_file, "w") as file:
|
||||
try:
|
||||
json.dump(config_meta_data, file, indent=4)
|
||||
print(f"JSON file created successfully at {output_file}")
|
||||
except Exception as e:
|
||||
print(f"Failed to create JSON file: {e}")
|
||||
153
common/utils/data_recording/lib/config_interface.py
Normal file
@@ -0,0 +1,153 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/lib/config_interface.py
|
||||
# brief Data Recording App Configuration Interface
|
||||
# author Abdo Gaber
|
||||
# date 2025
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
import yaml
|
||||
import json
|
||||
import argparse
|
||||
|
||||
|
||||
# Function to read the main configuration file in JSON format
|
||||
def read_main_config_file_json(main_config_file: str) -> dict:
|
||||
"""
|
||||
Reads main config file.
|
||||
"""
|
||||
|
||||
# read general parameter set from yaml config file
|
||||
with open(main_config_file, "r") as file:
|
||||
main_config = json.load(file)
|
||||
|
||||
return main_config
|
||||
|
||||
|
||||
# Function to read the main configuration file in YAML format
|
||||
def read_main_config_file(main_config_file: str) -> dict:
|
||||
"""
|
||||
Reads main config file.
|
||||
"""
|
||||
|
||||
# read general parameter set from yaml config file
|
||||
with open(main_config_file, "r") as file:
|
||||
main_config = yaml.load(file, Loader=yaml.Loader)
|
||||
|
||||
return main_config
|
||||
|
||||
|
||||
# Function to parse the OAI T_messages file and get the index of a given string
|
||||
def parse_message_file(file_path):
|
||||
with open(file_path, "r") as file:
|
||||
content = file.readlines()
|
||||
|
||||
# Extract lines that start with 'ID' and remove the 'ID = ' prefix
|
||||
tracer_msgs_identities = [
|
||||
line.strip().replace("ID = ", "")
|
||||
for line in content
|
||||
if line.strip().startswith("ID")
|
||||
]
|
||||
|
||||
return tracer_msgs_identities
|
||||
|
||||
|
||||
# Function to get the index of a given string in the list of ID lines
|
||||
def get_index_of_id(tracer_msgs_identities, message_id):
|
||||
try:
|
||||
return tracer_msgs_identities.index(message_id)
|
||||
except ValueError:
|
||||
return -1 # Return -1 if the string is not found
|
||||
|
||||
|
||||
# Function to get the indices of requested tracer messages
|
||||
def get_requested_tracer_msgs_indices(requested_tracer_messages, tracer_msgs_identities):
|
||||
# get requested tracer messages indices
|
||||
req_tracer_msgs_indices = []
|
||||
for idx, value in enumerate(requested_tracer_messages):
|
||||
msg_index = get_index_of_id(tracer_msgs_identities, value)
|
||||
req_tracer_msgs_indices.append(msg_index)
|
||||
print("Requested Traces IDs: ", req_tracer_msgs_indices)
|
||||
return req_tracer_msgs_indices
|
||||
|
||||
|
||||
# Function to get the data recording configuration
|
||||
def get_data_recording_config(config_meta_data):
|
||||
parser = argparse.ArgumentParser(description="request messages IDs")
|
||||
ue_args = parser.parse_args()
|
||||
gnb_args = parser.parse_args()
|
||||
|
||||
# get Tracer Messages IDs from the T-Tracer Messages file
|
||||
# MSG list order should be similar to the T-Tracer Messages txt file
|
||||
# Be sure that you are using the same T_messages file that is used in the OAI project
|
||||
# OAI Path: .../common/utils/T/T_messages.txt
|
||||
|
||||
# Parse the T_messages file
|
||||
tracer_msgs_identities = parse_message_file(
|
||||
config_meta_data["data_recording_config"]["t_tracer_message_definition_file"])
|
||||
config_meta_data["data_recording_config"]["tracer_msgs_identities"] = tracer_msgs_identities
|
||||
|
||||
# get requested tracer messages indices for gNB
|
||||
gnb_args.requested_tracer_messages = \
|
||||
config_meta_data["data_recording_config"]["base_station"]["requested_tracer_messages"]
|
||||
# get requested tracer messages indices for UE
|
||||
ue_args.requested_tracer_messages = \
|
||||
config_meta_data["data_recording_config"]["user_equipment"]["requested_tracer_messages"]
|
||||
|
||||
# check if gnb_requested_tracer_messages is not empty
|
||||
if gnb_args.requested_tracer_messages:
|
||||
config_meta_data["data_recording_config"]["base_station"][
|
||||
"req_tracer_msgs_indices"] = get_requested_tracer_msgs_indices(
|
||||
gnb_args.requested_tracer_messages, tracer_msgs_identities)
|
||||
|
||||
# get gNB Trace Messages
|
||||
gnb_args.num_records = config_meta_data["data_recording_config"]["num_records"]
|
||||
gnb_args.start_frame_number = config_meta_data["data_recording_config"]["start_frame_number"]
|
||||
gnb_args.req_tracer_msgs_indices = \
|
||||
config_meta_data["data_recording_config"]["base_station"]["req_tracer_msgs_indices"]
|
||||
gnb_args.num_requested_tracer_msgs = len(gnb_args.req_tracer_msgs_indices)
|
||||
# Split the string into IP and port
|
||||
gnb_args.IPaddress, gnb_args.port = config_meta_data["data_recording_config"][
|
||||
"tracer_service_baseStation_address"].split(":")
|
||||
gnb_args.bytes_IPaddress = bytes(gnb_args.IPaddress, "utf-8")
|
||||
|
||||
# check if ue_requested_tracer_messages is not empty
|
||||
if ue_args.requested_tracer_messages:
|
||||
config_meta_data["data_recording_config"]["user_equipment"][
|
||||
"req_tracer_msgs_indices"] = get_requested_tracer_msgs_indices(
|
||||
ue_args.requested_tracer_messages, tracer_msgs_identities)
|
||||
|
||||
# get UE Trace Messages
|
||||
ue_args.num_records = config_meta_data["data_recording_config"]["num_records"]
|
||||
ue_args.start_frame_number = config_meta_data["data_recording_config"]["start_frame_number"]
|
||||
ue_args.req_tracer_msgs_indices = \
|
||||
config_meta_data["data_recording_config"]["user_equipment"]["req_tracer_msgs_indices"]
|
||||
ue_args.num_requested_tracer_msgs = len(ue_args.req_tracer_msgs_indices)
|
||||
ue_args.IPaddress, ue_args.port = config_meta_data["data_recording_config"][
|
||||
"tracer_service_userEquipment_address"].split(":")
|
||||
ue_args.bytes_IPaddress = bytes(ue_args.IPaddress, "utf-8")
|
||||
|
||||
return config_meta_data, gnb_args, ue_args
|
||||
107
common/utils/data_recording/lib/data_recording_messages_def.py
Normal file
@@ -0,0 +1,107 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/lib/data_recording_messages_def.py
|
||||
# brief defination of captured data recording messages
|
||||
# author Abdo Gaber
|
||||
# date 2025
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
|
||||
# Data Collection Trace Messages - General message structure - number of bytes
|
||||
def get_general_msg_header_list():
|
||||
"""
|
||||
shared memory layout written from the app:
|
||||
=================================
|
||||
msg_id (uint8) message type ID
|
||||
frame (uint16)
|
||||
slot (uint8)
|
||||
datetime_yyyymmdd (uint32)
|
||||
datetime_hhmmssmmm (uint32)
|
||||
frame_type (uint8)
|
||||
freq_range (uint8)
|
||||
subcarrier_spacing (uint8)
|
||||
cyclic_prefix (uint8)
|
||||
symbols_per_slot (uint8)
|
||||
Nid_cell (uint16)
|
||||
rnti (uint16)
|
||||
rb_size (uint16)
|
||||
rb_start (uint16)
|
||||
start_symbol_index (uint8)
|
||||
nr_of_symbols (uint8)
|
||||
qam_mod_order (uint8)
|
||||
mcs_index (uint8)
|
||||
mcs_table (uint8)
|
||||
nrOfLayers (uint8)
|
||||
transform_precoding (uint8)
|
||||
dmrs_config_type (uint8)
|
||||
ul_dmrs_symb_pos (uint16)
|
||||
number_dmrs_symbols (uint8)
|
||||
dmrs_port (uint16)
|
||||
dmrs_scid (uint16)
|
||||
nb_antennas (uint8)
|
||||
number_of_bits (uint32)
|
||||
length_bytes (uint32)
|
||||
For IQ Data: IQ samples: I0, Q0, I1, Q1, ... I_x, Q_x (int16)
|
||||
For bit data: bits: b0, b1, b2, ... b_x (uint8)
|
||||
|
||||
"""
|
||||
# Data Collection Trace Messages - General message structure - number of bytes
|
||||
general_msg_header_list = {
|
||||
"msg_id": 2,
|
||||
"frame": 2,
|
||||
"slot": 1,
|
||||
"datetime_yyyymmdd": 4,
|
||||
"datetime_hhmmssmmm": 4,
|
||||
"frame_type": 1,
|
||||
"freq_range": 1,
|
||||
"subcarrier_spacing": 1,
|
||||
"cyclic_prefix": 1,
|
||||
"symbols_per_slot": 1,
|
||||
"Nid_cell": 2,
|
||||
"rnti": 2,
|
||||
"rb_size": 2,
|
||||
"rb_start": 2,
|
||||
"start_symbol_index": 1,
|
||||
"nr_of_symbols": 1,
|
||||
"qam_mod_order": 1,
|
||||
"mcs_index": 1,
|
||||
"mcs_table": 1,
|
||||
"nrOfLayers": 1,
|
||||
"transform_precoding": 1,
|
||||
"dmrs_config_type": 1,
|
||||
"ul_dmrs_symb_pos": 2,
|
||||
"number_dmrs_symbols": 1,
|
||||
"dmrs_port": 2,
|
||||
"dmrs_scid": 2,
|
||||
"nb_antennas": 1, # for gNB or nb_antennas_tx for UE
|
||||
"number_of_bits": 4,
|
||||
"length_bytes": 4,
|
||||
}
|
||||
# initial number of bytes to read to get data
|
||||
general_message_header_length = 0
|
||||
for key, value in general_msg_header_list.items():
|
||||
general_message_header_length = general_message_header_length + value
|
||||
return general_msg_header_list, general_message_header_length
|
||||
581
common/utils/data_recording/lib/sigmf_interface.py
Normal file
@@ -0,0 +1,581 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/lib/sigmf_interface.py
|
||||
# brief SigMF Interface to Write data and meta-data to files in SigMF format
|
||||
# author Abdo Gaber
|
||||
# date 2025
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
import os
|
||||
import sigmf
|
||||
from sigmf import SigMFFile
|
||||
# from sigmf.utils import get_data_type_str
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
import yaml
|
||||
from sigmf import SigMFCollection
|
||||
|
||||
"""
|
||||
SERIALIZATION_SCHEMES = {
|
||||
# gnb
|
||||
"rx-fd-data": ["subcarriers", "ofdm_symbols"],
|
||||
"tx-pilots-fd-data": ["subcarriers", "ofdm_symbols"],
|
||||
"raw-ce-fd-data": ["subcarriers", "ofdm_symbols"],
|
||||
"rx-payload-bits": ["bits", "subcarriers", "ofdm_symbols"],
|
||||
# ue
|
||||
"tx-scrambled-bits": ["bits", "subcarriers", "ofdm_symbols"],
|
||||
"tx-payload-bits": ["bits", "subcarriers", "ofdm_symbols"],
|
||||
}
|
||||
"""
|
||||
STANDARDS = {"5gnr_oai": "5gnr"}
|
||||
|
||||
|
||||
def time_stamp_formating(time_stamp, datetime_offset):
|
||||
# Parse the input string into a datetime object
|
||||
time_stamp_ms_obj = datetime.strptime(time_stamp, "%Y%m%d_%H%M%S%f")
|
||||
|
||||
# Format the datetime object into the desired output format with milliseconds
|
||||
time_stamp_ms_iso = (
|
||||
time_stamp_ms_obj.strftime("%Y_%m_%dT%H:%M:%S.%f")[:-3] + datetime_offset
|
||||
)
|
||||
time_stamp_ms = time_stamp_ms_iso
|
||||
time_stamp_ms_file_name = time_stamp_ms_iso.replace(":", "_").replace(".", "_")
|
||||
|
||||
return time_stamp_ms, time_stamp_ms_file_name
|
||||
|
||||
|
||||
def create_serialization_metadata(serialization_scheme, data_source: str,
|
||||
link_sim_parameters: dict) -> dict:
|
||||
"""Creates dict that specifies the serialization metadata."""
|
||||
|
||||
# retrieve parameter from LinkSimulator config
|
||||
if data_source == "5gnr_oai":
|
||||
num_ofdm_symbol = link_sim_parameters["nr_of_symbols"]
|
||||
num_subcarriers = link_sim_parameters["rb_size"] * 12
|
||||
num_bits_per_symbol = link_sim_parameters["qam_mod_order"]
|
||||
else:
|
||||
raise Exception(
|
||||
f"Invalid data source string '{data_source}'! Only '5gnr_oai' is valid!"
|
||||
)
|
||||
|
||||
# create array with scheme dimensions
|
||||
if len(serialization_scheme) == 2:
|
||||
counts = [num_subcarriers, num_ofdm_symbol]
|
||||
elif len(serialization_scheme) == 3:
|
||||
counts = [num_bits_per_symbol, num_subcarriers, num_ofdm_symbol]
|
||||
else:
|
||||
raise Exception(
|
||||
f"Invalid number of elements in serialization scheme: {len(serialization_scheme)}")
|
||||
|
||||
# create metadata dict
|
||||
serialization_dict = {
|
||||
"serialization:scheme": serialization_scheme,
|
||||
"serialization:counts": counts,
|
||||
}
|
||||
|
||||
return serialization_dict
|
||||
|
||||
|
||||
def map_metadata_to_sigmf_format(scope, waveform_generator, parameter_map_file, captured_data):
|
||||
"""
|
||||
Maps metadata from Waveform creator to API and SigMF format.
|
||||
The used parameters and the mapping pairs are specified in a separate YAML file
|
||||
that must be provided as well.
|
||||
"""
|
||||
# read waveform parameter map from yaml file
|
||||
dir_path = os.path.dirname(__file__)
|
||||
src_path = os.path.split(dir_path)[0]
|
||||
|
||||
with open(os.path.join(src_path, parameter_map_file), "r") as file:
|
||||
parameter_map_dic = yaml.load(file, Loader=yaml.Loader)
|
||||
# preallocate target dict
|
||||
sigmf_metadata_dict = {}
|
||||
|
||||
# get standard and name of generator
|
||||
standard_key = parameter_map_dic["waveform_generator"][waveform_generator]
|
||||
generator = standard_key["generator"]
|
||||
|
||||
if scope == "tx":
|
||||
parameter_map_dic = parameter_map_dic["transmitter"][
|
||||
STANDARDS[waveform_generator]
|
||||
]
|
||||
elif scope == "channel":
|
||||
parameter_map_dic = parameter_map_dic["channel"]
|
||||
elif scope == "rx":
|
||||
parameter_map_dic = parameter_map_dic["receiver"]
|
||||
else:
|
||||
raise Exception(
|
||||
f"Invalid mapping scope '{scope}'! Only 'tx', 'channel' and 'rx' are valid!"
|
||||
)
|
||||
|
||||
# check if standard key is given
|
||||
if parameter_map_dic is None:
|
||||
raise Exception(
|
||||
"Invalid standard key: "
|
||||
"Name should be corrected or added to wireless_link_parameter_map.yaml, given: "
|
||||
f"{waveform_generator}"
|
||||
)
|
||||
|
||||
for parameter_pair in parameter_map_dic:
|
||||
# check if key for chosen simulator even exists
|
||||
if waveform_generator + "_parameter" in parameter_pair.keys():
|
||||
# only continue with mapping from file if direct equivalent exists
|
||||
if parameter_pair[waveform_generator + "_parameter"]["name"]:
|
||||
# It is not necessary to get all parameters from wireless_link_parameter_map.yaml
|
||||
# in captured_data since some parameters related to DL or UL only
|
||||
if (parameter_pair[waveform_generator + "_parameter"]["name"] in captured_data.keys()):
|
||||
# extract value from waveform config source
|
||||
value = captured_data[
|
||||
parameter_pair[waveform_generator + "_parameter"]["name"]]
|
||||
# additional mapping if parameter values should come from a discrete set of values
|
||||
if ("value_map" in parameter_pair[waveform_generator + "_parameter"].keys()):
|
||||
value = parameter_pair[waveform_generator + "_parameter"]["value_map"][value]
|
||||
# write to target dictionary for SigMF
|
||||
sigmf_metadata_dict[parameter_pair["sigmf_parameter_name"]] = value
|
||||
else:
|
||||
raise Exception(
|
||||
f"Incomplete specification in field '{waveform_generator}_parameter'!")
|
||||
# else: # fill_non_explicit_fields
|
||||
# waveform_config[parameter_pair["sigmf_parameter_name"]] = "none"
|
||||
if not sigmf_metadata_dict:
|
||||
raise Exception(
|
||||
"""ERROR: Check captured meta-data or provided config and meta data """)
|
||||
|
||||
# check for non-JSON-serializable data types
|
||||
def isfloat(NumberString):
|
||||
try:
|
||||
float(NumberString)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
for key, value in sigmf_metadata_dict.items():
|
||||
if isinstance(value, np.integer):
|
||||
sigmf_metadata_dict[key] = int(value)
|
||||
elif isinstance(value, (np.float16, np.float32, np.float64)):
|
||||
sigmf_metadata_dict[key] = np.format_float_positional(value, trim="-")
|
||||
elif isinstance(value, int):
|
||||
sigmf_metadata_dict[key] = int(value)
|
||||
elif isinstance(value, float):
|
||||
# store value in decimal and not in scientific notation
|
||||
sigmf_metadata_dict[key] = float(value)
|
||||
elif isinstance(value, str) and key != "standard":
|
||||
# convert string to lower case
|
||||
sigmf_metadata_dict[key] = value.lower()
|
||||
if isfloat(value):
|
||||
if value.isdigit():
|
||||
sigmf_metadata_dict[key] = int(float(value))
|
||||
elif value.replace(".", "", 1).isdigit() and value.count(".") < 2:
|
||||
sigmf_metadata_dict[key] = float(value)
|
||||
|
||||
return sigmf_metadata_dict, generator
|
||||
|
||||
|
||||
def create_system_components_metadata(waveform_generator, parameter_map_file, captured_data):
|
||||
"""Creates system components (TX, channel, RX) metadata that will reside in the annotations."""
|
||||
# map metadata of waveform generator to SigMF format
|
||||
signal_info, generator = map_metadata_to_sigmf_format(
|
||||
"tx", waveform_generator, parameter_map_file, captured_data
|
||||
)
|
||||
tx_metadata = {
|
||||
"signal:detail": {
|
||||
"standard": STANDARDS[waveform_generator],
|
||||
"generator": generator,
|
||||
STANDARDS[waveform_generator]: signal_info,
|
||||
}
|
||||
}
|
||||
|
||||
channel_metadata = {} # will be filled later
|
||||
rx_metadata = {} # will be filled later
|
||||
|
||||
return tx_metadata, channel_metadata, rx_metadata
|
||||
|
||||
|
||||
def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, idx):
|
||||
"""
|
||||
Compiles and saves provided data and metadata into SigMF file format.
|
||||
"""
|
||||
# get meta data from config file
|
||||
base_station_meta_data = config_meta_data["data_recording_config"]["base_station"][
|
||||
"meta_data"]
|
||||
user_equipment_meta_data = config_meta_data["data_recording_config"][
|
||||
"user_equipment"]["meta_data"]
|
||||
|
||||
# Check the receive target path is valid, else create folder
|
||||
data_storage_path = config_meta_data["data_recording_config"]["data_storage_path"]
|
||||
if not os.path.isdir(data_storage_path):
|
||||
print("Create new folder for recorded data: " + str(data_storage_path))
|
||||
os.makedirs(data_storage_path)
|
||||
|
||||
# Write recorded data to file
|
||||
# Get time stamp
|
||||
time_stamp_ms, time_stamp_ms_file_name = time_stamp_formating(
|
||||
captured_data["time_stamp"], global_info["datetime_offset"])
|
||||
# Map OAI Message Name to SigMF Message Name
|
||||
file_name_prefix = config_meta_data["data_recording_config"][
|
||||
"supported_oai_tracer_messages"][captured_data["message_type"]]["file_name_prefix"]
|
||||
recorded_data_file_name = (
|
||||
file_name_prefix + "-rec-" + str(idx) + "-" + time_stamp_ms_file_name)
|
||||
|
||||
dataset_filename = recorded_data_file_name + ".sigmf-data"
|
||||
dataset_file_path = os.path.join(data_storage_path, dataset_filename)
|
||||
print(dataset_file_path)
|
||||
captured_data["recorded_data"].tofile(dataset_file_path)
|
||||
|
||||
# map OAI config data to SigMF metadata
|
||||
waveform_generator = config_meta_data["data_recording_config"]["global_info"][
|
||||
"waveform_generator"]
|
||||
parameter_map_file = config_meta_data["data_recording_config"]["parameter_map_file"]
|
||||
tx_metadata, channel_metadata, rx_metadata = create_system_components_metadata(
|
||||
waveform_generator, parameter_map_file, captured_data)
|
||||
|
||||
# Add other OAI metadata to SigMF metadata
|
||||
# ----------------------------------------------------
|
||||
# Set 1: Parameters needs to be derived from OAI message
|
||||
# ----------------------------------------------------
|
||||
# PUSCH DMRS: OFDM symbol start index within slot
|
||||
get_pusch_dmrs_start_ofdm_symbol = 0
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch_dmrs:ofdm_symbol_idx"] = []
|
||||
|
||||
for symbol in range(captured_data["start_symbol_index"],
|
||||
captured_data["start_symbol_index"] + captured_data["nr_of_symbols"]):
|
||||
|
||||
dmrs_symbol_flag = (captured_data["ul_dmrs_symb_pos"] >> symbol) & 0x01
|
||||
if dmrs_symbol_flag and not get_pusch_dmrs_start_ofdm_symbol:
|
||||
get_pusch_dmrs_start_ofdm_symbol = 1
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch_dmrs:start_ofdm_symbol"] = symbol
|
||||
if dmrs_symbol_flag:
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch_dmrs:ofdm_symbol_idx"].append(symbol) # Append symbol to the list
|
||||
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch_dmrs:duration_num_ofdm_symbols"] = 1
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch_dmrs:num_add_positions"] = (captured_data["number_dmrs_symbols"] - 1)
|
||||
|
||||
# If the message is a BIT message, add number of bits to the signal info
|
||||
# "UE_PHY_UL_SCRAMBLED_TX_BITS", "GNB_PHY_UL_PAYLOAD_RX_BITS", "UE_PHY_UL_PAYLOAD_TX_BITS"
|
||||
if "_BITS" in captured_data["message_type"]:
|
||||
# if "BIT" in captured_data["message_type"]:
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]]["num_bits"] = (
|
||||
captured_data["number_of_bits"])
|
||||
|
||||
# If the message is captured from UE, it is UPLink message,
|
||||
# so number of antennas is num of Tx antennas
|
||||
# number of antennas on Rx side should be read from global_info given by user
|
||||
if "UE" in captured_data["message_type"]:
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"num_tx_antennas"
|
||||
] = captured_data["nb_antennas"]
|
||||
rx_metadata.update(
|
||||
{"num_rx_antennas": base_station_meta_data["num_rx_antennas"]}
|
||||
)
|
||||
# If the message is captured from gNB, it is UPLink message,
|
||||
# so number of antennas is num of Rx antennas
|
||||
# number of antennas on Tx side should be read from global_info given by user
|
||||
else:
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"num_tx_antennas"] = user_equipment_meta_data["num_tx_antennas"]
|
||||
rx_metadata.update({"num_rx_antennas": captured_data["nb_antennas"]})
|
||||
|
||||
# Check if the message type contains "UL"
|
||||
if "UL" in captured_data["message_type"]:
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"link_direction"] = "uplink"
|
||||
else:
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"link_direction"] = "downlink"
|
||||
# ----------------------------------------------------
|
||||
# Set 2: Parameters that is hardcoded
|
||||
# ----------------------------------------------------
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch:content"] = "compliant"
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch:mapping_type"] = "B"
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]]["num_slots"] = 1
|
||||
if config_meta_data["test_config"]["test_mode"] == "rf_simulation":
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch:payload_bit_pattern"] = "random"
|
||||
elif config_meta_data["test_config"]["test_mode"] == "rf_real_time":
|
||||
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
|
||||
"pusch:payload_bit_pattern"] = "zeros"
|
||||
else:
|
||||
raise Exception("ERROR: Test mode is not supported in SigMF formatting")
|
||||
# ----------------------------------------------------
|
||||
# Read mean parameters
|
||||
freq = config_meta_data["environment_emulation"]["target_link_config"][
|
||||
"wireless_channel"]["carrierFreqValueList_hz"][0]
|
||||
bandwidth = config_meta_data["data_recording_config"]["common_meta_data"][
|
||||
"bandwidth"]
|
||||
# used_signal_bandwidth = (
|
||||
# pow(10, 6) * config_meta_data["environment_emulation"]["target_link_config"]["ran_config"][
|
||||
# "uplink"]["ul_used_signal_BW_MHz"])
|
||||
sample_rate = config_meta_data["data_recording_config"]["common_meta_data"][
|
||||
"sample_rate"]
|
||||
|
||||
# get signal emitter info
|
||||
signal_emitter = {
|
||||
"manufacturer": "NI",
|
||||
"seid": user_equipment_meta_data["seid"], # Unique ID of the emitter
|
||||
"hw": user_equipment_meta_data["hw_type"],
|
||||
"hw_subtype": user_equipment_meta_data["hw_subtype"],
|
||||
"frequency": freq,
|
||||
"sample_rate": sample_rate,
|
||||
"bandwidth": bandwidth,
|
||||
"gain_tx": user_equipment_meta_data["tx_gain"],
|
||||
"clock_reference": config_meta_data["data_recording_config"][
|
||||
"common_meta_data"
|
||||
]["clock_reference"],
|
||||
}
|
||||
|
||||
# ----------------------------------------------------
|
||||
# get channel metadata
|
||||
# ----------------------------------------------------
|
||||
# get downlink channel info
|
||||
# ch_downlink_config = config_meta_data["environment_emulation"]["target_link_config"]["wireless_channel"]["downlink"]
|
||||
# ch_downlink ={"channel_model": ch_downlink_config["type"]}
|
||||
|
||||
# ch_downlink_model = {"channel_model": "AWGN"}
|
||||
# get uplink channel info
|
||||
ch_uplink_config = config_meta_data["environment_emulation"]["target_link_config"][
|
||||
"wireless_channel"
|
||||
]["uplink"]
|
||||
# Real Time RF Emulation
|
||||
if config_meta_data["test_config"]["test_mode"] == "rf_real_time":
|
||||
channel_metadata.update({"emulation_mode": "ni rf real time"})
|
||||
if ch_uplink_config["type"] == "statistical_predefined":
|
||||
channel_metadata.update(
|
||||
{
|
||||
"channel_model": ch_uplink_config["statistical"]["predef_channel_profile"],
|
||||
"snr_esn0_db": ch_uplink_config["statistical"]["snr_db"],
|
||||
}
|
||||
)
|
||||
|
||||
elif ch_uplink_config["type"] == "statistical_var":
|
||||
channel_metadata.update(
|
||||
{
|
||||
"channel_model": ch_uplink_config["statistical"]["var_power_delay_profile"],
|
||||
"snr_esn0_db": ch_uplink_config["statistical"]["snr_db"],
|
||||
"delay_spread": ch_uplink_config["statistical"]["delay_spread_ns"] * pow(10, -9),
|
||||
"speed": ch_uplink_config["statistical"]["speed_mps"],
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise Exception("ERROR: channel type is not supported in SigMF formatting")
|
||||
|
||||
# RF Simulation
|
||||
elif config_meta_data["test_config"]["test_mode"] == "rf_simulation":
|
||||
channel_metadata.update({"emulation_mode": "oai rf simulation"})
|
||||
channel_metadata.update(
|
||||
{
|
||||
"channel_model": ch_uplink_config["statistical"]["predef_channel_profile"],
|
||||
"snr_esn0_db": ch_uplink_config["statistical"]["snr_db"],
|
||||
"delay_spread": ch_uplink_config["statistical"]["delay_spread_ns"]
|
||||
* pow(10, -9), "speed": ch_uplink_config["statistical"]["speed_mps"],
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
"ERROR: Channel Emulation mode is not supported in SigMF formatting")
|
||||
|
||||
channel_metadata["carrier_frequency"] = config_meta_data["environment_emulation"][
|
||||
"target_link_config"]["wireless_channel"]["carrierFreqValueList_hz"][0]
|
||||
|
||||
# ----------------------------------------------------
|
||||
# get rx metadata
|
||||
# ----------------------------------------------------
|
||||
rx_metadata.update(
|
||||
{
|
||||
"bandwidth": bandwidth,
|
||||
"gain": base_station_meta_data["rx_gain"],
|
||||
"manufacturer": "NI",
|
||||
"seid": base_station_meta_data["seid"],
|
||||
"hw_subtype": base_station_meta_data["hw_subtype"],
|
||||
"clock_reference": config_meta_data["data_recording_config"][
|
||||
"common_meta_data"
|
||||
]["clock_reference"],
|
||||
"phy_freq_domain_receiver_type": config_meta_data["test_config"][
|
||||
"dut_type"
|
||||
],
|
||||
}
|
||||
)
|
||||
# ----------------------------------------------------
|
||||
# get test config
|
||||
# ----------------------------------------------------
|
||||
"""
|
||||
test_config = {
|
||||
"test_name": config_meta_data["test_config"]["test_name"],
|
||||
"test_mode": config_meta_data["test_config"]["test_mode"],
|
||||
"dut_type": config_meta_data["test_config"]["dut_type"],
|
||||
}
|
||||
"""
|
||||
# Create sigmf metadata
|
||||
# ----------------------
|
||||
# Add global parameters to SigMF metadata
|
||||
# ----------------------
|
||||
meta = SigMFFile(
|
||||
data_file=dataset_file_path, # extension is optional
|
||||
global_info={
|
||||
SigMFFile.DATATYPE_KEY: captured_data[
|
||||
"sigmf_data_type"
|
||||
], # "cf32_le", # get_data_type_str(rx_data) - 'cf64_le' is not supported yet
|
||||
SigMFFile.SAMPLE_RATE_KEY: sample_rate,
|
||||
SigMFFile.NUM_CHANNELS_KEY: 1,
|
||||
SigMFFile.AUTHOR_KEY: global_info["author"],
|
||||
SigMFFile.DESCRIPTION_KEY: config_meta_data["data_recording_config"][
|
||||
"supported_oai_tracer_messages"
|
||||
][captured_data["message_type"]]["description"],
|
||||
SigMFFile.RECORDER_KEY: "NI Data Recording Application for OAI",
|
||||
SigMFFile.LICENSE_KEY: "MIT License",
|
||||
# Since we are focusing on 5G NR UL, the base station is the receiver
|
||||
SigMFFile.HW_KEY: base_station_meta_data["hw_type"],
|
||||
# Disable DATASET key to mitigate the warning when read SIGMF data although it is given in the spec.
|
||||
# It seems SIGMF still has bug here
|
||||
SigMFFile.DATASET_KEY: dataset_filename,
|
||||
SigMFFile.VERSION_KEY: sigmf.__version__,
|
||||
sigmf.SigMFFile.COLLECTION_KEY: global_info["collection_file"],
|
||||
},
|
||||
)
|
||||
|
||||
# ----------------------
|
||||
# Add capture parameters to SigMF metadata
|
||||
# ----------------------
|
||||
serialization_scheme = config_meta_data["data_recording_config"][
|
||||
"supported_oai_tracer_messages"
|
||||
][captured_data["message_type"]]["serialization_scheme"]
|
||||
capture_metadata = {
|
||||
sigmf.SigMFFile.DATETIME_KEY: time_stamp_ms,
|
||||
SigMFFile.FREQUENCY_KEY: freq,
|
||||
**create_serialization_metadata(
|
||||
serialization_scheme, waveform_generator, captured_data
|
||||
),
|
||||
}
|
||||
meta.add_capture(start_index=0, metadata=capture_metadata) # Sample Start
|
||||
|
||||
# create annotations dict
|
||||
system_components_dict = {
|
||||
"system_components:transmitter": [
|
||||
{
|
||||
"transmitter_id": "tx_0",
|
||||
**tx_metadata,
|
||||
"signal:emitter": signal_emitter,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
system_components_dict["system_components:channel"] = [
|
||||
{"transmitter_id": "tx_0", **channel_metadata}]
|
||||
|
||||
system_components_dict["system_components:receiver"] = rx_metadata
|
||||
# ----------------------
|
||||
# Add annotation parameters to SigMF metadata
|
||||
# ----------------------
|
||||
meta.add_annotation(
|
||||
start_index=0, # Sample Start
|
||||
length=len(captured_data["recorded_data"]), # Sample count
|
||||
metadata={
|
||||
# SigMFFile.FLO_KEY: freq - sample_rate/ 2, # args.freq - args.rate / 2,
|
||||
# SigMFFile.FHI_KEY: freq + sample_rate/ 2, # args.freq + args.rate / 2,
|
||||
# SigMFFile.LABEL_KEY: label,
|
||||
# SigMFFile.COMMENT_KEY: general_config["comment"],
|
||||
"num_transmitters": 1,
|
||||
**system_components_dict,
|
||||
},
|
||||
)
|
||||
|
||||
# Write Meta Data to file
|
||||
dataset_meta_filename = recorded_data_file_name + ".sigmf-meta"
|
||||
dataset_meta_file_path = os.path.join(data_storage_path, dataset_meta_filename)
|
||||
meta.tofile(dataset_meta_file_path) # extension is optional
|
||||
|
||||
print(dataset_meta_file_path)
|
||||
|
||||
return dataset_meta_file_path
|
||||
|
||||
|
||||
def save_sigmf_collection(streams: list, global_info: dict, description: str, storage_path: str):
|
||||
"""Save metadata and links to SigMF data/metadata files to SigMF collection file."""
|
||||
# construct path
|
||||
storage_path = os.path.expanduser(storage_path)
|
||||
collection_filepath = os.path.join(storage_path, global_info["collection_file"])
|
||||
# statically configure Collection metadata
|
||||
collection = SigMFCollection(
|
||||
streams,
|
||||
metadata={
|
||||
SigMFCollection.COLLECTION_KEY: {
|
||||
SigMFCollection.VERSION_KEY: sigmf.__version__,
|
||||
SigMFCollection.DESCRIPTION_KEY: description,
|
||||
SigMFCollection.AUTHOR_KEY: global_info["author"],
|
||||
# SigMFCollection.LICENSE_KEY: "License",
|
||||
# SigMFCollection.EXTENSIONS_KEY: global_info["extensions"],
|
||||
SigMFCollection.STREAMS_KEY: [],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# save collection file
|
||||
collection.tofile(collection_filepath)
|
||||
print("")
|
||||
print(collection_filepath + ".sigmf-collection")
|
||||
|
||||
|
||||
def load_sigmf(filename: str, storage_path: str, scope: str):
|
||||
"""
|
||||
Loads metadata and data from SigMF file.
|
||||
"""
|
||||
# construct path
|
||||
storage_path = os.path.expanduser(storage_path)
|
||||
filepath = os.path.join(storage_path, filename)
|
||||
# load SigMF file
|
||||
signal = sigmf.sigmffile.fromfile(filepath)
|
||||
|
||||
# get metadata fields
|
||||
global_metadata = signal.get_global_info()
|
||||
annotations_metadata = signal.get_annotations()
|
||||
|
||||
# we only consider single annotations metadata element per SigMF file
|
||||
annotation_start_idx = annotations_metadata[0][sigmf.SigMFFile.START_INDEX_KEY]
|
||||
annotation_length = annotations_metadata[0][sigmf.SigMFFile.LENGTH_INDEX_KEY]
|
||||
|
||||
# get capture metadata
|
||||
capture_metadata = signal.get_capture_info(annotation_start_idx)
|
||||
|
||||
# from source code: sigmffile.py
|
||||
# "autoscale : bool, default True
|
||||
# If dataset is in a fixed-point representation, scale samples from (min, max) to (-1.0, 1.0)
|
||||
# raw_components : bool, default False
|
||||
# If True read and return the sample components (individual I & Q for complex, samples for real)
|
||||
# with no conversions or interleaved channels.""
|
||||
# TODO: problem: raw_components flag is not used at all! changes in source code required!
|
||||
# TODO: data is always converted to float32 after being read
|
||||
|
||||
# read actual data
|
||||
data = signal.read_samples(
|
||||
annotation_start_idx, annotation_length, autoscale=False, raw_components=True)
|
||||
|
||||
return data, global_metadata, annotations_metadata
|
||||
150
common/utils/data_recording/lib/sync_service.py
Normal file
@@ -0,0 +1,150 @@
|
||||
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
# ---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/lib/sync_service.py
|
||||
# brief Sync service between captured Data from 5GNR gNB and UE
|
||||
# author Abdo Gaber
|
||||
# date 2025
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
import struct
|
||||
from lib import data_recording_messages_def
|
||||
|
||||
DEBUG_POW_MEAS_SYNC = True
|
||||
|
||||
|
||||
# check if first frame ahead:
|
||||
def is_frame_ahead(frame1, frame2, max_frame=1023):
|
||||
"""
|
||||
Check if frame1 is ahead of frame2, considering wrap-around from max_frame to 0.
|
||||
|
||||
Args:
|
||||
frame1 (int): The first frame number.
|
||||
frame2 (int): The second frame number.
|
||||
max_frame (int): The maximum frame number before wrap-around. Default is 1023.
|
||||
|
||||
Returns:
|
||||
bool: True if frame1 is ahead of frame2, False otherwise.
|
||||
"""
|
||||
# Calculate the difference considering wrap-around
|
||||
diff = (frame1 - frame2 + (max_frame + 1)) % (max_frame + 1)
|
||||
# If the difference is less than half the range, frame1 is ahead
|
||||
return diff < (max_frame + 1) // 2
|
||||
|
||||
|
||||
# find the frame and slot start
|
||||
def find_frame_slot_start(dataset1_start, dataset2_start):
|
||||
"""
|
||||
Function to find the frame and slot start for data sync
|
||||
Args:
|
||||
dataset1_start_info: Dictionary containing the start information of dataset1
|
||||
dataset2_start_info: Dictionary containing the start information of dataset2
|
||||
Returns:
|
||||
sync_info: Dictionary containing the sync information
|
||||
sync_info["frame"] = frame_start
|
||||
sync_info["slot"] = slot_start
|
||||
|
||||
"""
|
||||
sync_info = {}
|
||||
|
||||
if dataset1_start["frame"] == dataset2_start["frame"]:
|
||||
frame_diff = 0
|
||||
frame_start = dataset1_start["frame"]
|
||||
slot_start = max(dataset1_start["slot"], dataset2_start["slot"])
|
||||
|
||||
elif is_frame_ahead(dataset1_start["frame"], dataset2_start["frame"]):
|
||||
frame_start = dataset1_start["frame"]
|
||||
slot_start = dataset1_start["slot"]
|
||||
frame_diff = (dataset1_start["frame"]- dataset2_start["frame"] + 1024) % 1024
|
||||
elif is_frame_ahead(dataset2_start["frame"], dataset1_start["frame"]):
|
||||
frame_start = dataset2_start["frame"]
|
||||
slot_start = dataset2_start["slot"]
|
||||
frame_diff = (dataset2_start["frame"] - dataset1_start["frame"] + 1024) % 1024
|
||||
# check first if the delta time between the two datasets is larger than expected offset time.
|
||||
# So, the sync will not be applied
|
||||
# check during the calculation of the frame the ramp-around from 1023 to 0
|
||||
if abs(frame_diff) > 6:
|
||||
raise Exception(
|
||||
f"Frame difference between dataset1 and dataset2 is too large: {frame_diff}. "
|
||||
"This may indicate a problem with the data collection or synchronization."
|
||||
)
|
||||
|
||||
# Determine the starting frame and slot for data sync
|
||||
sync_info["frame"] = frame_start
|
||||
sync_info["slot"] = slot_start
|
||||
return sync_info
|
||||
|
||||
|
||||
# Read data from gNB T-tracer Application
|
||||
def get_frame_slot_start(shm_reading, bufIdx, general_msg_header_list,
|
||||
general_message_header_length):
|
||||
buf = shm_reading.read(bufIdx + general_message_header_length)
|
||||
msg_id = struct.unpack("<H", buf[bufIdx: bufIdx + general_msg_header_list.get("msg_id")])[0]
|
||||
bufIdx += general_msg_header_list.get("msg_id")
|
||||
frame = struct.unpack("<H", buf[bufIdx: bufIdx + general_msg_header_list.get("frame")])[0]
|
||||
bufIdx += general_msg_header_list.get("frame")
|
||||
slot = struct.unpack("B", buf[bufIdx: bufIdx + general_msg_header_list.get("slot")])[0]
|
||||
return frame, slot
|
||||
|
||||
|
||||
# Sync data between gNB and UE
|
||||
def sync_gnb_ue_captured_data(shm_reading_gnb, shm_reading_ue):
|
||||
"""
|
||||
Function to get the sync (frame, slot) data between gNB and UE
|
||||
Args:
|
||||
shm_reading_gnb: Shared memory for gNB
|
||||
shm_reading_ue: Shared memory for UE
|
||||
Returns:
|
||||
sync_info: Dictionary containing the sync information between gNB and UE
|
||||
sync_info["frame"] = frame_start
|
||||
sync_info["slot"] = slot_start
|
||||
"""
|
||||
# get general message header list
|
||||
general_msg_header_list, general_message_header_length = (
|
||||
data_recording_messages_def.get_general_msg_header_list())
|
||||
|
||||
# Read data from gNB T-tracer Application
|
||||
bufIdx = 0
|
||||
frame_gnb, slot_gnb = get_frame_slot_start(
|
||||
shm_reading_gnb, bufIdx, general_msg_header_list, general_message_header_length)
|
||||
# Read data from UE T-tracer Application
|
||||
bufIdx = 0
|
||||
frame_ue, slot_ue = get_frame_slot_start(
|
||||
shm_reading_ue, bufIdx, general_msg_header_list, general_message_header_length)
|
||||
dataset1_start = {}
|
||||
dataset1_start["frame"] = frame_gnb
|
||||
dataset1_start["slot"] = slot_gnb
|
||||
dataset2_start = {}
|
||||
dataset2_start["frame"] = frame_ue
|
||||
dataset2_start["slot"] = slot_ue
|
||||
# Sync data between gNB and UE
|
||||
# We noticed that the maximum difference between the frame number of gNB and UE is 3 frames
|
||||
# Calculate the frame difference considering the wrap-around from 1023 to 0
|
||||
sync_info = find_frame_slot_start(dataset1_start, dataset2_start)
|
||||
print(" gNB Start info: ", dataset1_start)
|
||||
print(" UE Start info: ", dataset2_start)
|
||||
print(" gNB and UE Sync info: ", sync_info)
|
||||
return sync_info
|
||||
150
common/utils/data_recording/sync_validation_demo.py
Normal file
@@ -0,0 +1,150 @@
|
||||
#/*
|
||||
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
# * contributor license agreements. See the NOTICE file distributed with
|
||||
# * this work for additional information regarding copyright ownership.
|
||||
# * The OpenAirInterface Software Alliance licenses this file to You under
|
||||
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
# * except in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# *
|
||||
# * http://www.openairinterface.org/?page_id=698
|
||||
# *
|
||||
# * Unless required by applicable law or agreed to in writing, software
|
||||
# * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# * See the License for the specific language governing permissions and
|
||||
# * limitations under the License.
|
||||
# *-------------------------------------------------------------------------------
|
||||
# * For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
# * contact@openairinterface.org
|
||||
# */
|
||||
#---------------------------------------------------------------------
|
||||
# file common/utils/data_recording/sync_validation_demo.py
|
||||
# brief simple application to validate sync between payload captured on gNB and UE
|
||||
# author Abdo Gaber
|
||||
# date 2025
|
||||
# version 1.0
|
||||
# company Emerson, NI Test and Measurement
|
||||
# email:
|
||||
# note
|
||||
# warning
|
||||
|
||||
import numpy as np
|
||||
import os
|
||||
import glob
|
||||
import matplotlib.pyplot as plt
|
||||
from lib import sigmf_interface
|
||||
from lib import config_interface
|
||||
import sigmf
|
||||
|
||||
|
||||
def calculate_ber(capture_subdir: str, scope: str):
|
||||
"""
|
||||
Iteratively counts all bit errors from data and finally calculates the BER for a given Eb/N0.
|
||||
"""
|
||||
|
||||
print("Start to calculate BER for subdirectory", capture_subdir)
|
||||
|
||||
# get all collection files in the specified directory
|
||||
file_list = glob.glob(os.path.join(capture_subdir, "*.sigmf-collection"))
|
||||
print("Found Collection files:", len(file_list))
|
||||
|
||||
bit_errors_total = 0
|
||||
num_bits_total = 0
|
||||
|
||||
for idx, file in enumerate(file_list):
|
||||
print("Processing file:", file)
|
||||
|
||||
# read SigMF collection file
|
||||
collection_file = sigmf.sigmffile.fromfile(file)
|
||||
print(" ")
|
||||
print("Collection file:", file)
|
||||
# extract filenames
|
||||
stream_files = collection_file.get_stream_names()
|
||||
print("Stream files:", stream_files)
|
||||
# extract filename from list of streams
|
||||
tx_bits_filename = next(
|
||||
filename for filename in stream_files if "tx-payload-bits" in filename
|
||||
)
|
||||
|
||||
rx_bits_ref_rx_real_ce_filename = next(
|
||||
filename for filename in stream_files if "rx-payload-bits" in filename
|
||||
)
|
||||
# extract data and deserialized data (not used here)
|
||||
tx_bits, _, _ = sigmf_interface.load_sigmf(
|
||||
tx_bits_filename, capture_subdir, scope="tx-payload-bits"
|
||||
)
|
||||
rx_bits, _, rx_annotations = (
|
||||
sigmf_interface.load_sigmf(
|
||||
rx_bits_ref_rx_real_ce_filename, capture_subdir, scope="rx-payload-bits"
|
||||
)
|
||||
)
|
||||
|
||||
# calculate bit errors
|
||||
bit_diffs = np.logical_xor(tx_bits, rx_bits)
|
||||
bit_errors = np.count_nonzero(bit_diffs)
|
||||
# increment total bit/symbol errors
|
||||
print("Current bit errors:", bit_errors / len(tx_bits))
|
||||
bit_errors_total += bit_errors
|
||||
num_bits_total += len(tx_bits)
|
||||
# plot "tx_bits" and "rx_bits_ref_rx_real_ce" for debugging purposes
|
||||
# Calculate the differences
|
||||
differences = np.array(tx_bits) - np.array(rx_bits)
|
||||
|
||||
# Plot TX and RX bits using a scatter plot
|
||||
fig = plt.figure(figsize=(15, 6))
|
||||
indices = np.arange(len(tx_bits))
|
||||
|
||||
plt.scatter(indices, tx_bits, label="TX bits", color="blue", marker="o", s=80)
|
||||
plt.scatter(indices, rx_bits, label="RX bits", color="red", marker="x", s=80)
|
||||
|
||||
# Highlight differences
|
||||
for i in range(len(differences)):
|
||||
if differences[i] != 0:
|
||||
plt.scatter(i, 1.1, color="red", marker="|", s=100)
|
||||
|
||||
plt.xlabel("Bit Index")
|
||||
plt.ylabel("Bit Value")
|
||||
plt.title(
|
||||
"TX vs RX Bits, Record "
|
||||
+ str(idx)
|
||||
+ ", Number of bits mismatchs: "
|
||||
+ str(bit_errors),
|
||||
fontsize=16,
|
||||
)
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
plt.ylim(-0.1, 1.2)
|
||||
plt.xlim(200, 250)
|
||||
plt.show(block=False)
|
||||
# Sleep for a specified amount of time (e.g., 5 seconds)
|
||||
plt.pause(6)
|
||||
plt.close(plt.gcf()) # Close the figure
|
||||
# Keep the script running to display the plots
|
||||
# input("Press Enter to continue...")
|
||||
|
||||
# calculate BER
|
||||
ber = bit_errors_total / num_bits_total
|
||||
print("Total bit errors = ", ber)
|
||||
|
||||
|
||||
def calculate_stats(main_config_file: str):
|
||||
|
||||
scope = "real-world"
|
||||
|
||||
print(f"Starting to generate statistics with data scope: {scope}")
|
||||
|
||||
# read main config
|
||||
main_config = config_interface.read_main_config_file_json(main_config_file)
|
||||
|
||||
# get paths for recorded captures
|
||||
capture_path = main_config["data_recording_config"]["data_storage_path"]
|
||||
calculate_ber(capture_path, scope)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
main_config = "/home/user/workarea/oai_recorded_data/config_data_recording_app.json"
|
||||
|
||||
# calculate BER stats
|
||||
calculate_stats(main_config)
|
||||
203
doc/data_recording.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Synchronized Real-Time Data Recording Application
|
||||
|
||||
The Data Recording Application runs in parallel to the system components on one of the LINUX servers. It makes use of OAI T-tracer framework. It is able to communicate with the Base station (gNB) and User terminal (UE).
|
||||
In addition, it synchronizes and combines data traces and control information (metadata) from Base station and User terminal into consistent [Signal Metadata Format (SigMF) format](https://github.com/gnuradio/SigMF) data sets. SigMF is an open-source standard that specifies a way to describe sets of recorded digital signal samples with metadata (data properties and scenario descriptions) provided in [JSON](http://www.json.org/) files. These recorded data sets have a wide range of uses in research and applications.
|
||||
|
||||
## Data Recording Application Architecture
|
||||
|
||||
Data Recording Application designed to have multiple services in different simulation environments:
|
||||
- Data Control Service (Python)
|
||||
- Data collection (T-Tracer) Service (C)
|
||||
- Data Conversion Service (Python)
|
||||
|
||||
Regarding to the APIs between different services, they are such as the following:
|
||||
- Data Control Service -> Data Collection (T–Tracer) Service: The API is based on a shared memory.
|
||||
- Data Collection (T–Tracer) Service -> 5G NR Stack: T-Tracer Framework, ethernet connection.
|
||||
- Data Collection (T–Tracer) Service -> Data Conversion Service: The API is based on a shared memory.
|
||||
|
||||
The following figure shows OAI gNB and UE with the Data Recording App system architecture.
|
||||
|
||||
<img src="images/data_recording_arch.png" alt="OAI gNB and UE with the Data Recording App system architectur" width="1000">
|
||||
|
||||
## Required Packages
|
||||
The following packages are required:
|
||||
- `sudo apt-get install -y python3-sysv-ipc`
|
||||
- `sudo apt-get install libxft-dev`
|
||||
- `sudo python3 -m pip install sigmf==1.2.1`
|
||||
- `sudo python3 -m pip install termcolor`
|
||||
- `sudo python3 -m pip install bitarray`
|
||||
- `sudo python3 -m pip install pandas`
|
||||
- `sudo python3 -m pip install numpy==1.23`
|
||||
|
||||
## Configuration Files
|
||||
### Main Data Recording JSON Configuration File
|
||||
The Data Recording application provides configuration file in [JSON](http://www.json.org/) format. It is stored in [common/utils/data_recording/config/config_data_recording.json](../common/utils/data_recording/config/config_data_recording.json) folder. The main parameters are:
|
||||
- **data_storage_path**: Path to directory for data storage
|
||||
- **num_records**: Number of requested data records in slots
|
||||
- **t_tracer_message_definition_file**: T-Tracer message definition file
|
||||
- **parameter_map_file**: Parameter mapping dictionary (OAI parameters to standardized metadata). It is located here: [common/utils/data_recording/config/wireless_link_parameter_map.yaml](../common/utils/data_recording/config/wireless_link_parameter_map.yaml)
|
||||
- **start_frame_number**: It can be used to start the recording from a specific frame, but it is not yet supported.
|
||||
- **base_station**:
|
||||
- requested_tracer_messages: Requested base station data traces. The supported messages are:
|
||||
- GNB_PHY_UL_FD_PUSCH_IQ
|
||||
- GNB_PHY_UL_FD_DMRS
|
||||
- GNB_PHY_UL_FD_CHAN_EST_DMRS_POS
|
||||
- GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL
|
||||
- GNB_PHY_UL_PAYLOAD_RX_BITS
|
||||
- meta_data: Additional base station metadata (additional fixed base station parameters that cannot be read from the system yet, but shall be included into the SigMF metadata)
|
||||
- **user_equipment**
|
||||
- requested_tracer_messages: Requested user equipment data traces. The supported messages are:
|
||||
- UE_PHY_UL_SCRAMBLED_TX_BITS
|
||||
- UE_PHY_UL_PAYLOAD_TX_BITS
|
||||
- meta_data: Additional user equipment metadata (additional fixed UE parameters that are not read from the system yet, but shall be included into the SigMF metadata)
|
||||
- **common_meta_data**: Common metadata parameters that are not read from the system yet, but shall be included into the SigMF metadata such as the sampling rate, the bandwidth, and the clock reference of USRP.
|
||||
- **tracer_service_baseStation_address**: The IP address and port number of base station server. If the Data Recording App and the Data Collection (T–Tracer) Service are running on the same server of base station, the IP address and port number are : `127.0.0.1:2021`
|
||||
- **tracer_service_userEquipment_address**: The IP address and port number of UE server.
|
||||
|
||||
**Notes**:
|
||||
- The data can be recorded from base station and UE or from base station only or UE only. To disable the recording from any station, make the list of `requested_tracer_messages` empty such as: `requested_tracer_messages:[]`
|
||||
|
||||
The figure below illustrates an example of a JSON Data Recording App configuration file.
|
||||
|
||||
<img src="images/data_recording_app_config.png" alt="Example from JSON Data Recording Configuration File" width="500">
|
||||
|
||||
### Wireless Link Parameter Map Dictionary
|
||||
Since every signal recorder has related configuration with different naming scheme, the [common/utils/data_recording/config/wireless_link_parameter_map.yaml](../common/utils/data_recording/config/wireless_link_parameter_map.yaml) is a dictionary to do the parameters pair between the signal configuration and the SigMF metadata (e.g. OAI parameter name vs. SigMF metadata parameter name). It eases of adoption in case of adding new parameters. In case of changing the name of given parameters in OAI and we need to get those parameters in metadata, the required changes need to be done in the parameter map dictionary.
|
||||
|
||||
The following figure shows an example of Wireless Link Parameter Map Dictionary. For example, the frequency range is called in standardized SigMF metadata `frequency_range` while it is called in OAI `freq_range` and it is called in NI 5G NR RFmx `frequency range`.
|
||||
|
||||
<img src="images/wireless_link_parameter_map_example.png" alt="Example of Wireless Link Parameter Map Dictionary" width="400">
|
||||
|
||||
### Global Metadata
|
||||
There are some metadata parameters that the user may need to change only once. Those parameters have been hard coded in the Data Recording App header.
|
||||
[common/utils/data_recording/data_recording_app_v1.0.py](../common/utils/data_recording/data_recording_app_v1.0.py). Those are:
|
||||
- The global metadata such as: author, description, sigmf collection file prefix, datetime_offset, enable saving config Data Recording App in json file with recorded data, and name of signal generator (i.e. 5gnr_oai). The name of signal generator is used for parameter mapping dictionary (OAI parameters to standardized metadata).
|
||||
- The mapping between supported OAI messages and file_name_prefix, scope, and description.
|
||||
|
||||
The following figure shows an example of global metadata.
|
||||
|
||||
<img src="images/global metadata_example.png" alt="Example from global meta-data" width="750">
|
||||
|
||||
The following figure shows an example of mapping between supported OAI messages by Data recorrding App and file_name_prefix.
|
||||
|
||||
<img src="images/mapping_oai_messages_and_file_name_prefix.png" alt="mapping between supported OAI messages and file_name_prefix" width="600">
|
||||
|
||||
## How to run Data Recording Application
|
||||
The data recording application can be hosted on the gNB Server or in another server (for example the data lake server). The T-Tracer framework is a Network socket-based (TCP) communication between OAI 5G RAN stack and external tracer program.
|
||||
|
||||
### Step1: Run NR gNB Softmodem with enabling T-Tracer Option
|
||||
|
||||
It is worth mentioning that there is no need to set the T-Tracer port number via NR gNB Softmodem command, the default port `2021` is going to be used. If the Data Recording App is running on the same server of gNB, the parameter `tracer_service_baseStation_address` should be `"tracer_service_baseStation_address": "127.0.0.1:2021`. The gNB T-tracer App will read captured data then from port `2021`.
|
||||
For the test simplicity, user can run the system in PHY-test mode. We will show how to run using PHY-Test mode:
|
||||
|
||||
Run NR gNB Softmodem with USRP:
|
||||
```
|
||||
sudo ./cmake_targets/ran_build/build/nr-softmodem -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.106PRB.1x1.usrpx410_3300MHz.conf --gNBs.[0].min_rxtxtime 6 --usrp-tx-thread-config 1 --phy-test -d --T_stdout 2 --T_nowait
|
||||
```
|
||||
Run NR gNB Softmodem in RF Simulation:
|
||||
```
|
||||
sudo ./cmake_targets/ran_build/build/nr-softmodem -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.106PRB.1x1.usrpx410_3300MHz.conf --gNBs.[0].min_rxtxtime 6 --rfsim --rfsimulator.serveraddr server --phy-test -d --T_stdout 2 --T_nowait
|
||||
```
|
||||
Note: User needs to change the name of gNB config file to your gNB config file.
|
||||
|
||||
It is worth mentioning that the possible values for T-tracer control options
|
||||
- --T_stdout option: default 1
|
||||
- --T_stdout = 0: Disable output on the terminal and only use the T tracer
|
||||
- --T_stdout = 1: Disable the T tracer and only output on the terminal
|
||||
- --T_stdout = 2: Both Eeable the T tracer and output on the terminal
|
||||
- --T_nowait: Starting gNB without waiting for T-Tracer to be connected
|
||||
- --T_port [port]: Default port 2021
|
||||
|
||||
### Step2: Run NR UE Softmodem with enabling T-Tracer Option
|
||||
|
||||
User needs to set the T-Tracer port number via NR UE Softmodem command, assume `2023`. The UE T-tracer App will read captured data then from port `2023`. The IP address and port number are configured in JSON file via the parameter: `"tracer_service_userEquipment_address": "192.168.100.3:2023”`, where the IP address `192.168.100.3` is the IP address of the UE server.
|
||||
In OAI PHY-test mode, after running the NR gNB softmodem, copy from gNB Server the following two RRC files `reconfig.raw` and `rbconfig.raw` to UE Server (assume to `/home/user/workarea/rrc_files/`):
|
||||
|
||||
Run OAI Soft UE with USRP:
|
||||
```
|
||||
sudo ./cmake_targets/ran_build/build/nr-uesoftmodem --usrp-args "type=x4xx,addr=192.168.10.2,second_addr=192.168.11.2,clock_source=external,time_source=external" --phy-test -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf --reconfig-file /home/user/workarea/rrc_files/reconfig.raw --rbconfig-file /home/user/workarea/rrc_files/rbconfig.raw --ue-rxgain 40 --ue-txgain 12 --T_stdout 2 --T_nowait --T_port 2023
|
||||
```
|
||||
|
||||
|
||||
Run OAI Soft UE in RF Simulation, assume the IP address of gNB server is `192.168.100.2`:
|
||||
```
|
||||
sudo ./cmake_targets/ran_build/build/nr-uesoftmodem --rfsim --rfsimulator.serveraddr 192.168.100.2 --phy-test -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf --reconfig-file /home/user/workarea/rrc_files/reconfig.raw --rbconfig-file /home/user/workarea/rrc_files/rbconfig.raw --T_stdout 2 --T_nowait --T_port 2023
|
||||
```
|
||||
|
||||
### Step3: Run Data Recording Application Services
|
||||
First, compile the Data Collection (T-Tracer) Services:
|
||||
- Go to: `common/utils/T/tracer`
|
||||
- Compile gNB and UE Data Collection Services (T-Tracer Apps)
|
||||
```
|
||||
make t_tracer_app_gnb
|
||||
make t_tracer_app_ue
|
||||
```
|
||||
|
||||
Run gNB Data Collection service on one terminal:
|
||||
```
|
||||
./t_tracer_app_gnb -d ../T_messages.txt
|
||||
```
|
||||
Run UE Data Collection service on another terminal:
|
||||
```
|
||||
./t_tracer_app_ue -d ../T_messages.txt
|
||||
```
|
||||
|
||||
Second, run main Data Recording Application:
|
||||
- Go to: `common/utils/data_recording`
|
||||
|
||||
Run the following python script:
|
||||
```
|
||||
python3 data_recording_app_v1.0.py
|
||||
```
|
||||
|
||||
The recorded data set will be stored in the configured path, assume `/home/user/workarea/oai_recorded_data/`.
|
||||
The following figure shows an example of global metadata.
|
||||
|
||||
<img src="images/sigmf_dataset.png" alt="Example from SigMF recorded data set" width="600">
|
||||
|
||||
## Overview on Collected Data Set
|
||||
|
||||
SigMF supports heterogeneous data via SigMF collections. It is used to bundle multiple SigMF files into one recording.
|
||||
A heterogeneous data set generated for a certain scenario is stored in a SigMF collection which consists of (as it is shown in the figure above)
|
||||
- One SigMF collection file per each record, e.g.
|
||||
- data-collection-rec-idx-timestamp.sigmf-collection
|
||||
- SigMF recordings (.sigmf-meta file + .sigmf-data file). Per each message, there are two files:
|
||||
- rx-fd-data-rec-idx-timestamp.sigmf-meta
|
||||
- rx-fd-data-rec-idx-timestamp.sigmf-data
|
||||
- tx-pilots-fd-data-rec-idx-timestamp.sigmf-meta
|
||||
- tx-pilots-fd-data-rec-idx-timestamp.sigmf-data
|
||||
- raw-ce-fd-data-rec-idx-timestamp.sigmf-meta
|
||||
- raw-ce-fd-data-rec-idx-timestamp.sigmf-data
|
||||
- tx-scrambled-bits-rec-idx-timestamp.sigmf-meta
|
||||
- tx-scrambled-bits-rec-idx-timestamp.sigmf-data
|
||||
|
||||
The following figure shows an example of SigMF collection file and how it bundles multiple SigMF files of a single record.
|
||||
|
||||
<img src="images/sigmf_data_collection.png" alt="Example from data collection" width="700">
|
||||
|
||||
Each SigMF metadata has three sections:
|
||||
- global: The standardized global parameters are presented here to understand and read the binary data.
|
||||
- captures: The standardized capture parameters provide details about the capture process.
|
||||
- annotations: It provides details about the recording scenario.
|
||||
|
||||
The following figure shows an example of SigMF metadata file, for example for the frequency-domain RX data.
|
||||
|
||||
<img src="images/sigmf_metadata.png" alt="Example from SigMF-metadata" width="500">
|
||||
|
||||
## Synchronization Validation
|
||||
For synchronization validation and to show how to read SigMF metadata, a simple script `common/utils/data_recording/sync_validation_demo.py` has been created to validate that, for example: the recorded bits from gNB and UE are in sync.
|
||||
|
||||
## Data Recording Application Limitation
|
||||
|
||||
- MIMO Support: The data recording app is ready, tested and validated for SISO. For MIMO, it should be enhanced, tested, and validated.
|
||||
- It supports the uplink gNB and UE messages listed above. If the user would like to record another message for example from DL, the user needs to define the required meta-data. User can use the existing meta-data definition of UL as a template. Then, add those messages to the headers of different Data Recording App services.
|
||||
- Data serialization in Tx scrambled bits message without considering location of DMRS symbols: Only captured valid data bits is stored. It means the location of DMRS symbols are not considered and filled with zeros and stored as it is in the grid presented on the figure below. For example:
|
||||
- Number of bits per IQ Symbol = 4
|
||||
- Number of subcarrier = 72
|
||||
- Number of OFDM symbols = 13
|
||||
- So, the valid number of bits in the transport block (slot) is: 3312 bits. If we will fill DMRS locations by zeros, the number of bits is: 3744 bits, but it is not done due to the real-timing issues. For Tx scrambled bits data de-serialization, the user can reconstruct the Tx Scrambled Bits Grid (2D Grid) by using the captured DMRS grid or Channel Estimates Grid as a reference and no need to derive the DMRS symbols locations based on 5G NR config parameters.
|
||||
|
||||
<img src="images/data_serialization_tx_scrambled_bit_message.png" alt="Data serialization " width="500">
|
||||
|
||||
### To Do List:
|
||||
- Provide an overview about the different services of the Data Recording App (Data Control Service, Data collection (T-Tracer) Service, Data Conversion Service) and the APIs definition between them.
|
||||
BIN
doc/images/data_recording_app_config.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
doc/images/data_recording_arch.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
doc/images/data_serialization_tx_scrambled_bit_message.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
doc/images/global metadata_example.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
doc/images/mapping_oai_messages_and_file_name_prefix.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
doc/images/sigmf_data_collection.png
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
doc/images/sigmf_dataset.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
doc/images/sigmf_metadata.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
doc/images/wireless_link_parameter_map_example.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
@@ -79,6 +79,8 @@ static void nr_pusch_antenna_processing(void *arg)
|
||||
uint64_t noise_amp2 = *(rdata->noise_amp2);
|
||||
int nest_count = *(rdata->nest_count);
|
||||
delay_t *delay = rdata->delay;
|
||||
c16_t *pusch_ch_est_dmrs_pos_slot_mem = rdata->pusch_ch_est_dmrs_pos_slot_mem;
|
||||
int dmrs_symbol_start_idx = rdata->dmrs_symbol_start_idx;
|
||||
|
||||
const int chest_freq = rdata->chest_freq;
|
||||
NR_gNB_PUSCH *pusch_vars = rdata->pusch_vars;
|
||||
@@ -124,6 +126,7 @@ static void nr_pusch_antenna_processing(void *arg)
|
||||
int re_offset = k0;
|
||||
LOG_D(PHY, "PUSCH estimation DMRS type 1, Freq-domain interpolation");
|
||||
int pilot_cnt = 0;
|
||||
int ch_est_cnt = 0; // To trace channel coefficients
|
||||
|
||||
for (int n = 0; n < 3 * nb_rb_pusch; n++) {
|
||||
// LS estimation
|
||||
@@ -140,6 +143,15 @@ static void nr_pusch_antenna_processing(void *arg)
|
||||
for (int k = pilot_cnt << 1; k < (pilot_cnt << 1) + 4; k++) {
|
||||
ul_ls_est[k] = ch16;
|
||||
}
|
||||
//------------------Write channel parameters to Memory for data recording ------------------//
|
||||
#if T_TRACER
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_POS)) {
|
||||
// Trace channel coefficients
|
||||
pusch_ch_est_dmrs_pos_slot_mem[dmrs_symbol_start_idx + delta + ch_est_cnt] = ch16; // 0, 2, 4, 6, 8, location of REs
|
||||
pusch_ch_est_dmrs_pos_slot_mem[dmrs_symbol_start_idx + delta + ch_est_cnt + 2] = ch16; // 0, 2, 4, 6, 8, location of REs
|
||||
}
|
||||
#endif
|
||||
ch_est_cnt += 4;
|
||||
pilot_cnt += 2;
|
||||
}
|
||||
|
||||
@@ -423,7 +435,9 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
|
||||
unsigned short bwp_start_subcarrier,
|
||||
nfapi_nr_pusch_pdu_t *pusch_pdu,
|
||||
int *max_ch,
|
||||
uint32_t *nvar)
|
||||
uint32_t *nvar,
|
||||
c16_t *pusch_dmrs_slot_mem,
|
||||
c16_t *pusch_ch_est_dmrs_pos_slot_mem)
|
||||
{
|
||||
c16_t pilot[3280] __attribute__((aligned(32)));
|
||||
|
||||
@@ -481,6 +495,20 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
|
||||
|
||||
#endif
|
||||
|
||||
//------------------Write DMRS to Memory for Data Recording ------------------//
|
||||
int dmrs_symbol_start_idx = symbol * pusch_pdu->nrOfLayers * nb_rb_pusch * NR_NB_SC_PER_RB + nl * nb_rb_pusch * NR_NB_SC_PER_RB;
|
||||
#if T_TRACER
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_DMRS)) {
|
||||
// used by T-Tracer to trace DMRS slot grid
|
||||
int dmrs_delta = 0; // intialize it to zero currently, derive it later from above functions
|
||||
for (int i = 0; i < (6 * nb_rb_pusch); i++) {
|
||||
// the generated DMRs is a complex conjugate of mod table, so flip the sign of imag. part
|
||||
pusch_dmrs_slot_mem [dmrs_symbol_start_idx + dmrs_delta + i * 2].r = pilot[i].r; // 0, 2, 4, 6, 8, location of REs
|
||||
pusch_dmrs_slot_mem [dmrs_symbol_start_idx + dmrs_delta + i * 2].i = -pilot[i].i; // 0, 2, 4, 6, 8, location of REs
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int nest_count = 0;
|
||||
uint64_t noise_amp2 = 0;
|
||||
delay_t *delay = &gNB->ulsch[ul_id].delay;
|
||||
@@ -532,6 +560,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
|
||||
rdata->chest_freq = gNB->chest_freq;
|
||||
rdata->rxdataF = gNB->common_vars.rxdataF;
|
||||
rdata->ans = &ans;
|
||||
rdata->pusch_ch_est_dmrs_pos_slot_mem = pusch_ch_est_dmrs_pos_slot_mem;
|
||||
rdata->dmrs_symbol_start_idx = dmrs_symbol_start_idx;
|
||||
// Call the nr_pusch_antenna_processing function
|
||||
if (job_id == num_jobs - 1) {
|
||||
// Run the last job inline
|
||||
|
||||
@@ -51,7 +51,9 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
|
||||
unsigned short bwp_start_subcarrier,
|
||||
nfapi_nr_pusch_pdu_t *pusch_pdu,
|
||||
int *max_ch,
|
||||
uint32_t *nvar);
|
||||
uint32_t *nvar,
|
||||
c16_t *pusch_dmrs_slot_mem,
|
||||
c16_t *pusch_ch_est_dmrs_pos_slot_mem);
|
||||
|
||||
void dump_nr_I0_stats(FILE *fd,PHY_VARS_gNB *gNB);
|
||||
|
||||
|
||||
@@ -10,9 +10,18 @@
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
#include <openair1/PHY/TOOLS/phy_scope_interface.h>
|
||||
#include "PHY/sse_intrin.h"
|
||||
#include "T.h"
|
||||
#include <sys/time.h>
|
||||
#include "PHY/log_tools.h"
|
||||
|
||||
#define INVALID_VALUE 255
|
||||
|
||||
static void copy_c16_data_to_slot_memory(
|
||||
c16_t *src, c16_t *dst_slot, int nb_re_pusch, int symbol)
|
||||
{
|
||||
memcpy(&dst_slot[nb_re_pusch * symbol], src, nb_re_pusch * sizeof(c16_t));
|
||||
}
|
||||
|
||||
void nr_idft(int32_t *z, uint32_t Msc_PUSCH)
|
||||
{
|
||||
|
||||
@@ -1021,7 +1030,9 @@ static void inner_rx(PHY_VARS_gNB *gNB,
|
||||
int length,
|
||||
int symbol,
|
||||
int output_shift,
|
||||
uint32_t nvar)
|
||||
uint32_t nvar,
|
||||
c16_t *rxFext_slot,
|
||||
c16_t *chFext_slot)
|
||||
{
|
||||
int nb_layer = rel15_ul->nrOfLayers;
|
||||
int nb_rx_ant = frame_parms->nb_antennas_rx;
|
||||
@@ -1032,6 +1043,7 @@ static void inner_rx(PHY_VARS_gNB *gNB,
|
||||
|
||||
memset(rxFext, 0, sizeof(c16_t) * nb_rx_ant * buffer_length);
|
||||
memset(chFext, 0, sizeof(c16_t) * nb_layer * nb_rx_ant* buffer_length);
|
||||
int nb_re_pusch = NR_NB_SC_PER_RB * rel15_ul->rb_size;
|
||||
int dmrs_symbol;
|
||||
if (gNB->chest_time == 0)
|
||||
dmrs_symbol = dmrs_symbol_flag ? symbol : get_valid_dmrs_idx_for_channel_est(rel15_ul->ul_dmrs_symb_pos, symbol);
|
||||
@@ -1052,6 +1064,15 @@ static void inner_rx(PHY_VARS_gNB *gNB,
|
||||
dmrs_symbol_flag,
|
||||
rel15_ul,
|
||||
frame_parms);
|
||||
#if T_TRACER
|
||||
// Assume assume Tx and Rx = 1
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_PUSCH_IQ)) {
|
||||
copy_c16_data_to_slot_memory(rxFext[aarx], rxFext_slot, nb_re_pusch, symbol);
|
||||
}
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL)) {
|
||||
copy_c16_data_to_slot_memory(chFext[aatx][aarx], chFext_slot, nb_re_pusch, symbol);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
c16_t rho[nb_layer][nb_layer][buffer_length] __attribute__((aligned(32)));
|
||||
@@ -1159,6 +1180,8 @@ typedef struct puschSymbolProc_s {
|
||||
uint32_t nvar;
|
||||
int beam_nb;
|
||||
task_ans_t *ans;
|
||||
c16_t *pusch_ch_est_dmrs_interpl_slot_mem;
|
||||
c16_t *rxFext_slot_mem;
|
||||
} puschSymbolProc_t;
|
||||
|
||||
static void nr_pusch_symbol_processing(void *arg)
|
||||
@@ -1188,7 +1211,9 @@ static void nr_pusch_symbol_processing(void *arg)
|
||||
gNB->pusch_vars[ulsch_id].ul_valid_re_per_slot[symbol],
|
||||
symbol,
|
||||
gNB->pusch_vars[ulsch_id].log2_maxh,
|
||||
rdata->nvar);
|
||||
rdata->nvar,
|
||||
rdata->rxFext_slot_mem,
|
||||
rdata->pusch_ch_est_dmrs_interpl_slot_mem);
|
||||
|
||||
int nb_re_pusch = gNB->pusch_vars[ulsch_id].ul_valid_re_per_slot[symbol];
|
||||
// layer de-mapping
|
||||
@@ -1250,6 +1275,37 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
|
||||
LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset);
|
||||
LOG_D(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos);
|
||||
|
||||
// Memories to store data for data recording
|
||||
int buffer_length_slot = rel15_ul->rb_size * NR_NB_SC_PER_RB * 14 ; // 14 OFDM Symbols per slot
|
||||
int nb_rx_ant = frame_parms->nb_antennas_rx;
|
||||
int nb_layer = rel15_ul->nrOfLayers;
|
||||
|
||||
// Initialize memory for DMRS signals
|
||||
c16_t pusch_dmrs_slot_mem[nb_layer * buffer_length_slot] __attribute__((aligned(32)));
|
||||
// Initialize memory for channel estimates based on DMRS positions
|
||||
c16_t pusch_ch_est_dmrs_pos_slot_mem[buffer_length_slot * nb_layer * nb_rx_ant] __attribute__((aligned(32)));
|
||||
// memory to store slot grid with channel coefficients based on DMRS positions after interpolation
|
||||
c16_t pusch_ch_est_dmrs_interpl_slot_mem[buffer_length_slot * nb_layer * nb_rx_ant] __attribute__((aligned(32)));
|
||||
// memory to store extracted data including PUSCH + DMRS
|
||||
c16_t rxFext_slot_mem[nb_rx_ant*buffer_length_slot] __attribute__((aligned(32)));
|
||||
|
||||
#if T_TRACER
|
||||
// Initialize memory for DMRS signals
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_DMRS))
|
||||
memset(pusch_dmrs_slot_mem, 0, sizeof(c16_t) * nb_layer * buffer_length_slot);
|
||||
|
||||
// Initialize memory for channel estimates based on DMRS positions
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_POS))
|
||||
memset(pusch_ch_est_dmrs_pos_slot_mem, 0, sizeof(c16_t) * buffer_length_slot * nb_layer * nb_rx_ant );
|
||||
|
||||
// memory to store slot grid with channel coefficients based on DMRS positions after interpolation
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL))
|
||||
memset(pusch_ch_est_dmrs_interpl_slot_mem, 0, sizeof(c16_t) * buffer_length_slot * nb_layer * nb_rx_ant );
|
||||
|
||||
// memory to store extracted data including PUSCH + DMRS
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_PUSCH_IQ))
|
||||
memset(rxFext_slot_mem, 0, sizeof(c16_t) * buffer_length_slot * nb_rx_ant);
|
||||
#endif
|
||||
//----------------------------------------------------------
|
||||
//------------------- Channel estimation -------------------
|
||||
//----------------------------------------------------------
|
||||
@@ -1275,7 +1331,9 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
|
||||
bwp_start_subcarrier,
|
||||
rel15_ul,
|
||||
&max_ch,
|
||||
&nvar_tmp);
|
||||
&nvar_tmp,
|
||||
pusch_dmrs_slot_mem,
|
||||
pusch_ch_est_dmrs_pos_slot_mem);
|
||||
nvar += nvar_tmp;
|
||||
}
|
||||
// measure the SNR from the channel estimation
|
||||
@@ -1503,6 +1561,8 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
|
||||
rdata->scramblingSequence = scramblingSequence;
|
||||
rdata->nvar = nvar;
|
||||
rdata->beam_nb = beam_nb;
|
||||
rdata->rxFext_slot_mem= rxFext_slot_mem;
|
||||
rdata->pusch_ch_est_dmrs_interpl_slot_mem = pusch_ch_est_dmrs_interpl_slot_mem;
|
||||
|
||||
if (rel15_ul->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) {
|
||||
nr_pusch_symbol_processing(rdata);
|
||||
@@ -1517,6 +1577,189 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
|
||||
}
|
||||
} // symbol loop
|
||||
|
||||
#if T_TRACER
|
||||
// Get Time Stamp for T-tracer messages
|
||||
char trace_time_stamp_str[30];
|
||||
get_time_stamp_usec(trace_time_stamp_str);
|
||||
// trace_time_stamp_str = 8 bytes timestamp = YYYYMMDD
|
||||
// + 9 bytes timestamp = HHMMSSMMM
|
||||
// Not Ready for MIMO
|
||||
int dmrs_port = get_dmrs_port(0,rel15_ul->dmrs_ports);
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_DMRS)) {
|
||||
// Log GNB_PHY_UL_FD_DMRS using T-Tracer if activated
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
T(T_GNB_PHY_UL_FD_DMRS,
|
||||
T_INT((int)frame),
|
||||
T_INT((int)slot),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 0, 8)),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 8, 9)),
|
||||
T_INT((int)frame_parms->frame_type), // Frame type (0 FDD, 1 TDD) frame_structure
|
||||
T_INT((int)frame_parms->freq_range), // Frequency range (0 FR1, 1 FR2)
|
||||
T_INT((int)rel15_ul->subcarrier_spacing), // Subcarrier spacing (0 15kHz, 1 30kHz, 2 60kHz)
|
||||
T_INT((int)rel15_ul->cyclic_prefix), // Normal or extended prefix (0 normal, 1 extended)
|
||||
T_INT((int)frame_parms->symbols_per_slot), // Number of symbols per slot
|
||||
T_INT((int)frame_parms->Nid_cell),
|
||||
T_INT((int)rel15_ul->rnti),
|
||||
T_INT((int)rel15_ul->rb_size),
|
||||
T_INT((int)rel15_ul->rb_start),
|
||||
T_INT((int)rel15_ul->start_symbol_index), //start_ofdm_symbol
|
||||
T_INT((int)rel15_ul->nr_of_symbols), //num_ofdm_symbols
|
||||
T_INT((int)rel15_ul->qam_mod_order), //modulation
|
||||
T_INT((int)rel15_ul->mcs_index), //mcs
|
||||
T_INT((int)rel15_ul->mcs_table), //mcs_table_index
|
||||
T_INT((int)rel15_ul->nrOfLayers), // num_layer
|
||||
T_INT((int)rel15_ul->transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
||||
T_INT((int)rel15_ul->dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
||||
T_INT((int)rel15_ul->ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
||||
T_INT((int)number_dmrs_symbols),
|
||||
//dmrs_start_ofdm_symbol
|
||||
//dmrs_duration_num_ofdm_symbols
|
||||
//dmrs_num_add_positions
|
||||
T_INT((int)dmrs_port), //dmrs_antenna_port
|
||||
T_INT((int)rel15_ul->scid), //dmrs_nscid
|
||||
T_INT((int)frame_parms->nb_antennas_rx), // rx antenna
|
||||
T_INT(0), //number_of_bits
|
||||
T_BUFFER((c16_t*)(&(pusch_dmrs_slot_mem[0])), rel15_ul->rb_size * NR_NB_SC_PER_RB * rel15_ul->nr_of_symbols * 4));
|
||||
}
|
||||
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_POS)) {
|
||||
// Log GNB_PHY_UL_FD_CHAN_EST_DMRS_POS using T-Tracer if activated
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
T(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_POS,
|
||||
T_INT((int)frame),
|
||||
T_INT((int)slot),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 0, 8)),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 8, 9)),
|
||||
T_INT((int)frame_parms->frame_type), // Frame type (0 FDD, 1 TDD) frame_structure
|
||||
T_INT((int)frame_parms->freq_range), // Frequency range (0 FR1, 1 FR2)
|
||||
T_INT((int)rel15_ul->subcarrier_spacing), // Subcarrier spacing (0 15kHz, 1 30kHz, 2 60kHz)
|
||||
T_INT((int)rel15_ul->cyclic_prefix), // Normal or extended prefix (0 normal, 1 extended)
|
||||
T_INT((int)frame_parms->symbols_per_slot), // Number of symbols per slot
|
||||
T_INT((int)frame_parms->Nid_cell),
|
||||
T_INT((int)rel15_ul->rnti),
|
||||
T_INT((int)rel15_ul->rb_size),
|
||||
T_INT((int)rel15_ul->rb_start),
|
||||
T_INT((int)rel15_ul->start_symbol_index), //start_ofdm_symbol
|
||||
T_INT((int)rel15_ul->nr_of_symbols), //num_ofdm_symbols
|
||||
T_INT((int)rel15_ul->qam_mod_order), //modulation
|
||||
T_INT((int)rel15_ul->mcs_index), //mcs
|
||||
T_INT((int)rel15_ul->mcs_table), //mcs_table_index
|
||||
T_INT((int)rel15_ul->nrOfLayers), // num_layer
|
||||
T_INT((int)rel15_ul->transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
||||
T_INT((int)rel15_ul->dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
||||
T_INT((int)rel15_ul->ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
||||
T_INT((int)number_dmrs_symbols),
|
||||
//dmrs_start_ofdm_symbol
|
||||
//dmrs_duration_num_ofdm_symbols
|
||||
//dmrs_num_add_positions
|
||||
T_INT((int)dmrs_port), //dmrs_antenna_port
|
||||
T_INT((int)rel15_ul->scid), //dmrs_nscid
|
||||
T_INT((int)frame_parms->nb_antennas_rx), // rx antenna
|
||||
T_INT(0), //number_of_bits
|
||||
T_BUFFER((c16_t*)(&(pusch_ch_est_dmrs_pos_slot_mem[0])), rel15_ul->rb_size * NR_NB_SC_PER_RB * rel15_ul->nr_of_symbols * 4));
|
||||
}
|
||||
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_PUSCH_IQ)) {
|
||||
// Log GNB_PHY_UL_FD_PUSCH_IQ using T-Tracer if activated
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
|
||||
T(T_GNB_PHY_UL_FD_PUSCH_IQ,
|
||||
T_INT((int)frame),
|
||||
T_INT((int)slot),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 0, 8)),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 8, 9)),
|
||||
T_INT((int)frame_parms->frame_type), // Frame type (0 FDD, 1 TDD) frame_structure
|
||||
T_INT((int)frame_parms->freq_range), // Frequency range (0 FR1, 1 FR2)
|
||||
T_INT((int)rel15_ul->subcarrier_spacing), // Subcarrier spacing (0 15kHz, 1 30kHz, 2 60kHz)
|
||||
T_INT((int)rel15_ul->cyclic_prefix), // Normal or extended prefix (0 normal, 1 extended)
|
||||
T_INT((int)frame_parms->symbols_per_slot), // Number of symbols per slot
|
||||
T_INT((int)frame_parms->Nid_cell),
|
||||
T_INT((int)rel15_ul->rnti),
|
||||
T_INT((int)rel15_ul->rb_size),
|
||||
T_INT((int)rel15_ul->rb_start),
|
||||
T_INT((int)rel15_ul->start_symbol_index), //start_ofdm_symbol
|
||||
T_INT((int)rel15_ul->nr_of_symbols), //num_ofdm_symbols
|
||||
T_INT((int)rel15_ul->qam_mod_order), //modulation
|
||||
T_INT((int)rel15_ul->mcs_index), //mcs
|
||||
T_INT((int)rel15_ul->mcs_table), //mcs_table_index
|
||||
T_INT((int)rel15_ul->nrOfLayers), // num_layer
|
||||
T_INT((int)rel15_ul->transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
||||
T_INT((int)rel15_ul->dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
||||
T_INT((int)rel15_ul->ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
||||
T_INT((int)number_dmrs_symbols),
|
||||
//dmrs_start_ofdm_symbol
|
||||
//dmrs_duration_num_ofdm_symbols
|
||||
//dmrs_num_add_positions
|
||||
T_INT((int)dmrs_port), //dmrs_antenna_port
|
||||
T_INT((int)rel15_ul->scid), //dmrs_nscid
|
||||
T_INT((int)frame_parms->nb_antennas_rx), // rx antenna
|
||||
T_INT(0), //number_of_bits
|
||||
T_BUFFER((c16_t*)(&(rxFext_slot_mem[0])), rel15_ul->rb_size * NR_NB_SC_PER_RB * rel15_ul->nr_of_symbols * frame_parms->nb_antennas_rx * 4));
|
||||
}
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL)) {
|
||||
// Log pusch_ch_est_dmrs_interpl_slot_mem using T-Tracer if activated
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
|
||||
T(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL,
|
||||
T_INT((int)frame),
|
||||
T_INT((int)slot),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 0, 8)),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 8, 9)),
|
||||
T_INT((int)frame_parms->frame_type), // Frame type (0 FDD, 1 TDD) frame_structure
|
||||
T_INT((int)frame_parms->freq_range), // Frequency range (0 FR1, 1 FR2)
|
||||
T_INT((int)rel15_ul->subcarrier_spacing), // Subcarrier spacing (0 15kHz, 1 30kHz, 2 60kHz)
|
||||
T_INT((int)rel15_ul->cyclic_prefix), // Normal or extended prefix (0 normal, 1 extended)
|
||||
T_INT((int)frame_parms->symbols_per_slot), // Number of symbols per slot
|
||||
T_INT((int)frame_parms->Nid_cell),
|
||||
T_INT((int)rel15_ul->rnti),
|
||||
T_INT((int)rel15_ul->rb_size),
|
||||
T_INT((int)rel15_ul->rb_start),
|
||||
T_INT((int)rel15_ul->start_symbol_index), //start_ofdm_symbol
|
||||
T_INT((int)rel15_ul->nr_of_symbols), //num_ofdm_symbols
|
||||
T_INT((int)rel15_ul->qam_mod_order), //modulation
|
||||
T_INT((int)rel15_ul->mcs_index), //mcs
|
||||
T_INT((int)rel15_ul->mcs_table), //mcs_table_index
|
||||
T_INT((int)rel15_ul->nrOfLayers), // num_layer
|
||||
T_INT((int)rel15_ul->transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
||||
T_INT((int)rel15_ul->dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
||||
T_INT((int)rel15_ul->ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
||||
T_INT((int)number_dmrs_symbols),
|
||||
//dmrs_start_ofdm_symbol
|
||||
//dmrs_duration_num_ofdm_symbols
|
||||
//dmrs_num_add_positions
|
||||
T_INT((int)dmrs_port), //dmrs_antenna_port
|
||||
T_INT((int)rel15_ul->scid), //dmrs_nscid
|
||||
T_INT((int)frame_parms->nb_antennas_rx), // rx antenna
|
||||
T_INT(0), //number_of_bits
|
||||
T_BUFFER((c16_t*)pusch_ch_est_dmrs_interpl_slot_mem,
|
||||
rel15_ul->rb_size * NR_NB_SC_PER_RB * rel15_ul->nr_of_symbols * frame_parms->nb_antennas_rx * rel15_ul->nrOfLayers * 4));
|
||||
}
|
||||
#endif
|
||||
|
||||
join_task_ans(&ans);
|
||||
stop_meas(&gNB->rx_pusch_symbol_processing_stats);
|
||||
|
||||
|
||||
@@ -109,7 +109,8 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
const uint8_t slot,
|
||||
unsigned int *G,
|
||||
int nb_ulsch,
|
||||
uint8_t *ULSCH_ids);
|
||||
uint8_t *ULSCH_ids,
|
||||
uint16_t number_dmrs_symbols);
|
||||
|
||||
/*! \brief Perform PUSCH scrambling. TS 38.211 V15.4.0 subclause 6.3.1.1
|
||||
@param[in] in Pointer to input bits
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h"
|
||||
#include "executables/nr-uesoftmodem.h"
|
||||
#include "common/utils/LOG/vcd_signal_dumper.h"
|
||||
#include "PHY/log_tools.h"
|
||||
|
||||
|
||||
int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
NR_UE_ULSCH_t *ulsch,
|
||||
@@ -39,7 +41,8 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
const uint8_t slot,
|
||||
unsigned int *G,
|
||||
int nb_ulsch,
|
||||
uint8_t *ULSCH_ids)
|
||||
uint8_t *ULSCH_ids,
|
||||
uint16_t number_dmrs_symbols)
|
||||
{
|
||||
start_meas_nr_ue_phy(ue, ULSCH_ENCODING_STATS);
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_UE_ULSCH_ENCODING, VCD_FUNCTION_IN);
|
||||
@@ -108,6 +111,59 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
|
||||
AssertFatal((A / 8) + 3 <= max_payload_bytes, "A %d is too big (A/8+3 = %d > %d)\n", A, (A / 8) + 3, max_payload_bytes);
|
||||
}
|
||||
|
||||
#if T_TRACER
|
||||
if (T_ACTIVE(T_UE_PHY_UL_PAYLOAD_TX_BITS)) {
|
||||
// Get Time Stamp for T-tracer messages
|
||||
char trace_tx_payload_time_stamp_str[30];
|
||||
get_time_stamp_usec(trace_tx_payload_time_stamp_str);
|
||||
// trace_time_stamp_str = 8 bytes timestamp = YYYYMMDD
|
||||
// + 9 bytes timestamp = HHMMSSMMM
|
||||
// Log UE_PHY_UL_PAYLOAD_TX_BITS using T-Tracer if activated
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid :
|
||||
// nb_antennas_tx : int,number_of_bits : buffer,data
|
||||
// Define the subcarrier spacing vector
|
||||
//int subcarrier_spacing_vect[] = {15000, 30000, 60000, 120000};
|
||||
NR_DL_FRAME_PARMS *frame_parms = &ue->frame_parms;
|
||||
int subcarrier_spacing_index = frame_parms->subcarrier_spacing / 15000 -1;
|
||||
T(T_UE_PHY_UL_PAYLOAD_TX_BITS,
|
||||
T_INT((int)frame),
|
||||
T_INT((int)slot),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_tx_payload_time_stamp_str, 0, 8)),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_tx_payload_time_stamp_str, 8, 9)),
|
||||
T_INT((int)frame_parms->frame_type), // Frame type (0 FDD, 1 TDD) frame_structure
|
||||
T_INT((int)frame_parms->freq_range), // Frequency range (0 FR1, 1 FR2)
|
||||
T_INT((int)subcarrier_spacing_index), // Subcarrier spacing (0 15kHz, 1 30kHz, 2 60kHz)
|
||||
T_INT((int)ulsch->pusch_pdu.cyclic_prefix), // Normal or extended prefix (0 normal, 1 extended)
|
||||
T_INT((int)frame_parms->symbols_per_slot), // Number of symbols per slot
|
||||
T_INT((int)frame_parms->Nid_cell),
|
||||
T_INT((int)ulsch->pusch_pdu.rnti),
|
||||
T_INT((int)ulsch->pusch_pdu.rb_size),
|
||||
T_INT((int)ulsch->pusch_pdu.rb_start),
|
||||
T_INT((int)ulsch->pusch_pdu.start_symbol_index), //start_ofdm_symbol
|
||||
T_INT((int)ulsch->pusch_pdu.nr_of_symbols), //num_ofdm_symbols
|
||||
T_INT((int)ulsch->pusch_pdu.qam_mod_order), //modulation
|
||||
T_INT((int)ulsch->pusch_pdu.mcs_index), //mcs
|
||||
T_INT((int)ulsch->pusch_pdu.mcs_table), //mcs_table_index
|
||||
T_INT((int)ulsch->pusch_pdu.nrOfLayers), // num_layer
|
||||
T_INT((int)ulsch->pusch_pdu.transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
||||
T_INT((int)ulsch->pusch_pdu.dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
||||
T_INT((int)ulsch->pusch_pdu.ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
||||
T_INT((int)number_dmrs_symbols),
|
||||
//dmrs_start_ofdm_symbol
|
||||
//dmrs_duration_num_ofdm_symbols
|
||||
//dmrs_num_add_positions
|
||||
T_INT((int)get_dmrs_port(0,ulsch->pusch_pdu.dmrs_ports)), //dmrs_antenna_port
|
||||
T_INT((int)ulsch->pusch_pdu.scid), //dmrs_nscid
|
||||
T_INT((int)frame_parms->nb_antennas_tx), // number of tx antennas
|
||||
T_INT((int)A), //number_of_bits
|
||||
T_BUFFER((uint8_t*)harq_process->payload_AB, A/8));
|
||||
}
|
||||
#endif
|
||||
///////////////////////// b---->| block segmentation |---->c /////////////////////////
|
||||
|
||||
harq_process->BG = pusch_pdu->ldpcBaseGraph;
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "executables/softmodem-common.h"
|
||||
#include "PHY/NR_REFSIG/ul_ref_seq_nr.h"
|
||||
#include <openair2/UTIL/OPT/opt.h>
|
||||
#include "PHY/log_tools.h"
|
||||
|
||||
//#define DEBUG_PUSCH_MAPPING
|
||||
//#define DEBUG_MAC_PDU
|
||||
@@ -539,7 +540,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||||
|
||||
/////////////////////////ULSCH coding/////////////////////////
|
||||
|
||||
if (nr_ulsch_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids) == -1) {
|
||||
if (nr_ulsch_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids, number_dmrs_symbols) == -1) {
|
||||
NR_UL_UE_HARQ_t *harq_process_ulsch = &UE->ul_harq_processes[harq_pid];
|
||||
harq_process_ulsch->ULstatus = SCH_IDLE;
|
||||
stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
|
||||
@@ -600,7 +601,62 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
|
||||
rnti,
|
||||
false,
|
||||
scrambled_output);
|
||||
#if T_TRACER
|
||||
if (T_ACTIVE(T_UE_PHY_UL_SCRAMBLED_TX_BITS)) {
|
||||
// Get Time Stamp for T-tracer messages
|
||||
char trace_time_stamp_str[30];
|
||||
get_time_stamp_usec(trace_time_stamp_str);
|
||||
// trace_time_stamp_str = 8 bytes timestamp = YYYYMMDD
|
||||
// + 9 bytes timestamp = HHMMSSMMM
|
||||
|
||||
int dmrs_port = get_dmrs_port(0, pusch_pdu->dmrs_ports);
|
||||
const uint8_t *in_bytes = (const uint8_t *)scrambled_output;
|
||||
|
||||
// Log UE_PHY_UL_SCRAMBLED_TX_BITS using T-Tracer if activated
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid :
|
||||
// nb_antennas_tx : int,number_of_bits : buffer,data
|
||||
// Define the subcarrier spacing vector
|
||||
//int subcarrier_spacing_vect[] = {15000, 30000, 60000, 120000};
|
||||
int subcarrier_spacing_index = frame_parms->subcarrier_spacing / 15000 -1;
|
||||
T(T_UE_PHY_UL_SCRAMBLED_TX_BITS,
|
||||
T_INT((int)frame),
|
||||
T_INT((int)slot),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 0, 8)),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_time_stamp_str, 8, 9)),
|
||||
T_INT((int)frame_parms->frame_type), // Frame type (0 FDD, 1 TDD) frame_structure
|
||||
T_INT((int)frame_parms->freq_range), // Frequency range (0 FR1, 1 FR2)
|
||||
T_INT((int)subcarrier_spacing_index), // Subcarrier spacing (0 15kHz, 1 30kHz, 2 60kHz)
|
||||
T_INT((int)pusch_pdu->cyclic_prefix), // Normal or extended prefix (0 normal, 1 extended)
|
||||
T_INT((int)frame_parms->symbols_per_slot), // Number of symbols per slot
|
||||
T_INT((int)frame_parms->Nid_cell),
|
||||
T_INT((int)pusch_pdu->rnti),
|
||||
T_INT((int)pusch_pdu->rb_size),
|
||||
T_INT((int)pusch_pdu->rb_start),
|
||||
T_INT((int)pusch_pdu->start_symbol_index), //start_ofdm_symbol
|
||||
T_INT((int)pusch_pdu->nr_of_symbols), //num_ofdm_symbols
|
||||
T_INT((int)pusch_pdu->qam_mod_order), //modulation
|
||||
T_INT((int)pusch_pdu->mcs_index), //mcs
|
||||
T_INT((int)pusch_pdu->mcs_table), //mcs_table_index
|
||||
T_INT((int)pusch_pdu->nrOfLayers), // num_layer
|
||||
T_INT((int)pusch_pdu->transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
||||
T_INT((int)pusch_pdu->dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
||||
T_INT((int)pusch_pdu->ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
||||
T_INT((int)number_dmrs_symbols),
|
||||
//dmrs_start_ofdm_symbol
|
||||
//dmrs_duration_num_ofdm_symbols
|
||||
//dmrs_num_add_positions
|
||||
T_INT((int)dmrs_port), //dmrs_antenna_port
|
||||
T_INT((int)pusch_pdu->scid), //dmrs_nscid
|
||||
T_INT((int)frame_parms->nb_antennas_tx), // number of tx antennas
|
||||
T_INT((int)available_bits), //number_of_bits
|
||||
T_BUFFER((uint8_t*)in_bytes, available_bits /8));
|
||||
}
|
||||
#endif
|
||||
/////////////////////////ULSCH modulation/////////////////////////
|
||||
|
||||
int max_num_re = Nl * number_of_symbols * nb_rb * NR_NB_SC_PER_RB;
|
||||
|
||||
@@ -595,6 +595,8 @@ typedef struct puschAntennaProc_s {
|
||||
NR_DL_FRAME_PARMS *frame_parms;
|
||||
c16_t ***rxdataF;
|
||||
task_ans_t* ans;
|
||||
c16_t *pusch_ch_est_dmrs_pos_slot_mem;
|
||||
int dmrs_symbol_start_idx;
|
||||
} puschAntennaProc_t;
|
||||
|
||||
struct puschAntennaReqId {
|
||||
|
||||
80
openair1/PHY/log_tools.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file openair1/PHY/log_tools.c
|
||||
* \brief log tools used by data recording application (to be merged to existing OAI log tools)
|
||||
* \author Abdo Gaber
|
||||
* \date 2024
|
||||
* \version 1.0
|
||||
* \company Emerson, NI Test and Measurement
|
||||
* \email:
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#include "log_tools.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
#include "PHY/TOOLS/tools_defs.h"
|
||||
|
||||
// Get Time Stamp in microseconds in YYYYMMDDHHMMSSmmmuuu format
|
||||
char* get_time_stamp_usec(char time_stamp_str[]){
|
||||
// initialization to measure time stamp --This part should be moved to inilization part
|
||||
time_t my_time;
|
||||
struct tm * timeinfo;
|
||||
time (&my_time);
|
||||
struct timeval tv;
|
||||
|
||||
// get time stamp
|
||||
timeinfo = localtime (&my_time);
|
||||
gettimeofday(&tv,NULL);
|
||||
// Add time stamp: yyyy mm dd hh mm ss msec
|
||||
uint16_t year= timeinfo->tm_year+1900;
|
||||
uint8_t mon= timeinfo->tm_mon+1;
|
||||
uint8_t mday= timeinfo->tm_mday;
|
||||
uint8_t hour= timeinfo->tm_hour;
|
||||
uint8_t min= timeinfo->tm_min;
|
||||
uint8_t sec= timeinfo->tm_sec;
|
||||
uint16_t usec= (tv.tv_usec);
|
||||
//printf ("Time stamp: %d_%d_%d_%d_%d_%d_%d \n",year,mon,mday,hour,min,sec,usec);
|
||||
//sprintf(time_stamp_str, "%d_%d_%d_%d_%d_%d_%d",year,mon,mday,hour,min,sec,usec);
|
||||
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06d", year, mon, mday, hour, min, sec, usec);
|
||||
|
||||
return time_stamp_str;
|
||||
}
|
||||
|
||||
// Function to convert timestamp string to integer
|
||||
int convert_time_stamp_to_int(const char* timestamp) {
|
||||
return atoi(timestamp);
|
||||
}
|
||||
|
||||
int split_time_stamp_and_convert_to_int (char time_stamp_str[], int shift, int length){
|
||||
char time_part[length+1]; // Buffer to hold the date part YYYYMMDD or HHMMSSmmm
|
||||
// Copy the first 8 or 9 characters (YYYYMMDD) to HHMMSSmmm
|
||||
strncpy(time_part, time_stamp_str + shift, length);
|
||||
time_part[length] = '\0'; // Null-terminate the string
|
||||
// Convert timestamp string to integer
|
||||
return convert_time_stamp_to_int(time_part);
|
||||
}
|
||||
43
openair1/PHY/log_tools.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file openair1/PHY/log_tools.h
|
||||
* \brief log tools used by data recording application (to be merged to existing OAI log tools)
|
||||
* \author Abdo Gaber
|
||||
* \date 2024
|
||||
* \version 1.0
|
||||
* \company Emerson, NI Test and Measurement
|
||||
* \email:
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#ifndef __PHY_LOG_TOOLS_H__
|
||||
#define __PHY_LOG_TOOLS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "PHY/TOOLS/tools_defs.h"
|
||||
|
||||
char* get_time_stamp_usec(char time_stamp_str[]);
|
||||
int convert_time_stamp_to_int(const char* timestamp);
|
||||
int split_time_stamp_and_convert_to_int (char time_stamp_str[], int shift, int length);
|
||||
|
||||
#endif /*__PHY_LOG_TOOLS_H__ */
|
||||
@@ -39,8 +39,10 @@
|
||||
#include "nfapi/oai_integration/vendor_ext.h"
|
||||
#include "assertions.h"
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <openair1/PHY/TOOLS/phy_scope_interface.h>
|
||||
#include "PHY/log_tools.h"
|
||||
|
||||
//#define DEBUG_RXDATA
|
||||
//#define SRS_IND_DEBUG
|
||||
@@ -402,6 +404,68 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo
|
||||
crc_valid = true;
|
||||
}
|
||||
}
|
||||
#if T_TRACER
|
||||
if (T_ACTIVE(T_GNB_PHY_UL_PAYLOAD_RX_BITS)) {
|
||||
// capture Rx Payload via T-Tracer for both CRC valid and invalid cases
|
||||
// Get Time Stamp for T-tracer messages
|
||||
char trace_rx_payload_time_stamp_str[30];
|
||||
get_time_stamp_usec(trace_rx_payload_time_stamp_str);
|
||||
// trace_rx_payload_time_stamp_str = 8 bytes timestamp = YYYYMMDD
|
||||
// + 9 bytes timestamp = HHMMSSMMM
|
||||
|
||||
// Log GNB_PHY_UL_PAYLOAD_RX_BITS using T-Tracer if activated
|
||||
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
|
||||
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
|
||||
// int,Nid_cell : int,rnti :
|
||||
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
|
||||
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
|
||||
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid :
|
||||
// int,nb_antennas_rx : int,number_of_bits : buffer,data
|
||||
|
||||
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
|
||||
int dmrs_port = get_dmrs_port(0, pusch_pdu->dmrs_ports);
|
||||
//int num_bytes = rdata->Kr_bytes - (ulsch_harq->F >> 3) - ((ulsch_harq->C > 1) ? 3 : 0);
|
||||
//printf("num_bytes %d, len with CRC %d, data len %d\n", num_bytes, lenWithCrc(1, rdata->A), rdata->A);
|
||||
// calculate the number of dmrs symbols in the slot
|
||||
int number_dmrs_symbols = 0;
|
||||
for (int l = pusch_pdu->start_symbol_index; l < pusch_pdu->start_symbol_index + pusch_pdu->nr_of_symbols; l++)
|
||||
number_dmrs_symbols += ((pusch_pdu->ul_dmrs_symb_pos)>>l) & 0x01;
|
||||
|
||||
// Log GNB_PHY_UL_PAYLOAD_RX_BITS using T-Tracer if activated
|
||||
T(T_GNB_PHY_UL_PAYLOAD_RX_BITS,
|
||||
T_INT((int)ulsch->frame),
|
||||
T_INT((int)ulsch->slot),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_rx_payload_time_stamp_str, 0, 8)),
|
||||
T_INT((int)split_time_stamp_and_convert_to_int(trace_rx_payload_time_stamp_str, 8, 9)),
|
||||
T_INT((int)frame_parms->frame_type), // Frame type (0 FDD, 1 TDD) frame_structure
|
||||
T_INT((int)frame_parms->freq_range), // Frequency range (0 FR1, 1 FR2)
|
||||
T_INT((int)pusch_pdu->subcarrier_spacing), // Subcarrier spacing (0 15kHz, 1 30kHz, 2 60kHz)
|
||||
T_INT((int)pusch_pdu->cyclic_prefix), // Normal or extended prefix (0 normal, 1 extended)
|
||||
T_INT((int)frame_parms->symbols_per_slot), // Number of symbols per slot
|
||||
T_INT((int)frame_parms->Nid_cell),
|
||||
T_INT((int)pusch_pdu->rnti),
|
||||
T_INT((int)pusch_pdu->rb_size),
|
||||
T_INT((int)pusch_pdu->rb_start),
|
||||
T_INT((int)pusch_pdu->start_symbol_index), //start_ofdm_symbol
|
||||
T_INT((int)pusch_pdu->nr_of_symbols), //num_ofdm_symbols
|
||||
T_INT((int)pusch_pdu->qam_mod_order), //modulation
|
||||
T_INT((int)pusch_pdu->mcs_index), //mcs
|
||||
T_INT((int)pusch_pdu->mcs_table), //mcs_table_index
|
||||
T_INT((int)pusch_pdu->nrOfLayers), // num_layer
|
||||
T_INT((int)pusch_pdu->transform_precoding), // transformPrecoder_enabled = 0, transformPrecoder_disabled = 1
|
||||
T_INT((int)pusch_pdu->dmrs_config_type), // dmrs_resource_map_config: pusch_dmrs_type1 = 0, pusch_dmrs_type2 = 1
|
||||
T_INT((int)pusch_pdu->ul_dmrs_symb_pos), // used to derive the DMRS symbol positions
|
||||
T_INT((int)number_dmrs_symbols),
|
||||
//dmrs_start_ofdm_symbol
|
||||
//dmrs_duration_num_ofdm_symbols
|
||||
//dmrs_num_add_positions
|
||||
T_INT((int)dmrs_port), //dmrs_antenna_port
|
||||
T_INT((int)pusch_pdu->scid), //dmrs_nscid
|
||||
T_INT((int)frame_parms->nb_antennas_rx), // rx antenna
|
||||
T_INT(((ulsch_harq->TBS) << 3)), //number_of_bits
|
||||
T_BUFFER((uint8_t*)((ulsch_harq->b)), ((ulsch_harq->TBS) << 3)/8)); //data
|
||||
}
|
||||
#endif
|
||||
|
||||
nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++];
|
||||
nfapi_nr_rx_data_pdu_t *pdu = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++];
|
||||
|
||||
@@ -513,7 +513,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (input_fd == NULL) {
|
||||
uint8_t ULSCH_ids[] = {0};
|
||||
nr_ulsch_encoding(UE, ulsch_ue, 0, 0, &G, 1, ULSCH_ids);
|
||||
nr_ulsch_encoding(UE, ulsch_ue, 0, 0, &G, 1, ULSCH_ids, 0);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||