summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Yanitskiy <vyanitskiy@sysmocom.de>2020-05-14 14:12:41 +0700
committerVadim Yanitskiy <axilirator@gmail.com>2020-05-17 07:26:07 +0000
commit220e60184ffccbebf650d07d2aefd5ab4d375cce (patch)
tree7f6afa4709fa07141d64111cef8d7ff241bf321e
parent2db781a5d77e1c4bc3ae8517e7ba7946ac71a1a7 (diff)
trx_toolkit/gsm_shared.py: implement hopping sequence generation
Based on firmware/layer1/rfch.c:rfch_hop_seq_gen() by Sylvain Munaut. Change-Id: I9ecabfef6f5a4e4180956c6a019c386ccb1c9acd
-rw-r--r--src/target/trx_toolkit/gsm_shared.py75
1 files changed, 73 insertions, 2 deletions
diff --git a/src/target/trx_toolkit/gsm_shared.py b/src/target/trx_toolkit/gsm_shared.py
index 71f43a7f..c8c6b8fe 100644
--- a/src/target/trx_toolkit/gsm_shared.py
+++ b/src/target/trx_toolkit/gsm_shared.py
@@ -2,9 +2,10 @@
# -*- coding: utf-8 -*-
# TRX Toolkit
-# Common GSM constants
+# Common GSM constants and helpers
#
-# (C) 2018-2019 by Vadim Yanitskiy <axilirator@gmail.com>
+# (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
+# Contributions by sysmocom - s.f.m.c. GmbH
#
# All Rights Reserved
#
@@ -106,3 +107,73 @@ class TrainingSeqGMSK(Enum):
return ts
return None
+
+class HoppingParams:
+ """ Hopping sequence generation as per 3GPP TS 45.002, section 6.2.3.
+
+ Based on firmware/layer1/rfch.c:rfch_hop_seq_gen() by Sylvain Munaut.
+
+ """
+
+ # Magic numbers for pseudo-random hopping sequence generation
+ RNTABLE = [
+ 48, 98, 63, 1, 36, 95, 78, 102, 94, 73,
+ 0, 64, 25, 81, 76, 59, 124, 23, 104, 100,
+ 101, 47, 118, 85, 18, 56, 96, 86, 54, 2,
+ 80, 34, 127, 13, 6, 89, 57, 103, 12, 74,
+ 55, 111, 75, 38, 109, 71, 112, 29, 11, 88,
+ 87, 19, 3, 68, 110, 26, 33, 31, 8, 45,
+ 82, 58, 40, 107, 32, 5, 106, 92, 62, 67,
+ 77, 108, 122, 37, 60, 66, 121, 42, 51, 126,
+ 117, 114, 4, 90, 43, 52, 53, 113, 120, 72,
+ 16, 49, 7, 79, 119, 61, 22, 84, 9, 97,
+ 91, 15, 21, 24, 46, 39, 93, 105, 65, 70,
+ 125, 99, 17, 123,
+ ]
+
+ def __init__(self, hsn, maio, ma):
+ # Make sure MA is not empty
+ ma_len = len(ma)
+ if ma_len == 0: # TODO: or rather > 1?
+ raise ValueError("Mobile Allocation is empty")
+
+ self.hsn = hsn
+ self.maio = maio
+ self.ma = ma
+
+ # Pre-calculate 2 ** NBIN in advance
+ self._pnm = (ma_len >> 0) | (ma_len >> 1) \
+ | (ma_len >> 2) | (ma_len >> 3) \
+ | (ma_len >> 4) | (ma_len >> 5) \
+ | (ma_len >> 6)
+
+ def __str__(self):
+ fmt = "hsn=%u, maio=%u, ma_len=%u"
+ return fmt % (self.hsn, self.maio, len(self.ma))
+
+ @staticmethod
+ def fn2gsm_time(fn):
+ t1 = fn // (26 * 51)
+ t2 = fn % 26
+ t3 = fn % 51
+ tc = (fn // 51) % 8
+ return (t1, t2, t3, tc)
+
+ # Resolve current ARFCN using the given TDMA frame number
+ def resolve(self, fn):
+ # Cyclic hopping
+ if self.hsn == 0:
+ mai = (fn + self.maio) % len(self.ma)
+ return self.ma[mai]
+
+ # Pseudo random hopping
+ (t1, t2, t3, tc) = self.fn2gsm_time(fn)
+ ma_len = len(self.ma)
+
+ rn_idx = (self.hsn ^ (t1 & 63)) + t3
+ m = t2 + self.RNTABLE[rn_idx]
+ mp = m & self._pnm
+
+ s = mp if mp < ma_len else (mp + t3 & self._pnm) % ma_len
+ mai = (s + self.maio) % ma_len
+ return self.ma[mai]