terminal profile: fix the per-byte bit order (b1 = LSB), add the inference notes (v2.2.20)
The spec tables (TS 102 223 5.2) list b1 - the least significant bit - first:
verified from the PDF connector geometry (label row 1 connects to the
rightmost cell) and from Wireshark's packet-gsm_sim.c masks ('Profile
download' = 0x01, 'E-UTRAN bearer' = 0x40, 'HSDPA bearer' = 0x80). The
Configure dialog had it mirrored: TP_BITS[n] was matched with 0x80 >> (n%8)
and displayed as b8..b1, so every label toggled the wrong physical bit
(our own BIP-capable default proved it: byte 12 = 0x1F read as DECLARE
SERVICE.. instead of OPEN/CLOSE/RECEIVE/SEND/GET CHANNEL STATUS).
- tpGetBit/tpSetBit now use 1 << (n%8); the form lists b1 first, matching
the spec; tpBitLabel names the RFU fallback bit accordingly.
- Tests pin the new mapping and the corrected spot-check indices, and the
project default is asserted to decode as BIP-capable (OPEN/SEND DATA,
E-UTRAN, HSDPA).
- docs/TERMINAL_PROFILE.md (in projects/docs): everything gathered about
inferring device characteristics from a TP - bit order, profile-length era
bands, headless/modem markers (Annex S ND/NK plus the byte 11/screen-size
heuristics), radio generation (byte 17 b7/b8 with the 'capability, not
attach' caveat), standards family (ESN/IMEISV/TIA/CCAT), BIP capability,
UI paradigm, and a suggested inference model. Corpus: samples/tp_db.csv
(185 profiles, CC-BY-SA, kept out of the repo).
- Help EN/RU note the bit order and point at the new document; AGENTS
records the mapping rule and the doc location.
This commit is contained in:
@@ -459,7 +459,7 @@
|
||||
<p class="text-sm mb-2">Хронологический список извлечённых проактивных команд. Каждая строка показывает время, код типа, имя и декодированный квалификатор (для команд, у которых он есть). Раскрытие строки показывает декодированную команду: текст и пункты DISPLAY TEXT / SELECT ITEM / SET UP MENU, параметры BIP-каналов, действия TIMER MANAGEMENT, названия квалификаторов PROVIDE LOCAL INFORMATION (все стандартные, включая ESN 07, MEID 0B и Supported RATs 1A) и SEND SHORT MESSAGE — разбирается SMS TPDU (тип, TP-MR, TP-DA, TP-PID, TP-DCS, срок действия, TP-UDL), а пользовательские данные показываются как текст для текстовых кодировок (GSM 7-бит, UCS2, 8-бит) или как secured-пакет (TS 31.115) при TP-PID = <code class="font-mono text-sm">7F</code> (SIM data download); заголовки конкатенации UDH декодируются. Для команд с данными ответа показывается строка <code class="font-mono text-sm">Ответ:</code> с байтами TERMINAL RESPONSE (без служебных TLV); ответы PROVIDE LOCAL INFORMATION декодируются через словарь данных PLI.</p>
|
||||
|
||||
<h3 id="terminal-profile" class="text-lg font-medium mb-2">8.4 TERMINAL PROFILE</h3>
|
||||
<p class="text-sm mb-3">Блок <strong>TERMINAL PROFILE</strong> (рядом с «STATUS и опрос») содержит кнопки <strong>«Отправить»</strong> (повторная отправка, как «Спасение») и <strong>«Настроить»</strong>. В диалоге настройки: селектор пресетов (модели устройств, например профиль BIP-совместимого аппарата из этого проекта), поле hex и форма с флажком на каждый бит профиля по <strong>TS 102 223 §5.2</strong> / <strong>TS 31.111 §5.2</strong> (байты 1–39; для битов 3GPP используются имена из TS 31.111); каждый байт — вертикальный список битов; блоки размещены фиксированными группами (байты 1–12 в 2 колонки, 13–16 в 4, 17–18 в 2, 19–21 в 3, 22–25 в 2, 26–28 в 3, 29–30 в 2, далее по одному в строке); переключение бита обновляет hex, а правка hex перерисовывает форму — поле hex основное, неизвестные байты и биты сохраняются. Кнопка <strong>«Применить»</strong> отправляет новое значение на сервер (и далее на карту), сбрасывая STK-сессию как «Спасение»; изменение хранится только в памяти (значение <code class="font-mono text-sm">--terminal-profile</code> — стартовое по умолчанию).</p>
|
||||
<p class="text-sm mb-3">Блок <strong>TERMINAL PROFILE</strong> (рядом с «STATUS и опрос») содержит кнопки <strong>«Отправить»</strong> (повторная отправка, как «Спасение») и <strong>«Настроить»</strong>. В диалоге настройки: селектор пресетов (модели устройств, например профиль BIP-совместимого аппарата из этого проекта), поле hex и форма с флажком на каждый бит профиля по <strong>TS 102 223 §5.2</strong> / <strong>TS 31.111 §5.2</strong> (байты 1–39; для битов 3GPP используются имена из TS 31.111); каждый байт — вертикальный список битов в порядке спецификации (<strong>b1 — младший бит, первая строка таблицы</strong>); блоки размещены фиксированными группами (байты 1–12 в 2 колонки, 13–16 в 4, 17–18 в 2, 19–21 в 3, 22–25 в 2, 26–28 в 3, 29–30 в 2, далее по одному в строке); переключение бита обновляет hex, а правка hex перерисовывает форму — поле hex основное, неизвестные байты и биты сохраняются. Кнопка <strong>«Применить»</strong> отправляет новое значение на сервер (и далее на карту), сбрасывая STK-сессию как «Спасение»; изменение хранится только в памяти (значение <code class="font-mono text-sm">--terminal-profile</code> — стартовое по умолчанию). Правила вывода характеристик устройства из TP (поколение связи, бездисплейные модемы, семейство стандартов) собраны в <code class="font-mono text-sm">docs/TERMINAL_PROFILE.md</code>.</p>
|
||||
|
||||
<h3 id="status-polling" class="text-lg font-medium mb-2">8.5 Опрос STATUS</h3>
|
||||
<p class="text-sm mb-3">Кнопка <strong>Отправить STATUS</strong> отправляет STATUS (F2) вручную. Переключатель <strong>Опрос</strong> включает фоновый опрос: после настраиваемого интервала бездействия (аргумент сервера <code class="font-mono text-sm">--poll-interval</code>, 1–255 с, по умолчанию 30 с, <code class="font-mono text-sm">0</code> отключает опрос) сервер отправляет STATUS и обрабатывает любую ожидающую проактивную команду. При извлечении карты опрос останавливается, а состояние карты сбрасывается.</p>
|
||||
|
||||
+1
-1
@@ -459,7 +459,7 @@
|
||||
<p class="text-sm mb-2">Chronological list of fetched proactive commands. Each row shows the elapsed time, type code, name, and a decoded qualifier (for commands that have one). Expanding a row shows the decoded command: DISPLAY TEXT / SELECT ITEM / SET UP MENU text and items, BIP channel parameters, TIMER MANAGEMENT actions, PROVIDE LOCAL INFORMATION qualifier names (all standard qualifiers, including ESN 07, MEID 0B and Supported RATs 1A), and SEND SHORT MESSAGE — the SMS TPDU is parsed (type, TP-MR, TP-DA, TP-PID, TP-DCS, validity period, TP-UDL) with the user data shown as text for text codings (GSM 7-bit, UCS2, 8-bit) or as a secured packet (TS 31.115) when TP-PID = <code class="font-mono text-sm">7F</code> (SIM data download); UDH concatenation headers are decoded. Commands with response data show a <code class="font-mono text-sm">Response:</code> line with the TERMINAL RESPONSE bytes (boilerplate TLVs stripped); PROVIDE LOCAL INFORMATION responses are decoded using the PLI data dictionary decoders.</p>
|
||||
|
||||
<h3 id="terminal-profile" class="text-lg font-medium mb-2">8.4 TERMINAL PROFILE</h3>
|
||||
<p class="text-sm mb-3">The <strong>TERMINAL PROFILE</strong> block (next to STATUS and Polling) offers <strong>Send</strong> (re-sends it, like Rescue) and <strong>Configure</strong>. The Configure dialog has a preset selector (device models, e.g. this project's BIP-capable handset profile), a hex field and a form with one checkbox per profile bit decoded per <strong>TS 102 223 §5.2</strong> / <strong>TS 31.111 §5.2</strong> (bytes 1–39, 3GPP-defined bits use the TS 31.111 names), each byte is a vertical list of its bits, placed in fixed column groups (bytes 1-12 in 2 columns, 13-16 in 4, 17-18 in 2, 19-21 in 3, 22-25 in 2, 26-28 in 3, 29-30 in 2, later bytes one per row); toggling a bit updates the hex and editing the hex re-renders the form — the hex field is authoritative and unknown bytes/bits are preserved. <strong>Apply</strong> sends the new value to the server (and on to the card), resetting the STK session like Rescue; the change is in-memory only (the <code class="font-mono text-sm">--terminal-profile</code> CLI value is the startup default).</p>
|
||||
<p class="text-sm mb-3">The <strong>TERMINAL PROFILE</strong> block (next to STATUS and Polling) offers <strong>Send</strong> (re-sends it, like Rescue) and <strong>Configure</strong>. The Configure dialog has a preset selector (device models, e.g. this project's BIP-capable handset profile), a hex field and a form with one checkbox per profile bit decoded per <strong>TS 102 223 §5.2</strong> / <strong>TS 31.111 §5.2</strong> (bytes 1–39, 3GPP-defined bits use the TS 31.111 names), each byte is a vertical list of its bits in spec order (<strong>b1 is the least significant bit and the first table row</strong>), placed in fixed column groups (bytes 1-12 in 2 columns, 13-16 in 4, 17-18 in 2, 19-21 in 3, 22-25 in 2, 26-28 in 3, 29-30 in 2, later bytes one per row); toggling a bit updates the hex and editing the hex re-renders the form — the hex field is authoritative and unknown bytes/bits are preserved. <strong>Apply</strong> sends the new value to the server (and on to the card), resetting the STK session like Rescue; the change is in-memory only (the <code class="font-mono text-sm">--terminal-profile</code> CLI value is the startup default). The rules for inferring device characteristics from a TP (radio generation, headless modems, standards family) are collected in <code class="font-mono text-sm">docs/TERMINAL_PROFILE.md</code>.</p>
|
||||
|
||||
<h3 id="status-polling" class="text-lg font-medium mb-2">8.5 STATUS polling</h3>
|
||||
<p class="text-sm mb-3">A <strong>Send STATUS</strong> button issues a manual STATUS (F2). A <strong>Polling</strong> toggle enables background polling: after a configurable idle interval (server CLI <code class="font-mono text-sm">--poll-interval</code>, 1–255 s, default 30 s, <code class="font-mono text-sm">0</code> disables polling) the server sends STATUS and handles any pending proactive command. Polling stops and card state resets if the card is removed.</p>
|
||||
|
||||
+12
-12
@@ -18,7 +18,7 @@
|
||||
<div class="max-w-7xl mx-auto px-6 py-2">
|
||||
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h1 class="text-2xl font-bold text-heading">OTAMan <span id="slogan" class="text-sm font-normal text-gray-500 dark:text-slate-400 ml-2" data-l10n="SIM OTA with a Human Face">SIM OTA with a Human Face</span> <span class="text-xs text-gray-400 dark:text-slate-500 ml-1">v2.2.19</span></h1>
|
||||
<h1 class="text-2xl font-bold text-heading">OTAMan <span id="slogan" class="text-sm font-normal text-gray-500 dark:text-slate-400 ml-2" data-l10n="SIM OTA with a Human Face">SIM OTA with a Human Face</span> <span class="text-xs text-gray-400 dark:text-slate-500 ml-1">v2.2.20</span></h1>
|
||||
<div class="flex items-center gap-4">
|
||||
<span id="state-indicator" class="flex items-center select-none" style="cursor:default" title="Connecting...">
|
||||
<span id="state-indicator-dot" class="text-xs text-gray-400" title="Connecting...">●</span>
|
||||
@@ -7170,15 +7170,11 @@ const PLI_QUALIFIERS = [
|
||||
{code:'1A',name:'Supported Radio Access Technologies'},
|
||||
];
|
||||
|
||||
// TERMINAL PROFILE bit allocation (TS 102 223 V18.3.0 5.2 + 3GPP
|
||||
// annex bits; bytes 1..33). Index 0 = bit 1 = first byte b8; bit n is
|
||||
// byte floor(n/8)+1, mask 0x80 >> (n%8). Bytes beyond 33 fall back to
|
||||
// generic labels in the UI.
|
||||
// TERMINAL PROFILE bit allocation: bytes 1..39 per ETSI TS 102 223
|
||||
// V18.3.0 5.2, 3GPP-defined bits named per ETSI TS 131 111 V18.12.0 5.2
|
||||
// (no "reserved by 3GPP" placeholders). Index 0 = bit 1 = first byte b8;
|
||||
// bit n is byte floor(n/8)+1, mask 0x80 >> (n%8). Bytes beyond the
|
||||
// table fall back to generic labels in the UI.
|
||||
// (no "reserved by 3GPP" placeholders). Index 0 = the first spec table row
|
||||
// = byte 1 b1 (the LSB); bit n is byte floor(n/8)+1, mask 1 << (n%8).
|
||||
// Bytes beyond the table fall back to generic labels in the UI.
|
||||
const TP_BITS = [
|
||||
'Profile download', 'SMS-PP data download', 'Cell Broadcast data download', 'Menu selection',
|
||||
'SMS-PP data download', 'Timer expiration', 'USSD string data object support in Call Control by USIM', 'Call Control by NAA',
|
||||
@@ -7278,11 +7274,15 @@ function tpNorm(hex) { return (hex || '').toUpperCase().replace(/[^0-9A-F]/g, ''
|
||||
|
||||
function tpValid(hex) { return hex.length > 0 && hex.length % 2 === 0 && hex.length <= 510; }
|
||||
|
||||
// Bit numbering follows TS 102 223 5.2 exactly: within a byte the spec tables
|
||||
// list b1 (the least significant bit) first, so TP_BITS[n] is bit b((n % 8)+1)
|
||||
// of byte (n >> 3)+1. tpGetBit/tpSetBit therefore use the LSB-first mask
|
||||
// (cross-checked against Wireshark's packet-gsm_sim.c: "Profile download"=0x01).
|
||||
function tpGetBit(hex, n) {
|
||||
const byteIdx = n >> 3;
|
||||
if (byteIdx * 2 + 2 > hex.length) return null;
|
||||
const b = parseInt(hex.substr(byteIdx * 2, 2), 16);
|
||||
return !!(b & (0x80 >> (n & 7)));
|
||||
return !!(b & (1 << (n & 7)));
|
||||
}
|
||||
|
||||
function tpSetBit(hex, n, on) {
|
||||
@@ -7290,7 +7290,7 @@ function tpSetBit(hex, n, on) {
|
||||
const byteIdx = n >> 3;
|
||||
while (bytes.length <= byteIdx) bytes.push('00');
|
||||
let b = parseInt(bytes[byteIdx], 16);
|
||||
const mask = 0x80 >> (n & 7);
|
||||
const mask = 1 << (n & 7);
|
||||
b = on ? (b | mask) : (b & ~mask);
|
||||
bytes[byteIdx] = b.toString(16).padStart(2, '0').toUpperCase();
|
||||
return bytes.join('');
|
||||
@@ -7298,7 +7298,7 @@ function tpSetBit(hex, n, on) {
|
||||
|
||||
function tpBitLabel(n) {
|
||||
if (n < TP_BITS.length) return TP_BITS[n];
|
||||
return 'RFU (byte ' + ((n >> 3) + 1) + ' b' + (8 - (n & 7)) + ')';
|
||||
return 'RFU (byte ' + ((n >> 3) + 1) + ' b' + ((n & 7) + 1) + ')';
|
||||
}
|
||||
|
||||
// Fixed byte-block layout: [firstByte, lastByte, columns] with 1-based
|
||||
@@ -7340,7 +7340,7 @@ function tpRenderForm() {
|
||||
html += '<div class="text-gray-500 dark:text-slate-400 mb-0.5">' + esc(t('Byte')) + ' ' + (bi + 1) + '</div>';
|
||||
for (let k = 0; k < 8; k++) {
|
||||
const n = bi * 8 + k;
|
||||
const mask = 'b' + (8 - k);
|
||||
const mask = 'b' + (k + 1); // b1 first, matching the spec tables
|
||||
const label = tpBitLabel(n);
|
||||
html += '<label class="flex items-start gap-1 mb-0.5">' +
|
||||
'<input type="checkbox" class="mt-0.5 shrink-0" ' +
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'otaman-v186';
|
||||
const CACHE = 'otaman-v187';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -39,48 +39,55 @@ test('tpNorm / tpValid normalize and validate profile hex', () => {
|
||||
assert.ok(!tpValid('F'));
|
||||
});
|
||||
|
||||
test('tpGetBit / tpSetBit address bits MSB-first per byte', () => {
|
||||
const hex = '80' + '01'; // byte 1 b8 set, byte 2 b1 set
|
||||
assert.strictEqual(tpGetBit(hex, 0), true); // byte 1 b8
|
||||
assert.strictEqual(tpGetBit(hex, 7), false); // byte 1 b1
|
||||
assert.strictEqual(tpGetBit(hex, 8), false); // byte 2 b8
|
||||
assert.strictEqual(tpGetBit(hex, 15), true); // byte 2 b1
|
||||
test('tpGetBit / tpSetBit address bits LSB-first (spec b1..b8 order)', () => {
|
||||
// TS 102 223 5.2 tables list b1 (LSB) first: index 0 = byte 1 b1 = 0x01
|
||||
const hex = '01' + '80'; // byte 1 b1 set, byte 2 b8 set
|
||||
assert.strictEqual(tpGetBit(hex, 0), true); // byte 1 b1
|
||||
assert.strictEqual(tpGetBit(hex, 7), false); // byte 1 b8
|
||||
assert.strictEqual(tpGetBit(hex, 8), false); // byte 2 b1
|
||||
assert.strictEqual(tpGetBit(hex, 15), true); // byte 2 b8
|
||||
assert.strictEqual(tpGetBit(hex, 16), null); // beyond the profile
|
||||
// toggling keeps the other bits untouched
|
||||
assert.strictEqual(tpSetBit('00', 0, true), '80');
|
||||
assert.strictEqual(tpSetBit('80', 0, false), '00');
|
||||
assert.strictEqual(tpSetBit('FF', 7, false), 'FE');
|
||||
assert.strictEqual(tpSetBit('00', 0, true), '01');
|
||||
assert.strictEqual(tpSetBit('01', 0, false), '00');
|
||||
assert.strictEqual(tpSetBit('FF', 7, false), '7F');
|
||||
// a bit beyond the current length grows the profile with zero bytes
|
||||
assert.strictEqual(tpSetBit('FF', 24, true), 'FF000080');
|
||||
assert.strictEqual(tpSetBit('FF', 24, true), 'FF000001');
|
||||
// the project default really is BIP-capable under this mapping
|
||||
const mi = 'FFFFFFFF7F9F00DFFF03021FE2000000C3FB000704117800710100000038428003';
|
||||
assert.strictEqual(tpGetBit(mi, 88), true); // byte 12 b1 = OPEN CHANNEL
|
||||
assert.strictEqual(tpGetBit(mi, 91), true); // byte 12 b4 = SEND DATA
|
||||
assert.strictEqual(tpGetBit(mi, 134), true); // byte 17 b7 = E-UTRAN
|
||||
assert.strictEqual(tpGetBit(mi, 135), true); // byte 17 b8 = HSDPA
|
||||
});
|
||||
|
||||
test('TERMINAL PROFILE bit table matches the spec spot checks', () => {
|
||||
assert.ok(TP_BITS.length >= 312, 'expected the full byte 1..39 table');
|
||||
assert.strictEqual(TP_BITS.length % 8, 0);
|
||||
assert.strictEqual(TP_BITS[0], 'Profile download'); // byte 1 b8
|
||||
assert.strictEqual(TP_BITS[16], 'Proactive UICC: DISPLAY TEXT'); // byte 3 b8
|
||||
assert.strictEqual(TP_BITS[32], 'Proactive UICC: SET UP EVENT LIST'); // byte 5 b8
|
||||
assert.strictEqual(TP_BITS[0], 'Profile download'); // byte 1 b1
|
||||
assert.strictEqual(TP_BITS[16], 'Proactive UICC: DISPLAY TEXT'); // byte 3 b1
|
||||
assert.strictEqual(TP_BITS[32], 'Proactive UICC: SET UP EVENT LIST'); // byte 5 b1
|
||||
assert.strictEqual(TP_BITS[42], 'Event: Data available'); // byte 6 b3
|
||||
assert.strictEqual(TP_BITS[50], 'Proactive UICC: PERFORM CARD APDU'); // byte 7 b6 (pySim said RESET)
|
||||
assert.strictEqual(TP_BITS[88], 'Proactive UICC: OPEN CHANNEL'); // byte 12 b8
|
||||
assert.strictEqual(TP_BITS[140], 'Proactive UICC: PROVIDE LOCAL INFORMATION (ESN)'); // byte 18 b4
|
||||
assert.strictEqual(TP_BITS[181], 'Proactive UICC: PROVIDE LOCAL INFORMATION (MEID)'); // byte 23 b3
|
||||
assert.strictEqual(TP_BITS[260], 'Proactive UICC: PROVIDE LOCAL INFORMATION (Supported Radio Access Technologies)');
|
||||
assert.strictEqual(TP_BITS[280], 'Data Connection Status Change Event support – PDU Connection'); // byte 36 b8
|
||||
assert.strictEqual(TP_BITS[50], 'Proactive UICC: PERFORM CARD APDU'); // byte 7 b3 (pySim said RESET)
|
||||
assert.strictEqual(TP_BITS[88], 'Proactive UICC: OPEN CHANNEL'); // byte 12 b1
|
||||
assert.strictEqual(TP_BITS[140], 'Proactive UICC: PROVIDE LOCAL INFORMATION (ESN)'); // byte 18 b5
|
||||
assert.strictEqual(TP_BITS[181], 'Proactive UICC: PROVIDE LOCAL INFORMATION (MEID)'); // byte 23 b6
|
||||
assert.strictEqual(TP_BITS[260], 'Proactive UICC: PROVIDE LOCAL INFORMATION (Supported Radio Access Technologies)'); // byte 33 b5
|
||||
assert.strictEqual(TP_BITS[280], 'Data Connection Status Change Event support – PDU Connection'); // byte 36 b1
|
||||
assert.strictEqual(tpBitLabel(0), 'Profile download');
|
||||
assert.match(tpBitLabel(400), /^RFU \(byte 51 b/); // beyond the table
|
||||
assert.match(tpBitLabel(400), /^RFU \(byte 51 b1\)/); // beyond the table
|
||||
});
|
||||
|
||||
test('3GPP-defined bits use the TS 31.111 names, not placeholders', () => {
|
||||
assert.ok(!TP_BITS.some(l => /reserved by 3gpp/i.test(l)), 'no "reserved by 3GPP" labels');
|
||||
assert.ok(!TP_BITS.some(l => /reserved by etsi/i.test(l)));
|
||||
// a few audited 3GPP bits (TS 31.111 5.2)
|
||||
// a few audited 3GPP bits (TS 31.111 5.2); byteLabels[0] is b1
|
||||
const byteLabels = b => TP_BITS.slice((b - 1) * 8, b * 8);
|
||||
assert.strictEqual(byteLabels(17)[6], 'E-UTRAN'); // byte 17 b2
|
||||
assert.strictEqual(byteLabels(17)[7], 'HSDPA'); // byte 17 b1
|
||||
assert.strictEqual(byteLabels(18)[5], 'CALL CONTROL on GPRS'); // byte 18 b3
|
||||
assert.strictEqual(byteLabels(25)[4], 'Event: Network Rejection for GERAN/UTRAN');
|
||||
assert.strictEqual(byteLabels(32)[0], 'IMS support'); // byte 32 b8
|
||||
assert.strictEqual(byteLabels(17)[6], 'E-UTRAN'); // byte 17 b7
|
||||
assert.strictEqual(byteLabels(17)[7], 'HSDPA'); // byte 17 b8
|
||||
assert.strictEqual(byteLabels(18)[5], 'CALL CONTROL on GPRS'); // byte 18 b6
|
||||
assert.strictEqual(byteLabels(25)[4], 'Event: Network Rejection for GERAN/UTRAN'); // byte 25 b5
|
||||
assert.strictEqual(byteLabels(32)[0], 'IMS support'); // byte 32 b1
|
||||
assert.strictEqual(byteLabels(34)[0], 'URI support for SEND SHORT MESSAGE');
|
||||
assert.match(byteLabels(39)[0], /NG-RAN\/Satellite NG-RAN Timing Advance/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user