Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help with embedded outbound Reverse Channel information #8

Open
kantooon opened this issue Jan 23, 2024 · 28 comments
Open

Help with embedded outbound Reverse Channel information #8

kantooon opened this issue Jan 23, 2024 · 28 comments

Comments

@kantooon
Copy link

kantooon commented Jan 23, 2024

Hi, I'm trying to implement in MMDVMHost the DMR tier III reverse channel commands for MS de-key and MS power control.
I use ok-dmrlib to generate the 32 bit PDUs, but the radios do not respond to the commands so there seems to be some issue with my code.
I figured out by myself that in VBPTC3211 I have to invert the parity bits row to odd in the set_parity() method and changed it accordingly. I'm not so sure about CRC7 configuration.
I wonder if you could help me troubleshoot my ok-dmrlib code below. Otherwise, the EMB fields are set with LCSS 0, PI 1 and correct colour code. The PDU is only transmitted in voice burst F so that seems fine as well.

 ba = bitarray('0100')
 CRC7_conf = BitCrcConfiguration(
        width_bits=7,
        polynomial=0x27,
        init_value=0x00,
        final_xor_value=0x7A,
        reverse_input_bytes=False,
        reverse_output_bytes=False,
    )
    crc7_calc = BitCrcCalculator(CRC7_conf, True)
    crc7_nm = crc7_calc.calculate_checksum(ba)
    print(crc7_nm)
    ba = ba + crc7_nm
    out = VBPTC3211.encode(ba)
    print(out)
    pad = bitarray('0000')
    padl = bitarray('0000')
    pad.extend(out)
    pad.extend(padl)
    all_bytes = bits_to_bytes(pad)
    readable = all_bytes.hex(',').split(',')
    x = str()
    for i in readable:
        x += "0x" + i + ', '
    print(x)
@smarek
Copy link
Member

smarek commented Jan 23, 2024

Hey @kantooon

  1. for formatting, put ``` (three backticks) on empty line before and after your code, docs: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks
  my formatted code
  1. Can you let me know if you have RC Info payload with valid CRC7 ? I can add the CRC7 with tests here

  2. From "ETSI TS 102 361-1 V2.5.1 (2017-10)" section B.3.13 (7-bit CRC calculation) I'd guess this is correct config

https://github.com/OK-DMR/ok-dmrlib/blob/master/okdmr/dmrlib/etsi/crc/crc.py#L291

    BitCrcConfiguration(
        width_bits=7,
        polynomial=0x27,
        init_value=0x00,
        final_xor_value=0x00,
        reverse_input_bytes=False,
        reverse_output_bytes=False,
    )

Not sure why you have final_xor_value=0x7A

@kantooon
Copy link
Author

Hi, thanks for responding, answers below:

2. Can you let me know if you have RC Info payload with valid CRC7 ? I can add the CRC7 with tests here

Unfortunately I don't. I'm writing this blind, and the only way of testing it is with my radios (Hytera HP785, manual says it supports transmit interrupt and power control).

Not sure why you have final_xor_value=0x7A

ETSI TS 102 361-1, section B.3.12 Data Type CRC Mask, table B.21, reverse channel has a CRC mask of 0x7A. Isn't that the purpose of final_xor_value?

Also, if you can, is it possible to add odd parity option to VBTC3211 as in B.2.2.2 Reverse Channel Single Burst BPTC?

@smarek
Copy link
Member

smarek commented Jan 23, 2024

Ok, data type mask, i handle those differently since the CRC impl is typically not data-type-specific, indeed I think that's where your problem might be

If you take a look for example in CRC9

return ba2int(~CRC9.CALC.calculate_checksum(data)) ^ mask.value

I apply the mask to calculated and two-complement value of CRC, you can try similar flow, coding the crc's was the most tricky part for me as well :))

And last, how did you figure your payload is not taking effect because of invalid CRC ? Working with Hytera radios sometimes the reason is codeplug settings or licensing or just straight firmware bug. If you're able to capture full traffic with your MS as PCAP, we could maybe see more? I guess you're already aware of pcap tooling we have

@kantooon
Copy link
Author

Ok, data type mask, i handle those differently since the CRC impl is typically not data-type-specific, indeed I think that's where your problem might be

If you take a look for example in CRC9

return ba2int(~CRC9.CALC.calculate_checksum(data)) ^ mask.value

I apply the mask to calculated and two-complement value of CRC, you can try similar flow, coding the crc's was the most tricky part for me as well :))

Thanks for the advice, I'll try this method as well, but for CRC9 the standard specifies an inversion polynomial, whereas for CRC7 it explicitly says there's no inversion polynomial.

And last, how did you figure your payload is not taking effect because of invalid CRC ? Working with Hytera radios sometimes the reason is codeplug settings or licensing or just straight firmware bug. If you're able to capture full traffic with your MS as PCAP, we could maybe see more? I guess you're already aware of pcap tooling we have

I guess I will need to double check the codeplug, power control is explicitly enabled, but there's no setting for receiving TX interrupt, only for transmitting. As far as licenses, I'm covered, the tier III license has everything.
I'll need to check what I can do with pcap since I don't see how to get the data from MMDVM to pcap...

@smarek
Copy link
Member

smarek commented Jan 23, 2024

Regarding the network, suffices to capture network traffic between mmdvmhost (i assume you have some rpi hotspot or similar) and your computer (hblink maybe?), pcap can then be filtered and data extracted using dmrlib-pcap-tool, usually simply running "dmrlib-pcap-tool PATH_TO_YOUR_CAPTURE.pcapng" shows you data and stats, then you can fiddle with cli switches, see help

> dmrlib-pcap-tool --help
usage: dmrlib-pcap-tool [-h] [--ports WHITELIST_PORTS [WHITELIST_PORTS ...]] [--filter BLACKLIST_PORTS [BLACKLIST_PORTS ...]] [--no-statistics]
                        [--print-raw] [--extract-embedded-lc] [--observe-transmissions] [--debug-vocoder-bytes] [--verbose] [--ipsc]
                        [--filter-ip FILTER_IP [FILTER_IP ...]]
                        files [files ...]

Read and debug UDP packets in PCAP/PCAPNG file

positional arguments:
  files                 PCAP or PCAPNG file to be read

options:
  -h, --help            show this help message and exit
  --ports WHITELIST_PORTS [WHITELIST_PORTS ...], -p WHITELIST_PORTS [WHITELIST_PORTS ...]
                        Whitelist UDP ports to inspect (source or destination or both) (default: [])
  --filter BLACKLIST_PORTS [BLACKLIST_PORTS ...], -f BLACKLIST_PORTS [BLACKLIST_PORTS ...]
                        Blacklist UDP ports to exclude from inspection (source or destination or both) (default: [67, 68, 53, 161, 162, 123, 137, 138, 443,
                        80, 1900, 5353, 5355, 30052, 30061])
  --no-statistics, -q   Suppress statistics at the end of output (default: False)
  --print-raw, -r       Print raw UDP Data before parsing the payload (default: False)
  --extract-embedded-lc, -e
                        Extract only Embedded LinkControl data and print these (default: False)
  --observe-transmissions, -o
                        Feed bursts to Transmission and debug the data and how the state of terminals changed (default: False)
  --debug-vocoder-bytes
                        Effective only with --observe-transmissions, will print voice bytes in format ready for vocoder decode (default: False)
  --verbose, -v         Verbose logging (default: False)
  --ipsc                Analyze IPSC traffic (map between slot-type, frame-type and data-type) (default: False)
  --filter-ip FILTER_IP [FILTER_IP ...]
                        Filter traffic by "ORIGIN" IP address(es) (default: [])

@kantooon
Copy link
Author

Oh I see what you meant with pcap. The RC PDU is only trnsmitted via RF currently, but I can also add it to the network transmisssion so I can wireshark it. I don't have a classic hotspot, my setup is all SDR-based and running on x86_64 Linux.

If you're curious, my test tier III system consists of a LimeSDR family device, qradiolink as lower PHY with 7 RF channels multiplexed in GNU radio flowgraphs, and some custom forks of MMDVM and MMDVMHost called MMDVM-SDR and MMDVMHost-SDR.
The setup is partly described here: https://qradiolink.org/multicarrier-transceiver-DMR-YSF-M17-with-MMDVM-and-LimeSDR.html but there's like zero docs for tier III since it's WIP.

The RC PDU transmit is initiated from the trunking controller (dmrtc) and sent on the alternate logical channel (it only works with aligned timing and not offset timing).

@smarek
Copy link
Member

smarek commented Jan 23, 2024

very impressive, i've yet to work more with downlink and Tier-III PDUs, and the reason is i don't have them captured (i have SDR but it wasn't my priority so far to get it), if you could supply me with the on-air data, i would add a support for all these PDUs and routines to handle them.

Regarding form of the data, you can take inspiration from our and lwvmobile's last-years adventure with dsd-fme "DSP structured output" lwvmobile/dsd-fme#94 (comment)

And last I guess your note about aligned/offset timing is just the current state of implementation, not actual AI protocol limitation?

@kantooon
Copy link
Author

Yes sure, I use dsd-fme as well to test PDUs. Let me know what PDU captures you need, I already asked lwvmobile for a few changes in the DMR decoder and supplied him with some captures off the air.

The aligned timing limitation is specified in the standard at 5.1.1 Channel timing relationship.
Reverse channel only works in this mode, basically the MS transmits its own timeslot, then switches to RX for the duration of the other timeslot and hears its own transmission repeated back (maybe with RC signalling). In offset timing, on the other hand it supports full duplex calls instead since the MS receives the other logical channel during that time.

@kantooon
Copy link
Author

I'm going to close this now, as I haven't been able to find the solution yet, but I'm busy with other stuff. I'll come back to this later with a fresh perspective and if I find the right solution I'll also let you know.
Thanks for your informative answers and your help in this topic!

@smarek smarek reopened this Jan 26, 2024
@smarek
Copy link
Member

smarek commented Jan 26, 2024

Keep it open, its bookmark for my brain as well, thanks and good luck 🤞

@smarek
Copy link
Member

smarek commented Nov 30, 2024

@kantooon just heads up, odd parity for B.2.2.2 is now included per 8e3c84d

@smarek
Copy link
Member

smarek commented Dec 1, 2024

@kantooon and because I became interested, i made some progress, you can check it 1a00ff4

However yes, currently CRC7 does not seem to match the data (which was extracted from Hytera IPSC protocol, so it might not be valid in the original IPSC payload as well)

Before the Single Burst RC BPTC encoder, the appropriate CRC Mask as defined in clause B.3.12 of the
present document shall be applied to the 7 bit CRC.
I think this means, you should not apply "Reverse Channel Crc Mask", but apply whatever mask matches the rest of surrounding data/voice burst

Few records from log

c3520050d1f6000001000501010000001111cccc1111000040b80022040220024460220002000104808f6460b252602000650022040220024440b8020000000006010000ce432800
178.238.234.72:50002 -> 192.168.1.110:50001 IPSC TS:1 SEQ: 209 [EmbeddedSignalling] [RC Info bitarray('11111000000001100000011001000101')][LCSS.SingleFragmentLCorCSBK] [PreemptionPowerIndicator.CarriesReverseChannelInformation] [CC: 1]

c3520050d7f6000001000501010000001111cccc1111000040b81d8ed769d5d5c5371f08b0c04113f9865321b58254a58012d5d5425f515188acb8c00000000006010000ce432800
178.238.234.72:50002 -> 192.168.1.110:50001 IPSC TS:1 SEQ: 215 [EmbeddedSignalling] [RC Info bitarray('01101111100100100001010100111000')][LCSS.SingleFragmentLCorCSBK] [PreemptionPowerIndicator.CarriesReverseChannelInformation] [CC: 1]

@kantooon
Copy link
Author

kantooon commented Dec 1, 2024

I think this is great news, I'll go generate the RC PDU again and compare it with the previously generated one. I can test the channel pre-emption over RF as well, but it's a little more convoluted, requires some changes in the trunking controller as well.

@kantooon
Copy link
Author

kantooon commented Dec 1, 2024

I just ran the new reverse_channel code on the PDU's I had already computed months ago. They are a a match bit for bit, so either the code is correct, or we both made the same mistake somewhere. These are also recognized by dsd-fme as such. From my previous testing, my Hytera radios don't respond to these reverse channel commands. It may be a CPS setting that I haven't found, or they might use some proprietary, non-ETSI PDU, not sure which.

@smarek
Copy link
Member

smarek commented Dec 1, 2024

Ok, i'm truly sorry, this has been a rabbit hole for me, turns out the initial test data are not correct, and I wasted few more hours trying to get it working

I need to get at least a single RC PDU (embedded or standalone) , which is verified to be complete and with valid CRC, please help me out if you can, @lwvmobile might have data maybe

Thanks

@smarek
Copy link
Member

smarek commented Dec 2, 2024

I cannot get it out of my head, few sanity checks, i think i'm going crazy
if you could take a look @kantooon please

1a) Parity - odd (zeroes==ones), even = (both counters should be (%2==0)), right?

odd parity
01000000001101011 <- RC(10 - 0) + H(4 - 0)
10111111110010100 <- PC(15 - 0)
ZEROES 17 ONES 17 LEN 34 ZEROES==ONES

even parity
01000000010010000 <- RC(10 - 0) + H(4 - 0)
01000000010010000 <- PC(15 - 0)
ZEROES 28 ONES 6 LEN 34 ZEROES%2==0, ONES%2==0

1b) Parity for all RC signalling should be "odd", so "even" should indicate "transmission bit-flip error", right?

1c) Parity should match, regardless of CRC Mask used, right?

2a) Distinguish embedded RC PDU (32bit) from LC fragment, these two conditions must be met, otherwise it's definitely not RC PDU, right?
- LCSS (LC Start/Stop) == 0 (SingleFragmentLCorCSBK)
- PI (Preemption Power Indicator) == 1 (CarriesReverseChannelInformation)

3a) i've found lwvmobile/dsd-fme#187 which seems my and lwvmobile implementations now match, which is unfortunate

4a) All RC embedded pdus I have have "even parity", which means they are not suitable for implementation/testing,


Also i found voice encryption by Chowdhary which places encryption params in the same places, but that should not be what i'm seeing in data captures, https://patentimages.storage.googleapis.com/df/89/8c/2b85c61f329b5b/US8422679.pdf

@kantooon
Copy link
Author

kantooon commented Dec 3, 2024

lwvmobile's implementation was independent, and it decodes my PDUs which are based on your code, it seems unlikely we all made the same error. It's more likely Hytera uses some other proprietary signalling I think.

@lwvmobile
Copy link

Just for what its worth, I don't think I've ever seen an actual Reverse Channel burst in real-life application for a system I've decoded. I have only ever seen the Single Burst, and its always one of three values: 1. TXI. 2. Encryption Key/Alg. 3. All Zeroes/Null.

All of the above are not per any sort of ETSI standard, aside from the BTPC encoding and parity. The CRC on the TXI pdu is different, and non-existent when it carries the Encryption info.

TXI Patent: https://patents.google.com/patent/US8271009B2
ENC Patent: https://patents.google.com/patent/EP2347540B1/en

Other than that, I have seen a variety of Tytera/Retevis radios signal something in the single burst that results in decoding errors from dsd-fme, so it is either random/garbage padding fill, or some other form of encoding scheme that isn't BPTC that only those CCR radios can interpret.

@mastodon987
Copy link

mastodon987 commented Dec 7, 2024

Hi, im currently looking on that issue too. This is closest i came (one bit error OTA).

i have an idea that there is something issue in vbptc3211. maybe double de/interleave or in algo. Lets see below.

as example i generate reverse channel with command Cease Transmission Command (0100).

File contents at the end of this comment.

Im using your previous vbptc_32_11 version without odd parity.

  1. After interleaving RC with call ReverseChannel(rc_command=RCCommand.CeaseTransmissionCommand).as_bits()
    i get this interleaved bitarray('01110000110100000100111100011111') - generated.

  2. When i deinterleave it same way ReverseChannel.from_bits((bitarray('01110000110100000100111100011111'))
    i get back origin RC with description just [RCCommand.CeaseTransmissionCommand], which is correct.

  3. when i pack it into burst with VoiceF with Idle Data (just as example) i get this bytes ready for send to Hytera
    '68e3206bd099b561cf0b0d02c651870d04f1f2b929d17bd189cf0ac5f30e6ea2da'
    IPSC TS:2 SEQ: 57 [EmbeddedSignalling] [LCSS.SingleFragmentLCorCSBK] [PreemptionPowerIndicator.CarriesReverseChannelInformation] [CC: 1] [RCCommand.CeaseTransmissionCommand]
    [[0 1 0 0 1 0 0 0 0 0 1 1 0 0 1 1]] - original deinterleaved bits + hamming

  4. After receiving it with SDR and playing in dsd-fme with .wav file as input with correct sample frequency i get this bytes in DSP file (-Q)
    '68e3206bd099b561cf0b0d02c651830d04f1f2b929d17bd189cf0ac5f30e6ea2da' which decodes closely as generated burst (same description, so without repeating it):
    Where new bitarray for RC is this: bitarray('00110000110100000100111100011111') - from dsd-fme interleaved still. here is different second bit of bitarray.

  5. After decoding it again into ReverseChannel.from_bits(bitarray('00110000110100000100111100011111'))
    i get this deinterleaved bits + hamming: [0 1 0 0 1 0 0 0 0 0 1 1 0 0 1 1] which is same as generated data thanks to parity check i guess.

just for sure here is deinterleaved all 32 bits from origin and from dsd-fme from that we can see there is difference in parity check bit PC[7]:
origin: bitarray('01001000001100111011011111001100')
dsd-fme: bitarray('01001000001100111011011101001100')

just for checking if parity check is okay i will split both into two lines to see inverting 0->1 and 1->0:

origin - here we are correct:
0100100000110011
1011011111001100

dsd-fme - here is mismatch maybe caused by OTA:
0100100000110011
1011011101001100

I captured it with SDR# with noise floor -45dB and signal close to 0dB, so maybe there can be reason for that bit mismatch.

However interesting is that dsd-fme didn't output any RC information also no "99" lines in DSP file. Not sure why.

In attachments there is DSP file for these data. all same bursts was sent to Hytera. (will be if i find working web browser)

As frame_type and slot_type for Hytera i used slot_type_data_f and frame_type_data. I tried also for different type. if i put RC into CSBK Preambles,
then repeater forces BsSourcedData which overwrites RC information. When i tried sending it with UDT Header, then repeater did not transmit it at all.

So now what is different? When i saw @smarek RC Info bitarray that i see that parity check is wrong calculated.
Actually not sure if it is interleaved or deinterleaved.
from this issue #8:
1111100000000110
0000011001000101

So there from what i believe is issue in vbptc_32_11 file in interleaving. code at the end of comment.
Crc7.py file content i have same as smarek. and also 0x7A mask in Crc_masks.py

Unfortunately also in these transmitted bursts there is a lot FEC failures. not sure if it is because of signal or because of weaker protection. Dont know...
Also i do not have any compatible radio to check that.

from typing import Union

from bitarray import bitarray
from bitarray.util import ba2int, int2ba

from okdmr.dmrlib.etsi.fec.vbptc_32_11 import VBPTC3211
from okdmr.dmrlib.etsi.crc.crc7 import CRC7
from okdmr.dmrlib.etsi.layer2.elements.rc_command import RCCommand
from okdmr.dmrlib.etsi.layer2.elements.crc_masks import CrcMasks

from okdmr.dmrlib.utils.bits_bytes import numpy_array_to_int
from okdmr.dmrlib.utils.bits_interface import BitsInterface


class ReverseChannel(BitsInterface):
    """
	
    ETSI TS 102 361-4 V1.12.1 (2023-07) - 6.4.14.1 Reverse Channel (RC) PDU
    """

    def __init__(
        self,
        rc_command: Union[RCCommand, int] = 0,
        crc7: int = 0,
    ):

        self.rc_command: bitarray = (
            int2ba(rc_command, length=4) if isinstance(rc_command, int)
            else int2ba(RCCommand(rc_command).value, length=4)
        )
        self.crc7 = CRC7.calculate(self.rc_command, CrcMasks.ReverseChannel) #0x7A
        self.crc7_ok: bool = CRC7.calculate(self.rc_command, CrcMasks.ReverseChannel) == crc7
 
    def __repr__(self) -> str:
        return (
            f" [{RCCommand(ba2int(self.rc_command))}]"
            f" {'[Wrong CRC]' if not self.crc7_ok else ''}"

    def as_bits(self) -> bitarray:
        return VBPTC3211.encode(
            bitarray(self.rc_command)
            + int2ba(self.crc7, length=7)
        )

    @staticmethod
    def from_bits(bits: bitarray) -> "ReverseChannel":
        assert (
            len(bits) == 32
        ), "RC (Reverse Channel) should be exactly 32 bits long"
        print('RC_BITS: ', bits)
        deinterleaved_bits = VBPTC3211.deinterleave_data_bits(bits)
        return ReverseChannel(
            rc_command=ba2int(deinterleaved_bits[0:4]),
            crc7=ba2int(deinterleaved_bits[4:11]),
        )

vbptc_32_11.py.new is latest from master 75d6f52. and left file is what im using from previous version.
diff ../etsi/fec/vbptc_32_11.py ../etsi/fec/vbptc_32_11.py.new

107c107
<         Will take BPTC interleaved (and FEC protected) bits and return 32 bits of deinterleaved bits
---
>         Will take BPTC interleaved (and FEC protected) bits and return 11 bits of deinterleaved bits
109c109
<         :return: bitarray with 32 (11b data bits + 5b hamming bits + 16b parity bits)
---
>         :return:
118,121c118
<             out[n] = bits[i]
<
< #        for i in range(16):
< #            out[16 + i] = out[16 + i] ^ 1
---
>             out[i] = bits[n]
180c177
<     def encode(bits_deinterleaved: bitarray) -> bitarray:
---
>     def encode(bits_deinterleaved: bitarray, even_parity: bool = True) -> bitarray:
209d205
< #        print('table: ', table)
213d208
< #        print('table: ', table)
217c212
<             table[:, column] = VBPTC3211.set_parity(table[:, column])
---
>             table[:, column] = VBPTC3211.set_parity(table[:, column], even_parity)
219d213
< #        print('table: ', table)
233c227
<     def set_parity(column: numpy.ndarray, inverse_parity:bool = True) -> numpy.ndarray:
---
>     def set_parity(column: numpy.ndarray, even_parity: bool = True) -> numpy.ndarray:
235,238c229
<         if inverse_parity:
<             column[1] = column[0] ^ 1
<         else:
<             column[1] = column[0]
---
>         column[1] = column[0] if even_parity else not column[0]

@mastodon987
Copy link

I just ran the new reverse_channel code on the PDU's I had already computed months ago. They are a a match bit for bit, so either the code is correct, or we both made the same mistake somewhere. These are also recognized by dsd-fme as such. From my previous testing, my Hytera radios don't respond to these reverse channel commands. It may be a CPS setting that I haven't found, or they might use some proprietary, non-ETSI PDU, not sure which.

In CPS v9 is in developer menu option to choose if radio will support only ETSI (v1.7.1) format for RC, or Hytera format plus ETSI same version. So only two options. According to ETSI docs v1.7.1 that means that V9 Hytera radios does not support Cease transmission request. However Cease transmission command they should support. No more switches i didnt find there.

@lwvmobile
Copy link

I looked over the code for DSP output on the SB/RC, and I've found a bug in DSD-FME that may produce invalid results on the 99 line (contained to just the DSP file itself, not the internal decoding of SB or RC). Also, it is currently only set to dump the RC with designation 99 if the p/pi indicator bit is set to 1, and its not all zeroes. I will change that to always dump, if either or.

I do need to push an update to fix the array overflow for that output, but also need a bit of feedback first, and potentially a few .bin samples with RC information in it, either from a radio, or crafted, and you tell me what is supposed to be in said RC or SB so I can make sure that it is correct.

Also, if desired, I can add an indication on the line to indicate whether the p/pi bit is on or off, or whether or not it is a single burst, or reverse channel burst by appending it to the hex output on that line. Just needed to check to make sure that doesn't break any import scheme or not first.

@mastodon987
Copy link

mastodon987 commented Dec 8, 2024

Sure. here are files for you both.

idle_rc.bin.txt
idle_rc.dsp.txt

Just as info i transmitted only F bursts with 360ms spacing, so other non-F bursts may be filled by repeater itself with something unknown. LCSS=0 (SingleFragmentLCorCSBK), PremeptionPowerIndicator=1.

whole 48bits carries CC=1, PI=1, LCSS=0, EMB parity. inside 32bits RC is CeaseTransmissionCommand, CRC7, Hamming, parity check.

@lwvmobile
Copy link

I had a lot of errors in your capture, any chance you can clean up your reception by tweaking settings and so on with SDR#, making sure your radio isn't overloading the front end of your SDR, and so on?

Watching the output of dsd-fme console log is a good indication. I couldn't do much with the first bin file, too many errors.

@mastodon987
Copy link

mastodon987 commented Dec 8, 2024

Here are new files. i transmitted 12 bursts, but there is also repeater delay for transmittion.
In dsd-fme i see one burst decoded correct.
In dmrlib-dsd-fme (with .dsp as input file) i see 2 RC bursts correct and at least 30 RC not correct bursts with wrong EMB FEC.
idle_rc2.bin.txt
idle_rc2.dsp.txt

few more tests decoding in dmrlib tool.
Now received bitarray from SDR# is exactly same as generated in those decoded bursts.
RC_bits:
bitarray('01110000110100000100111100011111') - interleaved.
bitarray('01001000001100111011011111001100') - deinterleaved.

parity check correct:
0100100000110011
1011011111001100

@lwvmobile
Copy link

Alright, that one still had some errors in it, most likely from your radio overloading the front end of the SDR, but it was good enough to get a few good RC bursts out of it for testing.

Did have a couple of random questions, though.

Here is a snippet of output from DSD-FME.

01:15:47 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC1 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:47 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
01:15:47 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC2 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:47 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
 Activity Update TS1: Idle; Hash: 0; TS2: Idle; Hash: 0;
 SLCO Completed Block [10][00][00][01][60]

01:15:47 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC3 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC4 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
 Activity Update TS1: Idle; Hash: 0; TS2: Idle; Hash: 0;
 SLCO Completed Block [10][00][00][01][60]

01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC5 
 AMBE 63B1D952237A80 err = [1] [2] 
 AMBE 7A567518A1BF80 err = [2] [4] 
 AMBE F48C5CFAA38100 err = [2] [4] 
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC6 
 SLOT 2 FLCO FEC ERR  (FEC ERR)
 DMR PDU Payload [84][81][00][20][38][0F][83][E0][F8]
 AMBE 63B1D952237A80 err = [1] [2] 
 AMBE 7A567518A1BF80 err = [2] [4] 
 AMBE F48C5CFAA38100 err = [2] [4] 
 RC: 01001000001 - 241; 
 RC: Cease Transmission Command;
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
 Activity Update TS1: Idle; Hash: 0; TS2: Idle; Hash: 0;
 SLCO Completed Block [10][00][00][01][60]

01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC1 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC2 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
 Activity Update TS1: Idle; Hash: 0; TS2: Idle; Hash: 0;
 SLCO Completed Block [10][00][00][01][60]

01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC3 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC4 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
 Activity Update TS1: Idle; Hash: 0; TS2: Idle; Hash: 0;
 SLCO Completed Block [10][00][00][01][60]

01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC5 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
01:15:48 Sync: +DMR  [slot1]  slot2  | Color Code=01 | IDLE 
01:15:48 Sync: +DMR   slot1  [SLOT2] | Color Code=01 | VC6 
 SLOT 2 FLCO FEC ERR  (FEC ERR)
 DMR PDU Payload [03][00][00][23][30][0F][15][37][47]
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 AMBE F801A99F8CE080 err = [0] [0] 
 SB: 00000000000 - 000; 

Is your embedded signalling for VC B-E for link control broken? Every single instance all produced an FEC error. Speaking of which, didn't capture a single voice link control header not a termination link control header (VLC and TLC in DSD-FME vernacular).

When the Reverse Channel hits, does it knock out both Voice in E and F? Both of those voice frames show errors, otherwise you get the standard silence vector from AMBE+2. Isn't it just supposed to drop F?

When you are active, your activity update slow link control shows both timeslots are IDLE status with a 0 hash value. Shouldn't that have the 8-bit hash of your target or talkgroup value in it? When you stop transmitting, I noticed the slow link control goes to a NULL message with all zeroes in it.

@mastodon987
Copy link

mastodon987 commented Dec 8, 2024

I didnt generate any A-E Voice bursts. Let's me explain little better that. Those generated F only bursts are instead of voice payload filled with Idle pattern and sent over IP network to repeater as it gives me better flexibility for experimenting. So repeater is filling those A-E bursts with something unknown to me for just keeping transmitting continuously (maybe with idle only for keep channel open before hangtime). That means in receipent handheld radio i listen just buzzing ticks every 360ms same as you can hear in dsd-fme.
From ETSI docs i thought that in voice channel is place for RC only in F bursts, as A-E are desired for FullLC. 361-1 V2.6.1 (2023-05) its desribed in Figure 7.5 plus in 5.1.5.1 Embedded outbound RC is mentioned four rules where RC belongs to be placed. Just in quick: F burst, not in LC Header/Data Header, but can be in UDT Data Header, not after TLC, and not repeating more than 360ms.
Actually what i generated is i think mix between embedded RC and dedicated RC becuase of that idle payload, but im not sure. Dedicated are defined in 5.1.5.2.
Reason for that is it is easier to generate them like that than working with ambe encoding in linux with adding RC to that just for now. It's because we are looking for RC only for now.
About signal strength. Im actually few kilometers away from repeater, so i had to little increase SDR gain for incoming traffic for decoding.

Edit: Also i looked on those 2 bursts which was decoded with dmrlib as in current master - dmrlib is not implemented RC decoding (its just mine modified version) gives me idea that it is from "10" lines in dsp file from latest idle_rc2.dsp. actualy is one closest "10" line after 99 line which means for what i assume that dsd-fme didn't decoded it.
By closer look i found that burst is same by bits as line 10 just before 99. Maybe dsd-fme is awaits there automatically some different type and didn't decoded it embedded RC? i dont know.
As there is no frames A-E so there cannot be any info for source/destination radio id.

@lwvmobile
Copy link

Oh, okay, I understand better now how the samples were generated and what ended up being decoded on them.

As far as RC, from the appendix of 361-1 (Tables E.X), seems like they can be with voice, standalone without anything at all(dead air), NULL(zeroes), or be the same as a data sync burst but where the sync pattern for data is replaced by the RC channel data, which I assume also occurs on VC-F bursts. Those types can be encoded as BPTC or 3/4 Rate Trellis Data.

Internally, I don't have a check down for looking to see what is in the VC-F location, whether it should be voice or data when the P/PI bit is set or anything, it just assumes voice and produces those errors in AMBE when played as such. May have to implement that one day in the future, but would require quite a bit of rewrites, which I had not planned on doing, so most likely won't be happening, but I may leave in a bit of test code to just push any potential RC data through both data and voice, and see if one or the other produces any actual results, such as decoding a single data burst, whatever kind usually may accompany a BPTC general data burst with RC, for example.

On a related note, DSD-FME (when DSP output is fixed, anyways, for RC) will produce both a line for the RC data, and the complete VC frame, but is that really necessary. Reason I ask, we already have the RC data embedded in the middle of the VC frame where the sync pattern would be on VC-A, so, isn't it kind of redundant?

2 98 0B9C28
2 10 68E3206BD099B561CF0B0D02C651870D04F1F2B929D17BD189CF0AC5F30E6EA2DA
2 99 70D04F1F <--is this really needed if its the same as above?

@mastodon987
Copy link

mastodon987 commented Dec 8, 2024

By me its already okay in dsd-fme as it seems working. Im here to just maybe help smarek and kantoon them in RC encoding implementation. About that 99 line i think its place here for answer from smarek.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants