TERMINAL PROFILE: device characteristics in list + detail views

- decode.py: _decode_terminal_profile (TS 102 223 §5.2, b1 = LSB per the
  spec tables): class (declared ND/NK at byte 14 b6/b7, strict likely/
  handset rules), generation (positive-only 3G/HSDPA, 4G/E-UTRAN from
  byte 17 — never a 2G claim, real devices under-declare), BIP (byte 12
  b1, byte 13 bearers/channels, byte 17 transports), standards family
  (byte 18 IMEISV/ESN), UI facts (soft keys/screen/markup), era band
- list summary replaces raw hex: e.g. '3G/4G · BIP', 'modem · BIP',
  'no BIP', fallback '<n>-byte profile'
- detail view: tpRows() panel with class/reason, radio bits, BIP,
  standards, UI, profile length
- body row label for TERMINAL PROFILE: 'TLV data' -> 'Profile'
- tests: TERMINAL_PROFILE.md §10 regression anchors + synthetic
  declared-modem/handset vectors
- Bump 1.22.21, SW cache v52->v53
This commit is contained in:
catarrh committed 2026-09-19 01:39:34 +03:00
1 parent 2c373f50b7
commit 8441b56e99
7 files changed
+261 -6

No files matched your search

+73
View File
@@ -1006,6 +1006,79 @@ class TestPacketSummaries(unittest.TestCase):
self.assertIsNone(self._dec('010100', 'vcc').get('summary'))
class TestTerminalProfile(unittest.TestCase):
"""TERMINAL PROFILE device characteristics (regression anchors from
projects/docs/TERMINAL_PROFILE.md §10)."""
def _dec(self, profile_hex):
data = bytes.fromhex(profile_hex)
raw = '80100000%02x' % len(data) + profile_hex + '9000'
return decode_message(bytes.fromhex(raw))
def test_xiaomi_mi_a1(self):
r = self._dec('FFFFFFFF7F9F00DFFF03021FE2000000C3FB0007041178'
'00710100000038428003')
tp = r['tp']
self.assertEqual(r['body']['label'], 'Profile')
self.assertTrue(tp['bip'])
self.assertEqual(tp['bip_channels'], 7)
self.assertEqual(tp['bip_bearers'], ['GPRS'])
self.assertEqual(tp['bip_transports'],
['TCP client, remote', 'UDP client, remote'])
self.assertTrue(tp['hsdpa'])
self.assertTrue(tp['eutran'])
self.assertEqual(tp['generation'], '3G/4G')
self.assertTrue(tp['utran_ps'])
self.assertEqual(tp['standards'], '3GPP + CDMA')
self.assertEqual(tp['summary'], '3G/4G \u00b7 BIP')
self.assertEqual(r['summary'], '3G/4G \u00b7 BIP')
def test_quectel_gsm_module(self):
r = self._dec('FFFFFFFF7F1F007FFF00001F230811060700')
tp = r['tp']
self.assertTrue(tp['bip'])
self.assertEqual(tp['bip_channels'], 1)
self.assertEqual(tp['bip_bearers'], ['CSD', 'GPRS'])
self.assertNotIn('generation', tp)
self.assertEqual(tp['summary'], 'BIP')
def test_huawei_e173_underdeclared(self):
# The E173 is a 3G modem yet leaves E-UTRAN/HSDPA clear — the TP
# must not be read as "2G".
r = self._dec('2965D7E11F90001FD50000000000000000E00000')
tp = r['tp']
self.assertIs(tp['bip'], False)
self.assertNotIn('generation', tp)
self.assertEqual(tp['standards'], '3GPP')
self.assertEqual(tp['summary'], 'no BIP')
def test_nokia_7210(self):
r = self._dec('FFFFFFFF7F3F00DFFF00001FE28A0D0203290000020040')
tp = r['tp']
self.assertTrue(tp['bip'])
self.assertEqual(tp['bip_channels'], 7)
self.assertEqual(tp['screen_h'], 10)
self.assertEqual(tp['screen_w'], 13)
self.assertNotIn('device_class', tp)
self.assertEqual(tp['summary'], 'BIP')
def test_declared_headless_modem(self):
# byte 14 b6 (0x20) = no display capability (class ND).
r = self._dec('FFFFFFFF7F9F00DFFF03021F00' + '20' + '00' * 6)
tp = r['tp']
self.assertEqual(tp['device_class'], 'modem')
self.assertIn('declared', tp['class_reason'])
self.assertIn('no display', tp['class_reason'])
self.assertEqual(tp['summary'], 'modem \u00b7 BIP')
def test_handset_class(self):
r = self._dec('FFFFFFFF7F9F00DFFF03021FE208110000')
tp = r['tp']
self.assertEqual(tp['device_class'], 'handset')
self.assertIn('screen 8\u00d717', tp['class_reason'])
self.assertEqual(tp['summary'], 'handset \u00b7 BIP')
class TestSummary(unittest.TestCase):
def test_select_path_from_mf(self):
r = decode_message(bytes.fromhex('a0a40804022fe29000'))
+1 -1
View File
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
class TestVersion(unittest.TestCase):
def test_package_version(self):
from simtrace2_pysniff import __version__
self.assertEqual(__version__, '1.22.20')
self.assertEqual(__version__, '1.22.21')
def test_status_includes_version(self):
import os