2018-01-23 23:10:09 +00:00
|
|
|
#!/usr/bin/env python2
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2018-03-12 18:09:56 +00:00
|
|
|
# TRX Toolkit
|
2018-01-23 23:10:09 +00:00
|
|
|
# DATA interface message definitions and helpers
|
|
|
|
#
|
|
|
|
# (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
|
|
|
|
#
|
|
|
|
# All Rights Reserved
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
import random
|
2018-02-28 08:48:17 +00:00
|
|
|
import struct
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
from gsm_shared import *
|
|
|
|
|
|
|
|
class DATAMSG:
|
2019-06-22 13:18:13 +00:00
|
|
|
""" TRXD (DATA) message codec (common part).
|
|
|
|
|
|
|
|
The DATA messages are used to carry bursts in both directions
|
|
|
|
between L1 and TRX. There exist two kinds of them:
|
|
|
|
|
|
|
|
- L12TRX (L1 -> TRX) - to be transmitted bursts,
|
|
|
|
- TRX2L1 (TRX -> L1) - received bursts.
|
|
|
|
|
|
|
|
Both of them have quite similar structure, and start with
|
|
|
|
the common fixed-size message header (no TLVs):
|
|
|
|
|
|
|
|
+---------------+-----------------+------------+
|
|
|
|
| common header | specific header | burst bits |
|
|
|
|
+---------------+-----------------+------------+
|
|
|
|
|
|
|
|
while the message specific headers and bit types are different.
|
|
|
|
|
|
|
|
The common header is represented by this class, which is the
|
|
|
|
parent of both DATAMSG_L12TRX and DATAMSG_TRX2L2 (see below),
|
|
|
|
and has the following fields:
|
|
|
|
|
|
|
|
+--------------+-------------------+
|
|
|
|
| TN (1 octet) | FN (4 octets, BE) |
|
|
|
|
+--------------+-------------------+
|
|
|
|
|
|
|
|
where:
|
|
|
|
|
|
|
|
- TN is TDMA time-slot number (1 octet), and
|
|
|
|
- FN is TDMA frame number (4 octets, big endian).
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2018-02-27 18:04:51 +00:00
|
|
|
# Common constructor
|
|
|
|
def __init__(self, fn = None, tn = None, burst = None):
|
|
|
|
self.burst = burst
|
|
|
|
self.fn = fn
|
|
|
|
self.tn = tn
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Generates message specific header
|
|
|
|
def gen_hdr(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
# Parses message specific header
|
|
|
|
def parse_hdr(self, hdr):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2018-01-27 13:04:25 +00:00
|
|
|
# Generates message specific burst
|
|
|
|
def gen_burst(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
# Parses message specific burst
|
|
|
|
def parse_burst(self, burst):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2018-01-24 19:24:44 +00:00
|
|
|
# Generates a random frame number
|
|
|
|
def rand_fn(self):
|
|
|
|
return random.randint(0, GSM_HYPERFRAME)
|
|
|
|
|
|
|
|
# Generates a random timeslot number
|
|
|
|
def rand_tn(self):
|
|
|
|
return random.randint(0, 7)
|
|
|
|
|
|
|
|
# Randomizes the message header
|
|
|
|
def rand_hdr(self):
|
|
|
|
self.fn = self.rand_fn()
|
|
|
|
self.tn = self.rand_tn()
|
|
|
|
|
2018-01-27 13:44:15 +00:00
|
|
|
# Generates human-readable header description
|
|
|
|
def desc_hdr(self):
|
|
|
|
result = ""
|
|
|
|
|
|
|
|
if self.fn is not None:
|
|
|
|
result += ("fn=%u " % self.fn)
|
|
|
|
|
|
|
|
if self.tn is not None:
|
|
|
|
result += ("tn=%u " % self.tn)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
# Converts unsigned soft-bits {254..0} to soft-bits {-127..127}
|
|
|
|
def usbit2sbit(self, bits):
|
|
|
|
buf = []
|
|
|
|
|
|
|
|
for bit in bits:
|
|
|
|
if bit == 0xff:
|
|
|
|
buf.append(-127)
|
|
|
|
else:
|
|
|
|
buf.append(127 - bit)
|
|
|
|
|
|
|
|
return buf
|
|
|
|
|
|
|
|
# Converts soft-bits {-127..127} to unsigned soft-bits {254..0}
|
|
|
|
def sbit2usbit(self, bits):
|
|
|
|
buf = []
|
|
|
|
|
|
|
|
for bit in bits:
|
|
|
|
buf.append(127 - bit)
|
|
|
|
|
|
|
|
return buf
|
|
|
|
|
|
|
|
# Converts soft-bits {-127..127} to bits {1..0}
|
|
|
|
def sbit2ubit(self, bits):
|
|
|
|
buf = []
|
|
|
|
|
|
|
|
for bit in bits:
|
|
|
|
buf.append(1 if bit < 0 else 0)
|
|
|
|
|
|
|
|
return buf
|
|
|
|
|
|
|
|
# Converts bits {1..0} to soft-bits {-127..127}
|
|
|
|
def ubit2sbit(self, bits):
|
|
|
|
buf = []
|
|
|
|
|
|
|
|
for bit in bits:
|
|
|
|
buf.append(-127 if bit else 127)
|
|
|
|
|
|
|
|
return buf
|
|
|
|
|
|
|
|
# Validates the message fields
|
|
|
|
def validate(self):
|
|
|
|
if self.burst is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if len(self.burst) not in (GSM_BURST_LEN, EDGE_BURST_LEN):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if self.fn is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if self.fn < 0 or self.fn > GSM_HYPERFRAME:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if self.tn is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if self.tn < 0 or self.tn > 7:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
# Generates a TRX DATA message
|
2018-12-17 22:34:18 +00:00
|
|
|
def gen_msg(self, legacy = False):
|
2018-01-23 23:10:09 +00:00
|
|
|
# Validate all the fields
|
|
|
|
if not self.validate():
|
|
|
|
raise ValueError("Message incomplete or incorrect")
|
|
|
|
|
|
|
|
# Allocate an empty byte-array
|
|
|
|
buf = bytearray()
|
|
|
|
|
|
|
|
# Put timeslot index
|
|
|
|
buf.append(self.tn)
|
|
|
|
|
2019-06-22 15:27:27 +00:00
|
|
|
# Put frame number (4 octets, BE)
|
|
|
|
buf += struct.pack(">L", self.fn)
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Generate message specific header part
|
|
|
|
hdr = self.gen_hdr()
|
|
|
|
buf += hdr
|
|
|
|
|
2018-01-27 13:04:25 +00:00
|
|
|
# Generate burst
|
|
|
|
buf += self.gen_burst()
|
2018-01-23 23:10:09 +00:00
|
|
|
|
2018-12-17 22:34:18 +00:00
|
|
|
# This is a rudiment from (legacy) OpenBTS transceiver,
|
|
|
|
# some L1 implementations still expect two dummy bytes.
|
|
|
|
if legacy:
|
|
|
|
buf += bytearray(2)
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
return buf
|
|
|
|
|
|
|
|
# Parses a TRX DATA message
|
|
|
|
def parse_msg(self, msg):
|
|
|
|
# Calculate message length
|
|
|
|
length = len(msg)
|
|
|
|
|
|
|
|
# Check length
|
|
|
|
if length < (self.HDR_LEN + GSM_BURST_LEN):
|
|
|
|
raise ValueError("Message is to short")
|
|
|
|
|
|
|
|
# Parse both fn and tn
|
2019-06-22 15:27:27 +00:00
|
|
|
self.fn = struct.unpack(">L", msg[1:5])[0]
|
2018-01-23 23:10:09 +00:00
|
|
|
self.tn = msg[0]
|
|
|
|
|
|
|
|
# Specific message part
|
|
|
|
self.parse_hdr(msg)
|
|
|
|
|
|
|
|
# Copy burst, skipping header
|
2018-01-27 13:04:25 +00:00
|
|
|
msg_burst = msg[self.HDR_LEN:]
|
|
|
|
self.parse_burst(msg_burst)
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
class DATAMSG_L12TRX(DATAMSG):
|
2019-06-22 13:18:13 +00:00
|
|
|
""" L12TRX (L1 -> TRX) message codec.
|
|
|
|
|
|
|
|
This message represents a Downlink burst on the BTS side,
|
|
|
|
or an Uplink burst on the MS side, and has the following
|
|
|
|
message specific fixed-size header preceding the burst bits:
|
|
|
|
|
|
|
|
+-----+--------------------+
|
|
|
|
| PWR | hard-bits (1 or 0) |
|
|
|
|
+-----+--------------------+
|
|
|
|
|
|
|
|
where PWR (1 octet) is relative (to the full-scale amplitude)
|
|
|
|
transmit power level in dB. The absolute value is set on
|
|
|
|
the control interface.
|
|
|
|
|
|
|
|
Each hard-bit (1 or 0) of the burst is represented using one
|
|
|
|
byte (0x01 or 0x00 respectively).
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
# Constants
|
|
|
|
HDR_LEN = 6
|
|
|
|
PWR_MIN = 0x00
|
|
|
|
PWR_MAX = 0xff
|
|
|
|
|
|
|
|
# Specific message fields
|
|
|
|
pwr = None
|
|
|
|
|
|
|
|
# Validates the message fields
|
|
|
|
def validate(self):
|
|
|
|
# Validate common fields
|
|
|
|
if not DATAMSG.validate(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if self.pwr is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if self.pwr < self.PWR_MIN or self.pwr > self.PWR_MAX:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2018-01-24 19:24:44 +00:00
|
|
|
# Generates a random power level
|
|
|
|
def rand_pwr(self, min = None, max = None):
|
|
|
|
if min is None:
|
|
|
|
min = self.PWR_MIN
|
|
|
|
|
|
|
|
if max is None:
|
|
|
|
max = self.PWR_MAX
|
|
|
|
|
|
|
|
return random.randint(min, max)
|
|
|
|
|
|
|
|
# Randomizes message specific header
|
|
|
|
def rand_hdr(self):
|
|
|
|
DATAMSG.rand_hdr(self)
|
|
|
|
self.pwr = self.rand_pwr()
|
|
|
|
|
2018-01-27 13:44:15 +00:00
|
|
|
# Generates human-readable header description
|
|
|
|
def desc_hdr(self):
|
|
|
|
# Describe the common part
|
|
|
|
result = DATAMSG.desc_hdr(self)
|
|
|
|
|
|
|
|
if self.pwr is not None:
|
|
|
|
result += ("pwr=%u " % self.pwr)
|
|
|
|
|
|
|
|
# Strip useless whitespace and return
|
|
|
|
return result.strip()
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
# Generates message specific header part
|
|
|
|
def gen_hdr(self):
|
|
|
|
# Allocate an empty byte-array
|
|
|
|
buf = bytearray()
|
|
|
|
|
|
|
|
# Put power
|
|
|
|
buf.append(self.pwr)
|
|
|
|
|
|
|
|
return buf
|
|
|
|
|
|
|
|
# Parses message specific header part
|
|
|
|
def parse_hdr(self, hdr):
|
|
|
|
# Parse power level
|
|
|
|
self.pwr = hdr[5]
|
|
|
|
|
2018-01-27 13:04:25 +00:00
|
|
|
# Generates message specific burst
|
|
|
|
def gen_burst(self):
|
|
|
|
# Copy burst 'as is'
|
|
|
|
return bytearray(self.burst)
|
|
|
|
|
|
|
|
# Parses message specific burst
|
|
|
|
def parse_burst(self, burst):
|
|
|
|
length = len(burst)
|
|
|
|
|
|
|
|
# Distinguish between GSM and EDGE
|
|
|
|
if length >= EDGE_BURST_LEN:
|
|
|
|
self.burst = list(burst[:EDGE_BURST_LEN])
|
|
|
|
else:
|
|
|
|
self.burst = list(burst[:GSM_BURST_LEN])
|
|
|
|
|
2018-02-26 23:46:15 +00:00
|
|
|
# Transforms this message to TRX2L1 message
|
|
|
|
def gen_trx2l1(self):
|
|
|
|
# Allocate a new message
|
|
|
|
msg = DATAMSG_TRX2L1(fn = self.fn, tn = self.tn)
|
|
|
|
|
|
|
|
# Convert burst bits
|
|
|
|
if self.burst is not None:
|
|
|
|
msg.burst = self.ubit2sbit(self.burst)
|
|
|
|
|
|
|
|
return msg
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
class DATAMSG_TRX2L1(DATAMSG):
|
2019-06-22 13:18:13 +00:00
|
|
|
""" TRX2L1 (TRX -> L1) message codec.
|
|
|
|
|
|
|
|
This message represents an Uplink burst on the BTS side,
|
|
|
|
or a Downlink burst on the MS side, and has the following
|
|
|
|
message specific fixed-size header preceding the burst bits:
|
|
|
|
|
|
|
|
+------+-----+--------------------+
|
|
|
|
| RSSI | ToA | soft-bits (254..0) |
|
|
|
|
+------+-----+--------------------+
|
|
|
|
|
|
|
|
where:
|
|
|
|
|
|
|
|
- RSSI (1 octet) - Received Signal Strength Indication
|
|
|
|
encoded without the negative sign.
|
|
|
|
- ToA (2 octets) - Timing of Arrival in units of 1/256
|
|
|
|
of symbol (big endian).
|
|
|
|
|
|
|
|
Unlike to be transmitted bursts, the received bursts are designated
|
|
|
|
using the soft-bits notation, so the receiver can indicate its
|
|
|
|
assurance from 0 to -127 that a given bit is 1, and from 0 to +127
|
|
|
|
that a given bit is 0. The Viterbi algorithm allows to approximate
|
|
|
|
the original sequence of hard-bits (1 or 0) using these values.
|
|
|
|
|
|
|
|
Each soft-bit (-127..127) of the burst is encoded as an unsigned
|
|
|
|
value in range (254..0) respectively using the constant shift.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
# Constants
|
|
|
|
HDR_LEN = 8
|
2019-05-31 18:42:43 +00:00
|
|
|
|
|
|
|
# rxlev2dbm(0..63) gives us [-110..-47], plus -10 dbm for noise
|
2018-01-23 23:10:09 +00:00
|
|
|
RSSI_MIN = -120
|
2019-05-31 18:42:43 +00:00
|
|
|
RSSI_MAX = -47
|
2018-01-23 23:10:09 +00:00
|
|
|
|
2019-04-23 15:36:49 +00:00
|
|
|
# Min and max values of int16_t
|
|
|
|
TOA256_MIN = -32768
|
|
|
|
TOA256_MAX = 32767
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Specific message fields
|
|
|
|
rssi = None
|
2018-02-28 08:38:27 +00:00
|
|
|
toa256 = None
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Validates the message fields
|
|
|
|
def validate(self):
|
|
|
|
# Validate common fields
|
|
|
|
if not DATAMSG.validate(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if self.rssi is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if self.rssi < self.RSSI_MIN or self.rssi > self.RSSI_MAX:
|
|
|
|
return False
|
|
|
|
|
2018-02-28 08:38:27 +00:00
|
|
|
if self.toa256 is None:
|
2018-01-23 23:10:09 +00:00
|
|
|
return False
|
|
|
|
|
2018-02-28 08:38:27 +00:00
|
|
|
if self.toa256 < self.TOA256_MIN or self.toa256 > self.TOA256_MAX:
|
2018-01-23 23:10:09 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2018-01-24 19:24:44 +00:00
|
|
|
# Generates a random RSSI value
|
|
|
|
def rand_rssi(self, min = None, max = None):
|
|
|
|
if min is None:
|
|
|
|
min = self.RSSI_MIN
|
|
|
|
|
|
|
|
if max is None:
|
|
|
|
max = self.RSSI_MAX
|
|
|
|
|
|
|
|
return random.randint(min, max)
|
|
|
|
|
|
|
|
# Generates a ToA (Time of Arrival) value
|
2018-02-28 08:38:27 +00:00
|
|
|
def rand_toa256(self, min = None, max = None):
|
2018-01-24 19:24:44 +00:00
|
|
|
if min is None:
|
2018-02-28 08:38:27 +00:00
|
|
|
min = self.TOA256_MIN
|
2018-01-24 19:24:44 +00:00
|
|
|
|
|
|
|
if max is None:
|
2018-02-28 08:38:27 +00:00
|
|
|
max = self.TOA256_MAX
|
2018-01-24 19:24:44 +00:00
|
|
|
|
2018-02-28 08:38:27 +00:00
|
|
|
return random.randint(min, max)
|
2018-01-24 19:24:44 +00:00
|
|
|
|
|
|
|
# Randomizes message specific header
|
|
|
|
def rand_hdr(self):
|
|
|
|
DATAMSG.rand_hdr(self)
|
|
|
|
self.rssi = self.rand_rssi()
|
2018-02-28 08:38:27 +00:00
|
|
|
self.toa256 = self.rand_toa256()
|
2018-01-24 19:24:44 +00:00
|
|
|
|
2018-01-27 13:44:15 +00:00
|
|
|
# Generates human-readable header description
|
|
|
|
def desc_hdr(self):
|
|
|
|
# Describe the common part
|
|
|
|
result = DATAMSG.desc_hdr(self)
|
|
|
|
|
|
|
|
if self.rssi is not None:
|
|
|
|
result += ("rssi=%d " % self.rssi)
|
|
|
|
|
2018-02-28 08:38:27 +00:00
|
|
|
if self.toa256 is not None:
|
|
|
|
result += ("toa256=%d " % self.toa256)
|
2018-01-27 13:44:15 +00:00
|
|
|
|
|
|
|
# Strip useless whitespace and return
|
|
|
|
return result.strip()
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
# Generates message specific header part
|
|
|
|
def gen_hdr(self):
|
|
|
|
# Allocate an empty byte-array
|
|
|
|
buf = bytearray()
|
|
|
|
|
|
|
|
# Put RSSI
|
|
|
|
buf.append(-self.rssi)
|
|
|
|
|
2018-02-28 08:38:27 +00:00
|
|
|
# Encode ToA (Time of Arrival)
|
|
|
|
# Big endian, 2 bytes (int32_t)
|
2018-12-16 01:39:44 +00:00
|
|
|
buf += struct.pack(">h", self.toa256)
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
return buf
|
|
|
|
|
|
|
|
# Parses message specific header part
|
|
|
|
def parse_hdr(self, hdr):
|
|
|
|
# Parse RSSI
|
|
|
|
self.rssi = -(hdr[5])
|
|
|
|
|
|
|
|
# Parse ToA (Time of Arrival)
|
2018-02-28 08:48:17 +00:00
|
|
|
self.toa256 = struct.unpack(">h", hdr[6:8])[0]
|
2018-01-23 23:10:09 +00:00
|
|
|
|
2018-01-27 13:04:25 +00:00
|
|
|
# Generates message specific burst
|
|
|
|
def gen_burst(self):
|
|
|
|
# Convert soft-bits to unsigned soft-bits
|
|
|
|
burst_usbits = self.sbit2usbit(self.burst)
|
|
|
|
|
|
|
|
# Encode to bytes
|
|
|
|
return bytearray(burst_usbits)
|
|
|
|
|
|
|
|
# Parses message specific burst
|
|
|
|
def parse_burst(self, burst):
|
|
|
|
length = len(burst)
|
|
|
|
|
|
|
|
# Distinguish between GSM and EDGE
|
|
|
|
if length >= EDGE_BURST_LEN:
|
|
|
|
burst_usbits = list(burst[:EDGE_BURST_LEN])
|
|
|
|
else:
|
|
|
|
burst_usbits = list(burst[:GSM_BURST_LEN])
|
|
|
|
|
|
|
|
# Convert unsigned soft-bits to soft-bits
|
|
|
|
burst_sbits = self.usbit2sbit(burst_usbits)
|
|
|
|
|
|
|
|
# Save
|
|
|
|
self.burst = burst_sbits
|
|
|
|
|
2018-02-26 23:46:15 +00:00
|
|
|
# Transforms this message to L12TRX message
|
|
|
|
def gen_l12trx(self):
|
|
|
|
# Allocate a new message
|
|
|
|
msg = DATAMSG_L12TRX(fn = self.fn, tn = self.tn)
|
|
|
|
|
|
|
|
# Convert burst bits
|
|
|
|
if self.burst is not None:
|
|
|
|
msg.burst = self.sbit2ubit(self.burst)
|
|
|
|
|
|
|
|
return msg
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
# Regression test
|
|
|
|
if __name__ == '__main__':
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
import logging as log
|
|
|
|
|
|
|
|
# Configure logging
|
|
|
|
log.basicConfig(level = log.DEBUG,
|
|
|
|
format = "[%(levelname)s] %(filename)s:%(lineno)d %(message)s")
|
|
|
|
|
2018-01-27 13:04:25 +00:00
|
|
|
# Generate two random bursts
|
|
|
|
burst_l12trx_ref = []
|
|
|
|
burst_trx2l1_ref = []
|
|
|
|
|
2018-01-23 23:10:09 +00:00
|
|
|
for i in range(0, GSM_BURST_LEN):
|
2018-01-27 13:04:25 +00:00
|
|
|
ubit = random.randint(0, 1)
|
|
|
|
burst_l12trx_ref.append(ubit)
|
|
|
|
|
|
|
|
sbit = random.randint(-127, 127)
|
|
|
|
burst_trx2l1_ref.append(sbit)
|
2018-01-23 23:10:09 +00:00
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Generating the reference messages")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Create messages of both types
|
2019-06-22 16:27:23 +00:00
|
|
|
msg_l12trx_ref = DATAMSG_L12TRX(burst = burst_l12trx_ref)
|
|
|
|
msg_trx2l1_ref = DATAMSG_TRX2L1(burst = burst_trx2l1_ref)
|
|
|
|
|
|
|
|
# Validate header randomization
|
|
|
|
for i in range(0, 100):
|
|
|
|
msg_l12trx_ref.rand_hdr()
|
|
|
|
msg_trx2l1_ref.rand_hdr()
|
2018-01-23 23:10:09 +00:00
|
|
|
|
2019-06-22 16:27:23 +00:00
|
|
|
assert(msg_l12trx_ref.validate())
|
|
|
|
assert(msg_trx2l1_ref.validate())
|
2018-01-23 23:10:09 +00:00
|
|
|
|
2019-06-22 16:27:23 +00:00
|
|
|
log.info("Validate header randomization: OK")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Encoding the reference messages")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Encode DATA messages
|
|
|
|
l12trx_raw = msg_l12trx_ref.gen_msg()
|
|
|
|
trx2l1_raw = msg_trx2l1_ref.gen_msg()
|
|
|
|
|
2018-12-17 22:34:18 +00:00
|
|
|
# Encode a TRX2L1 message in legacy mode
|
|
|
|
trx2l1_raw_legacy = msg_trx2l1_ref.gen_msg(legacy = True)
|
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Parsing generated messages back")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Parse generated DATA messages
|
|
|
|
msg_l12trx_dec = DATAMSG_L12TRX()
|
|
|
|
msg_trx2l1_dec = DATAMSG_TRX2L1()
|
|
|
|
msg_l12trx_dec.parse_msg(l12trx_raw)
|
|
|
|
msg_trx2l1_dec.parse_msg(trx2l1_raw)
|
|
|
|
|
2018-12-17 22:34:18 +00:00
|
|
|
# Parse generated TRX2L1 message in legacy mode
|
|
|
|
msg_trx2l1_legacy_dec = DATAMSG_TRX2L1()
|
|
|
|
msg_trx2l1_legacy_dec.parse_msg(trx2l1_raw_legacy)
|
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Comparing decoded messages with the reference")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Compare bursts
|
2018-01-27 13:04:25 +00:00
|
|
|
assert(msg_l12trx_dec.burst == burst_l12trx_ref)
|
|
|
|
assert(msg_trx2l1_dec.burst == burst_trx2l1_ref)
|
2018-12-17 22:34:18 +00:00
|
|
|
assert(msg_trx2l1_legacy_dec.burst == burst_trx2l1_ref)
|
2018-01-23 23:10:09 +00:00
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Compare bursts: OK")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Compare both parsed messages with the reference data
|
2019-06-22 16:27:23 +00:00
|
|
|
assert(msg_l12trx_dec.fn == msg_l12trx_ref.fn)
|
|
|
|
assert(msg_trx2l1_dec.fn == msg_trx2l1_ref.fn)
|
|
|
|
assert(msg_l12trx_dec.tn == msg_l12trx_ref.tn)
|
|
|
|
assert(msg_trx2l1_dec.tn == msg_trx2l1_ref.tn)
|
2018-01-23 23:10:09 +00:00
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Compare FN / TN: OK")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Compare message specific parts
|
|
|
|
assert(msg_trx2l1_dec.rssi == msg_trx2l1_ref.rssi)
|
|
|
|
assert(msg_l12trx_dec.pwr == msg_l12trx_ref.pwr)
|
2018-02-28 08:48:17 +00:00
|
|
|
assert(msg_trx2l1_dec.toa256 == msg_trx2l1_ref.toa256)
|
2018-01-23 23:10:09 +00:00
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Compare message specific data: OK")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Bit conversation test
|
2018-02-27 18:17:31 +00:00
|
|
|
usbits_ref = list(range(0, 256))
|
|
|
|
sbits_ref = list(range(-127, 128))
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Test both usbit2sbit() and sbit2usbit()
|
|
|
|
sbits = msg_trx2l1_ref.usbit2sbit(usbits_ref)
|
|
|
|
usbits = msg_trx2l1_ref.sbit2usbit(sbits)
|
|
|
|
assert(usbits[:255] == usbits_ref[:255])
|
|
|
|
assert(usbits[255] == 254)
|
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Check both usbit2sbit() and sbit2usbit(): OK")
|
2018-01-23 23:10:09 +00:00
|
|
|
|
|
|
|
# Test both sbit2ubit() and ubit2sbit()
|
|
|
|
ubits = msg_trx2l1_ref.sbit2ubit(sbits_ref)
|
|
|
|
assert(ubits == ([1] * 127 + [0] * 128))
|
|
|
|
|
|
|
|
sbits = msg_trx2l1_ref.ubit2sbit(ubits)
|
|
|
|
assert(sbits == ([-127] * 127 + [127] * 128))
|
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Check both sbit2ubit() and ubit2sbit(): OK")
|
2018-02-26 23:46:15 +00:00
|
|
|
|
|
|
|
# Test message transformation
|
|
|
|
msg_l12trx_dec = msg_trx2l1_ref.gen_l12trx()
|
|
|
|
msg_trx2l1_dec = msg_l12trx_ref.gen_trx2l1()
|
|
|
|
|
|
|
|
assert(msg_l12trx_dec.fn == msg_trx2l1_ref.fn)
|
|
|
|
assert(msg_l12trx_dec.tn == msg_trx2l1_ref.tn)
|
|
|
|
|
|
|
|
assert(msg_trx2l1_dec.fn == msg_l12trx_ref.fn)
|
|
|
|
assert(msg_trx2l1_dec.tn == msg_l12trx_ref.tn)
|
|
|
|
|
|
|
|
assert(msg_l12trx_dec.burst == msg_l12trx_dec.sbit2ubit(burst_trx2l1_ref))
|
|
|
|
assert(msg_trx2l1_dec.burst == msg_trx2l1_dec.ubit2sbit(burst_l12trx_ref))
|
|
|
|
|
trx_toolkit: use generic logging module instead of print()
There are multiple advantages of using Python's logging module:
- advanced message formatting (file name, line number, etc.),
- multiple logging targets (e.g. stderr, file, socket),
- logging levels (e.g. DEBUG, INFO, ERROR),
- the pythonic way ;)
so, let's replace multiple print() calls by logging calls,
add use the following logging message format by default:
[%(levelname)s] %(filename)s:%(lineno)d %(message)s
Examples:
[INFO] ctrl_if_bts.py:57 Starting transceiver...
[DEBUG] clck_gen.py:87 IND CLOCK 26826
[DEBUG] ctrl_if_bts.py:71 Recv POWEROFF cmd
[INFO] ctrl_if_bts.py:73 Stopping transceiver...
[INFO] fake_trx.py:127 Shutting down...
Please note that there is no way to filter messages by logging
level yet. This is to be introduced soon, together with argparse.
Change-Id: I7fcafabafe8323b58990997a47afdd48b6d1f357
2018-12-06 22:00:26 +00:00
|
|
|
log.info("Check L12TRX <-> TRX2L1 type transformations: OK")
|