<p>fixeria has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmocom-bb/+/14579">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">trx_toolkit/data_msg.py: implement header version 0x01 coding<br><br>The new version mostly adds new fields to the TRX2L1 message:<br><br> +------+-----+-----+-----+-----+--------------------+<br> | RSSI | ToA | MTS | C/I | L16 | soft-bits (254..0) |<br> +------+-----+-----+-----+-----+--------------------+<br><br> where:<br><br> - MTS (1 octet) - Modulation and Training Sequence info,<br> - C/I (2 octets) - Carrier-to-Interference ratio (big endian),<br> - L16 (2 octets) - length of burst (big endian).<br><br>In case of L12TRX, the burst bits are now also encoded as L16V:<br><br> +-----+-----+--------------------+<br> | PWR | L16 | hard-bits (1 or 0) |<br> +-----+-----+--------------------+<br><br>== Coding of MTS: Modulation and Training Sequence info<br><br>3GPP TS 45.002 version 15.1.0 defines several modulation types,<br>and a few sets of training sequences for each type. The most<br>common are GMSK and 8-PSK (which is used in EDGE).<br><br> +-----------------+---------------------------------------+<br> | 7 6 5 4 3 2 1 0 | bit numbers (value range) |<br> +-----------------+---------------------------------------+<br> | . . . . . X X X | Training Sequence Code (0..7) |<br> +-----------------+---------------------------------------+<br> | . X X X X . . . | Modulation, TS set number (see below) |<br> +-----------------+---------------------------------------+<br> | X . . . . . . . | IDLE / nope frame indication (0 or 1) |<br> +-----------------+---------------------------------------+<br><br>The bit number 7 (MSB) is set to high when either nothing has been<br>detected, or during IDLE frames, so we can deliver noise levels,<br>and avoid clock gaps on the L1 side. Other bits are ignored,<br>and should be set to low (0) in this case. L16 shall be set to 0x00.<br><br>== Coding of modulation and TS set number<br><br>GMSK has 4 sets of training sequences (see tables 5.2.3a-d),<br>while 8-PSK (see tables 5.2.3f-g) and the others have 2 sets.<br>Access and Synchronization bursts also have several synch.<br>sequences.<br><br> +-----------------+---------------------------------------+<br> | 7 6 5 4 3 2 1 0 | bit numbers (value range) |<br> +-----------------+---------------------------------------+<br> | . 0 0 X X . . . | GMSK, 4 TS sets (0..3) |<br> +-----------------+---------------------------------------+<br> | . 0 1 0 X . . . | 8-PSK, 2 TS sets (0..1) |<br> +-----------------+---------------------------------------+<br> | . 0 1 1 X . . . | AQPSK, 2 TS sets (0..1) |<br> +-----------------+---------------------------------------+<br> | . 1 0 0 X . . . | 16QAM, 2 TS sets (0..1) |<br> +-----------------+---------------------------------------+<br> | . 1 0 1 X . . . | 32QAM, 2 TS sets (0..1) |<br> +-----------------+---------------------------------------+<br> | . 1 1 1 X . . . | RESERVED (0) |<br> +-----------------+---------------------------------------+<br><br>== C/I: Carrier-to-Interference ratio<br><br>The C/I value is computed from the training sequence of each burst,<br>where we can compare the "ideal" training sequence with the actual<br>training sequence and then express that in dB.<br><br>Change-Id: Ie810c5a482d1c908994e8cdd32a2ea641ae7cedd<br>Related: OS#4006<br>---<br>M src/target/trx_toolkit/data_msg.py<br>1 file changed, 289 insertions(+), 32 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/79/14579/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/target/trx_toolkit/data_msg.py b/src/target/trx_toolkit/data_msg.py</span><br><span>index 8a114f7..8d52764 100644</span><br><span>--- a/src/target/trx_toolkit/data_msg.py</span><br><span>+++ b/src/target/trx_toolkit/data_msg.py</span><br><span>@@ -25,8 +25,17 @@</span><br><span> import random</span><br><span> import struct</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+from enum import IntEnum</span><br><span> from gsm_shared import *</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+class Modulation(IntEnum):</span><br><span style="color: hsl(120, 100%, 40%);">+ """ Modulation types defined in 3GPP TS 45.002 """</span><br><span style="color: hsl(120, 100%, 40%);">+ ModGMSK = 0b0000</span><br><span style="color: hsl(120, 100%, 40%);">+ Mod8PSK = 0b0100</span><br><span style="color: hsl(120, 100%, 40%);">+ ModAQPSK = 0b0110</span><br><span style="color: hsl(120, 100%, 40%);">+ Mod16QAM = 0b1000</span><br><span style="color: hsl(120, 100%, 40%);">+ Mod32QAM = 0b1010</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> class DATAMSG:</span><br><span> """ TRXD (DATA) message codec (common part).</span><br><span> </span><br><span>@@ -45,6 +54,12 @@</span><br><span> </span><br><span> while the message specific headers and bit types are different.</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ NOTE: since version 0x01, the burst bits are encoded as L16V:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ +--------------------+------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | L16 (2 octets, BE) | burst bits |</span><br><span style="color: hsl(120, 100%, 40%);">+ +--------------------+------------+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> The common header is represented by this class, which is the</span><br><span> parent of both DATAMSG_L12TRX and DATAMSG_TRX2L2 (see below),</span><br><span> and has the following fields:</span><br><span>@@ -99,6 +114,12 @@</span><br><span> self.fn = fn</span><br><span> self.tn = tn</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # The common header length</span><br><span style="color: hsl(120, 100%, 40%);">+ @property</span><br><span style="color: hsl(120, 100%, 40%);">+ def CHDR_LEN(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ # (VER + TN) + FN</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1 + 4</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Generates message specific header</span><br><span> def gen_hdr(self):</span><br><span> raise NotImplementedError</span><br><span>@@ -227,7 +248,13 @@</span><br><span> buf += hdr</span><br><span> </span><br><span> # Generate burst</span><br><span style="color: hsl(0, 100%, 40%);">- buf += self.gen_burst()</span><br><span style="color: hsl(120, 100%, 40%);">+ burst = self.gen_burst()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Starting from version 0x01, the burst bits are</span><br><span style="color: hsl(120, 100%, 40%);">+ # encoded as L16V (length is 2 octets, big endian).</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ver >= 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ buf += struct.pack(">H", len(burst))</span><br><span style="color: hsl(120, 100%, 40%);">+ buf += burst</span><br><span> </span><br><span> # This is a rudiment from (legacy) OpenBTS transceiver,</span><br><span> # some L1 implementations still expect two dummy bytes.</span><br><span>@@ -241,8 +268,8 @@</span><br><span> # Calculate message length</span><br><span> length = len(msg)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Check length</span><br><span style="color: hsl(0, 100%, 40%);">- if length < (self.HDR_LEN + GSM_BURST_LEN):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Make sure we have at least header</span><br><span style="color: hsl(120, 100%, 40%);">+ if length < self.HDR_LEN:</span><br><span> raise ValueError("Message is to short")</span><br><span> </span><br><span> # Parse version and TDMA TN</span><br><span>@@ -255,9 +282,38 @@</span><br><span> # Specific message part</span><br><span> self.parse_hdr(msg)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Copy burst, skipping header</span><br><span style="color: hsl(0, 100%, 40%);">- msg_burst = msg[self.HDR_LEN:]</span><br><span style="color: hsl(0, 100%, 40%);">- self.parse_burst(msg_burst)</span><br><span style="color: hsl(120, 100%, 40%);">+ # We're done with the header now</span><br><span style="color: hsl(120, 100%, 40%);">+ msg = msg[self.HDR_LEN:]</span><br><span style="color: hsl(120, 100%, 40%);">+ length -= self.HDR_LEN</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Verify the burst length</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ver == 0x00:</span><br><span style="color: hsl(120, 100%, 40%);">+ # For version 0x00, we can either have as much as</span><br><span style="color: hsl(120, 100%, 40%);">+ # a GSM burst length, or an EDGE burst length.</span><br><span style="color: hsl(120, 100%, 40%);">+ if length in (GSM_BURST_LEN, EDGE_BURST_LEN):</span><br><span style="color: hsl(120, 100%, 40%);">+ pass</span><br><span style="color: hsl(120, 100%, 40%);">+ # Some ancient transceivers append 2 dummy octets</span><br><span style="color: hsl(120, 100%, 40%);">+ elif (length - 2) in (GSM_BURST_LEN, EDGE_BURST_LEN):</span><br><span style="color: hsl(120, 100%, 40%);">+ msg = msg[:-2]</span><br><span style="color: hsl(120, 100%, 40%);">+ length -= 2</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError("Odd burst length")</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.ver >= 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Starting from version 0x01, the burst bits are</span><br><span style="color: hsl(120, 100%, 40%);">+ # encoded as L16V (length is 2 octets, big endian).</span><br><span style="color: hsl(120, 100%, 40%);">+ if length < 2:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError("Missing burst LV")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ burst_len = struct.unpack(">H", msg[:2])[0]</span><br><span style="color: hsl(120, 100%, 40%);">+ if burst_len != (length - 2):</span><br><span style="color: hsl(120, 100%, 40%);">+ raise ValueError("Odd burst length")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # We're done with L16 header</span><br><span style="color: hsl(120, 100%, 40%);">+ msg = msg[2:]</span><br><span style="color: hsl(120, 100%, 40%);">+ length -= 2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parse burst</span><br><span style="color: hsl(120, 100%, 40%);">+ self.parse_burst(msg)</span><br><span> </span><br><span> class DATAMSG_L12TRX(DATAMSG):</span><br><span> """ L12TRX (L1 -> TRX) message codec.</span><br><span>@@ -266,13 +322,24 @@</span><br><span> or an Uplink burst on the MS side, and has the following</span><br><span> message specific fixed-size header preceding the burst bits:</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ == Version 0x00</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> +-----+--------------------+</span><br><span> | PWR | hard-bits (1 or 0) |</span><br><span> +-----+--------------------+</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- where PWR (1 octet) is relative (to the full-scale amplitude)</span><br><span style="color: hsl(0, 100%, 40%);">- transmit power level in dB. The absolute value is set on</span><br><span style="color: hsl(0, 100%, 40%);">- the control interface.</span><br><span style="color: hsl(120, 100%, 40%);">+ == Version 0x01</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----+-----+--------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | PWR | L16 | hard-bits (1 or 0) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----+-----+--------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ where:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ - PWR (1 octet) - relative (to the full-scale amplitude) transmit</span><br><span style="color: hsl(120, 100%, 40%);">+ power level in dB. The absolute value is set</span><br><span style="color: hsl(120, 100%, 40%);">+ on the control interface.</span><br><span style="color: hsl(120, 100%, 40%);">+ - L16 (2 octets) - length of burst (big endian).</span><br><span> </span><br><span> Each hard-bit (1 or 0) of the burst is represented using one</span><br><span> byte (0x01 or 0x00 respectively).</span><br><span>@@ -280,13 +347,26 @@</span><br><span> """</span><br><span> </span><br><span> # Constants</span><br><span style="color: hsl(0, 100%, 40%);">- HDR_LEN = 6</span><br><span> PWR_MIN = 0x00</span><br><span> PWR_MAX = 0xff</span><br><span> </span><br><span> # Specific message fields</span><br><span> pwr = None</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # Calculates header length depending on its version</span><br><span style="color: hsl(120, 100%, 40%);">+ @property</span><br><span style="color: hsl(120, 100%, 40%);">+ def HDR_LEN(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Common header length</span><br><span style="color: hsl(120, 100%, 40%);">+ length = self.CHDR_LEN</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Message specific header length</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ver in (0x00, 0x01):</span><br><span style="color: hsl(120, 100%, 40%);">+ length += 1 # PWR</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise IndexError("Unhandled version %u" % self.ver)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return length</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Validates the message fields</span><br><span> def validate(self):</span><br><span> # Validate common fields</span><br><span>@@ -349,13 +429,8 @@</span><br><span> </span><br><span> # Parses message specific burst</span><br><span> def parse_burst(self, burst):</span><br><span style="color: hsl(0, 100%, 40%);">- length = len(burst)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Distinguish between GSM and EDGE</span><br><span style="color: hsl(0, 100%, 40%);">- if length >= EDGE_BURST_LEN:</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst = list(burst[:EDGE_BURST_LEN])</span><br><span style="color: hsl(0, 100%, 40%);">- else:</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst = list(burst[:GSM_BURST_LEN])</span><br><span style="color: hsl(120, 100%, 40%);">+ # Nothing to do, we already have hard-bits</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst = list(burst)</span><br><span> </span><br><span> # Transforms this message to TRX2L1 message</span><br><span> def gen_trx2l1(self, ver = None):</span><br><span>@@ -376,16 +451,79 @@</span><br><span> or a Downlink burst on the MS side, and has the following</span><br><span> message specific fixed-size header preceding the burst bits:</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ == Version 0x00</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> +------+-----+--------------------+</span><br><span> | RSSI | ToA | soft-bits (254..0) |</span><br><span> +------+-----+--------------------+</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ == Version 0x01</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ +------+-----+-----+-----+-----+--------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | RSSI | ToA | MTS | C/I | L16 | soft-bits (254..0) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +------+-----+-----+-----+-----+--------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> where:</span><br><span> </span><br><span> - RSSI (1 octet) - Received Signal Strength Indication</span><br><span> encoded without the negative sign.</span><br><span> - ToA (2 octets) - Timing of Arrival in units of 1/256</span><br><span> of symbol (big endian).</span><br><span style="color: hsl(120, 100%, 40%);">+ - MTS (1 octet) - Modulation and Training Sequence info.</span><br><span style="color: hsl(120, 100%, 40%);">+ - C/I (2 octets) - Carrier-to-Interference ratio (big endian).</span><br><span style="color: hsl(120, 100%, 40%);">+ - L16 (2 octets) - length of burst (big endian).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Coding of TS: Training Sequence and modulation</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ 3GPP TS 45.002 version 15.1.0 defines several modulation types,</span><br><span style="color: hsl(120, 100%, 40%);">+ and a few sets of training sequences for each type. The most</span><br><span style="color: hsl(120, 100%, 40%);">+ common are GMSK and 8-PSK (which is used in EDGE).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | 7 6 5 4 3 2 1 0 | bit numbers (value range) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | . . . . . X X X | Training Sequence Code (0..7) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | . X X X X . . . | Modulation, TS set number (see below) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | X . . . . . . . | IDLE / nope frame indication (0 or 1) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The bit number 7 (MSB) is set to high when either nothing has been</span><br><span style="color: hsl(120, 100%, 40%);">+ detected, or during IDLE frames, so we can deliver noise levels,</span><br><span style="color: hsl(120, 100%, 40%);">+ and avoid clock gaps on the L1 side. Other bits are ignored,</span><br><span style="color: hsl(120, 100%, 40%);">+ and should be set to low (0) in this case. L16 shall be set to 0x00.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Coding of modulation and TS set number</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ GMSK has 4 sets of training sequences (see tables 5.2.3a-d),</span><br><span style="color: hsl(120, 100%, 40%);">+ while 8-PSK (see tables 5.2.3f-g) and the others have 2 sets.</span><br><span style="color: hsl(120, 100%, 40%);">+ Access and Synchronization bursts also have several synch.</span><br><span style="color: hsl(120, 100%, 40%);">+ sequences.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | 7 6 5 4 3 2 1 0 | bit numbers (value range) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | . 0 0 X X . . . | GMSK, 4 TS sets (0..3) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | . 0 1 0 X . . . | 8-PSK, 2 TS sets (0..1) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | . 0 1 1 X . . . | AQPSK, 2 TS sets (0..1) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | . 1 0 0 X . . . | 16QAM, 2 TS sets (0..1) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | . 1 0 1 X . . . | 32QAM, 2 TS sets (0..1) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ | . 1 1 1 X . . . | RESERVED (0) |</span><br><span style="color: hsl(120, 100%, 40%);">+ +-----------------+---------------------------------------+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == C/I: Carrier-to-Interference ratio</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The C/I value can be computed from the training sequence of each</span><br><span style="color: hsl(120, 100%, 40%);">+ burst, where we can compare the "ideal" training sequence with</span><br><span style="color: hsl(120, 100%, 40%);">+ the actual training sequence and then express that in dB.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ == Coding of the burst bits</span><br><span> </span><br><span> Unlike to be transmitted bursts, the received bursts are designated</span><br><span> using the soft-bits notation, so the receiver can indicate its</span><br><span>@@ -398,9 +536,6 @@</span><br><span> </span><br><span> """</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- # Constants</span><br><span style="color: hsl(0, 100%, 40%);">- HDR_LEN = 8</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> # rxlev2dbm(0..63) gives us [-110..-47], plus -10 dbm for noise</span><br><span> RSSI_MIN = -120</span><br><span> RSSI_MAX = -47</span><br><span>@@ -409,10 +544,42 @@</span><br><span> TOA256_MIN = -32768</span><br><span> TOA256_MAX = 32767</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # TSC (Training Sequence Code) range</span><br><span style="color: hsl(120, 100%, 40%);">+ TSC_RANGE = range(0, 8)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # IDLE frame / nope detection indicator</span><br><span style="color: hsl(120, 100%, 40%);">+ NOPE_IND = (1 << 7)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Specific message fields</span><br><span> rssi = None</span><br><span> toa256 = None</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # Version 0x01 specific (default values)</span><br><span style="color: hsl(120, 100%, 40%);">+ mod_type = Modulation.ModGMSK</span><br><span style="color: hsl(120, 100%, 40%);">+ nope_ind = False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ tsc_set = None</span><br><span style="color: hsl(120, 100%, 40%);">+ tsc = None</span><br><span style="color: hsl(120, 100%, 40%);">+ ci = None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Calculates header length depending on its version</span><br><span style="color: hsl(120, 100%, 40%);">+ @property</span><br><span style="color: hsl(120, 100%, 40%);">+ def HDR_LEN(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ # Common header length</span><br><span style="color: hsl(120, 100%, 40%);">+ length = self.CHDR_LEN</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Message specific header length</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ver == 0x00:</span><br><span style="color: hsl(120, 100%, 40%);">+ # RSSI + ToA</span><br><span style="color: hsl(120, 100%, 40%);">+ length += 1 + 2</span><br><span style="color: hsl(120, 100%, 40%);">+ elif self.ver == 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ # RSSI + ToA + TS + C/I</span><br><span style="color: hsl(120, 100%, 40%);">+ length += 1 + 2 + 1 + 2</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise IndexError("Unhandled version %u" % self.ver)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return length</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Validates the message fields</span><br><span> def validate(self):</span><br><span> # Validate common fields</span><br><span>@@ -459,6 +626,17 @@</span><br><span> self.rssi = self.rand_rssi()</span><br><span> self.toa256 = self.rand_toa256()</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ver >= 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.mod_type = random.choice(list(Modulation))</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.mod_type == Modulation.ModGMSK:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tsc_set = random.randint(0, 3)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tsc_set = random.randint(0, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tsc = random.choice(self.TSC_RANGE)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # FIXME: C/I: Carrier-to-Interference ratio</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ci = random.choice(range(-100, 100))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Generates human-readable header description</span><br><span> def desc_hdr(self):</span><br><span> # Describe the common part</span><br><span>@@ -470,9 +648,61 @@</span><br><span> if self.toa256 is not None:</span><br><span> result += ("toa256=%d " % self.toa256)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ver >= 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ if not self.nope_ind:</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.mod_type is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ result += ("%s " % self.mod_type)</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.tsc_set is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ result += ("set=%u " % self.tsc_set)</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.tsc is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ result += ("tsc=%u " % self.tsc)</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ci is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ result += ("C/I=%d " % self.ci)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ result += "IDLE / NOP IND"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Strip useless whitespace and return</span><br><span> return result.strip()</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # Encodes Modulation and Training Sequence info</span><br><span style="color: hsl(120, 100%, 40%);">+ def gen_mts(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ # IDLE / nope indication has no MTS info</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.nope_ind:</span><br><span style="color: hsl(120, 100%, 40%);">+ return self.NOPE_IND</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # TSC: . . . . . X X X</span><br><span style="color: hsl(120, 100%, 40%);">+ mts = self.tsc & 0b111</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # MTS: . X X X X . . .</span><br><span style="color: hsl(120, 100%, 40%);">+ mts |= self.mod_type << 3</span><br><span style="color: hsl(120, 100%, 40%);">+ mts |= self.tsc_set << 3</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return mts</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # Parses Modulation and Training Sequence info</span><br><span style="color: hsl(120, 100%, 40%);">+ def parse_mts(self, mts):</span><br><span style="color: hsl(120, 100%, 40%);">+ # IDLE / nope indication has no MTS info</span><br><span style="color: hsl(120, 100%, 40%);">+ self.nope_ind = (mts & self.NOPE_IND) > 0</span><br><span style="color: hsl(120, 100%, 40%);">+ if self.nope_ind:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.mod_type = None</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tsc_set = None</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tsc = None</span><br><span style="color: hsl(120, 100%, 40%);">+ return</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # TSC: . . . . . X X X</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tsc = mts & 0b111</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # MTS: . X X X X . . .</span><br><span style="color: hsl(120, 100%, 40%);">+ mts = (mts >> 3) & 0b1111</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mts & 0b1100) > 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Mask: . . . . M M M S</span><br><span style="color: hsl(120, 100%, 40%);">+ self.mod_type = Modulation(mts & 0b1110)</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tsc_set = mts & 0b1</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ # GMSK: . . . . 0 0 S S</span><br><span style="color: hsl(120, 100%, 40%);">+ self.mod_type = Modulation.ModGMSK</span><br><span style="color: hsl(120, 100%, 40%);">+ self.tsc_set = mts & 0b11</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Generates message specific header part</span><br><span> def gen_hdr(self):</span><br><span> # Allocate an empty byte-array</span><br><span>@@ -485,6 +715,20 @@</span><br><span> # Big endian, 2 bytes (int32_t)</span><br><span> buf += struct.pack(">h", self.toa256)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ver >= 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Modulation and Training Sequence info</span><br><span style="color: hsl(120, 100%, 40%);">+ mts = self.gen_mts()</span><br><span style="color: hsl(120, 100%, 40%);">+ buf.append(mts)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # C/I: Carrier-to-Interference ratio</span><br><span style="color: hsl(120, 100%, 40%);">+ # 2 octets, big endian (int16_t)</span><br><span style="color: hsl(120, 100%, 40%);">+ # FIXME: Dummy filling (C/I)</span><br><span style="color: hsl(120, 100%, 40%);">+ if not self.nope_ind:</span><br><span style="color: hsl(120, 100%, 40%);">+ # FIXME!!!</span><br><span style="color: hsl(120, 100%, 40%);">+ buf += bytearray(2)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ buf += bytearray(2)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return buf</span><br><span> </span><br><span> # Parses message specific header part</span><br><span>@@ -495,6 +739,18 @@</span><br><span> # Parse ToA (Time of Arrival)</span><br><span> self.toa256 = struct.unpack(">h", hdr[6:8])[0]</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if self.ver >= 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Modulation and Training Sequence info</span><br><span style="color: hsl(120, 100%, 40%);">+ self.parse_mts(hdr[8])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # C/I: Carrier-to-Interference ratio</span><br><span style="color: hsl(120, 100%, 40%);">+ # 2 octets, big endian (int16_t)</span><br><span style="color: hsl(120, 100%, 40%);">+ if not self.nope_ind:</span><br><span style="color: hsl(120, 100%, 40%);">+ # FIXME!!!</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ci = None</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ci = None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # Generates message specific burst</span><br><span> def gen_burst(self):</span><br><span> # Convert soft-bits to unsigned soft-bits</span><br><span>@@ -505,19 +761,8 @@</span><br><span> </span><br><span> # Parses message specific burst</span><br><span> def parse_burst(self, burst):</span><br><span style="color: hsl(0, 100%, 40%);">- length = len(burst)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Distinguish between GSM and EDGE</span><br><span style="color: hsl(0, 100%, 40%);">- if length >= EDGE_BURST_LEN:</span><br><span style="color: hsl(0, 100%, 40%);">- burst_usbits = list(burst[:EDGE_BURST_LEN])</span><br><span style="color: hsl(0, 100%, 40%);">- else:</span><br><span style="color: hsl(0, 100%, 40%);">- burst_usbits = list(burst[:GSM_BURST_LEN])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> # Convert unsigned soft-bits to soft-bits</span><br><span style="color: hsl(0, 100%, 40%);">- burst_sbits = self.usbit2sbit(burst_usbits)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- # Save</span><br><span style="color: hsl(0, 100%, 40%);">- self.burst = burst_sbits</span><br><span style="color: hsl(120, 100%, 40%);">+ self.burst = self.usbit2sbit(burst)</span><br><span> </span><br><span> # Transforms this message to L12TRX message</span><br><span> def gen_l12trx(self, ver = None):</span><br><span>@@ -682,6 +927,18 @@</span><br><span> </span><br><span> log.info("Check header version %u coding: OK" % ver)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # Match version specific fields</span><br><span style="color: hsl(120, 100%, 40%);">+ if msg_trx2l1.ver >= 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("TRX2L1: %s" % msg_trx2l1.desc_hdr())</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("TRX2L1: %s" % msg_trx2l1_dec.desc_hdr())</span><br><span style="color: hsl(120, 100%, 40%);">+ assert(msg_trx2l1_dec.nope_ind == msg_trx2l1.nope_ind)</span><br><span style="color: hsl(120, 100%, 40%);">+ assert(msg_trx2l1_dec.mod_type == msg_trx2l1.mod_type)</span><br><span style="color: hsl(120, 100%, 40%);">+ assert(msg_trx2l1_dec.tsc_set == msg_trx2l1.tsc_set)</span><br><span style="color: hsl(120, 100%, 40%);">+ assert(msg_trx2l1_dec.tsc == msg_trx2l1.tsc)</span><br><span style="color: hsl(120, 100%, 40%);">+ assert(msg_trx2l1_dec.ci == msg_trx2l1.ci)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ log.info("Check version %u specific header coding: OK" % ver)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> msg_trx2l1_gen = msg_l12trx.gen_trx2l1()</span><br><span> msg_l12trx_gen = msg_trx2l1.gen_l12trx()</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmocom-bb/+/14579">change 14579</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmocom-bb/+/14579"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: osmocom-bb </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ie810c5a482d1c908994e8cdd32a2ea641ae7cedd </div>
<div style="display:none"> Gerrit-Change-Number: 14579 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: fixeria <axilirator@gmail.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>