diff options
Diffstat (limited to 'src/target/trx_toolkit/gsm_shared.py')
-rw-r--r-- | src/target/trx_toolkit/gsm_shared.py | 84 |
1 files changed, 75 insertions, 9 deletions
diff --git a/src/target/trx_toolkit/gsm_shared.py b/src/target/trx_toolkit/gsm_shared.py index 71f43a7f..5f59ba6f 100644 --- a/src/target/trx_toolkit/gsm_shared.py +++ b/src/target/trx_toolkit/gsm_shared.py @@ -1,10 +1,10 @@ -#!/usr/bin/env python # -*- 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 # @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from enum import Enum @@ -29,8 +25,8 @@ GSM_SUPERFRAME = 26 * 51 GSM_HYPERFRAME = 2048 * GSM_SUPERFRAME # Burst length -GSM_BURST_LEN = 148 -EDGE_BURST_LEN = GSM_BURST_LEN * 3 +GMSK_BURST_LEN = 148 +EDGE_BURST_LEN = GMSK_BURST_LEN * 3 class BurstType(Enum): """ Burst types defined in 3GPP TS 45.002 """ @@ -106,3 +102,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] |