Decoding single-channel NXDN

Status
Not open for further replies.

AK9R

Lead Wiki Manager and almost an Awesome Moderator
Staff member
Super Moderator
Joined
Jul 18, 2004
Messages
11,202
Reaction score
10,881
Location
Central Indiana
I'm hearing what might be a single-channel NXDN transmission on VHF. It could be the output of a repeater, but I'm not sure. I'm 99% certain that it's not trunked.

I have an RTL-SDR available. What software would you suggest to decode this transmission?

Thank you.
 

Reconrider

Inside the Galaxy
Joined
Sep 26, 2017
Messages
1,993
Reaction score
923
Location
Radio Galaxy
I was going to suggest SDRTrunk but good thing I checked, it doesn't do it haha.

You can get DSDplus fastlane, 25$ for lifetime or 10$ for a year and do it with just that
or
Use the SDR# plugin and try it with that. dsd comes bundled with sdr# already I believe and you just have to look for the dsd plugin in the menu, unless that was removed. I never had luck doing it this way, but lots of others have.
 

lwvmobile

DSD-FME
Joined
Apr 26, 2020
Messages
1,479
Reaction score
1,025
Pretty sure you can just use the base version of dsdplus if all you want is NXDN voice decoding. I don't recall the voice quality being significantly better on Fast Lane for NXDN. It might be a little more difficult to route the audio in and out of it though if you don't use the Fast Lane option, but you can use fmp if you just want to set it and forget it.

Edit: A nice alternative is using the compiled version of DSD 1.8.2 which was mentioned in the post here. It has a pretty simple to use bat file, but only does decoding, it doesn't have a tuning component, so you'll have to route the audio into it with virtual cables or something.
 
Last edited:

KA1RBI

Member
Joined
Aug 15, 2008
Messages
799
Reaction score
135
Location
Portage Escarpment
OP25 can do NXDN? I didn't know that.

NXDN (both RX *and* TX) was added circa 2020. We are aware that the website is out of date (interested serious volunteers are welcome to help update it - PM me).

The current level of NXDN in OP25 does not support call following on a trunked system, although most of the pieces except the overall driver logic are present. Reception of single channel in NXDN96 or NXDN48 (as re: the OP) is fully supported.

The addition of NXDN support in OP25 was possible thanks to a kind donation made by KB9MWR...

Max
 

lwvmobile

DSD-FME
Joined
Apr 26, 2020
Messages
1,479
Reaction score
1,025
Okay, after googling and working on this for a majority of the Bucaneers game, I think I got it figured out. The NXDN voice sounds really good side by side with Fast Lane, couldn't tell the difference.

Just for future reference if anybody goes to search for this and finds it, here is the json file I made for 2 NXDN channels in my area. May it serve as an example configuration for somebody who might need it. Any critiques or suggestions welcome as well. I honestly have no idea what half the settings in here really do, or if anything could be added to make it better. Also feel free to remove datascope if you don't want it or change the UDP ports to your liking.

Code:
{
    "channels": [
        {
            "demod_type": "fsk4",
            "device": "rtl_0",
            "frequency": 154987500,
            "destination": "udp://127.0.0.1:23456",
            "excess_bw": 0.2,
            "filter_type": "nxdn",
            "if_rate": 24000,
            "name": "Suwannee Fire Rescue NXDN48",
            "plot": "datascope",
            "symbol_rate": 2400

        },

        {
            "demod_type": "fsk4",
            "device": "rtl_0",
            "frequency": 153545000,
            "destination": "udp://127.0.0.1:23460",
            "excess_bw": 0.2,
            "filter_type": "nxdn",
            "if_rate": 24000,
            "name": "Suwannee Valley Electric NXDN48",
            "plot": "datascope",
            "symbol_rate": 2400

        }

    ],
    "devices": [
        {
            "args": "rtl=0",
            "frequency": 154200000,
            "gains": "LNA:42",
            "name": "rtl_0",
            "offset": 0,
            "ppm": -2,
            "rate": 2048000,
            "tunable": false
        }
       
    ]
   
}

Runs with
Code:
./multi_rx.py -c suwannee-nxdn-2022.json -v 10 2> suwannee-nxdn.log

Should mention, this only works with Osmocom OP25.
 

KA1RBI

Member
Joined
Aug 15, 2008
Messages
799
Reaction score
135
Location
Portage Escarpment
very nice! Bill and/or Graham may know of an easy way to configure a two-channel system such as this one so that the audio from the two channels is routed to the two speakers (left and right) respectively. Possibly a little magic using liquidsoap, not sure.

Thx

Max
 

ka3jjz

Wiki Admin Emeritus
Joined
Jul 22, 2002
Messages
25,897
Reaction score
2,579
Location
Bowie, Md.
What else does OP25 decode? While the website might not be up to snuff, at least we can fix the link in our wiki...;.Mike
 

KA1RBI

Member
Joined
Aug 15, 2008
Messages
799
Reaction score
135
Location
Portage Escarpment
What else does OP25 decode? While the website might not be up to snuff, at least we can fix the link in our wiki...;.Mike

* DMR (base station)
* Yaesu Fusion - both halfrate vocoder (DN) and fullrate vocoder(VW)
* Dstar
* NXDN48 and NXDN96
* P25 - Phase I, Phase II, and TDMA-CC

Please note that OP25 also contains a full stack of software encoders to enable voice transmission of all the above modes (for P25, only P1/FDMA/FSK4 is supported). The TX package also includes a P25 P1 trunking control channel / voice channel simulator known as "fakecc" which broadcasts a P25 CC and one voice channel concurrently. An SDR with transmit capability (such as the HackRF or USRP), and/or suitable analog radio transmitter(s) are required for any of these TX modes.

Max
 

lwvmobile

DSD-FME
Joined
Apr 26, 2020
Messages
1,479
Reaction score
1,025
* NXDN48 and NXDN96

On the NXDN decoding, does it decode the RID and optional text labels that come off of NXDN systems? I was looking in the stderr log and didn't find any mentions of RIDs, and also got the web interface running, but it doesn't show anything when an NXDN voice system is active. I didn't know if the web interface worked with NXDN or not, or do I need to set up the sql logging stuff first?
 

lwvmobile

DSD-FME
Joined
Apr 26, 2020
Messages
1,479
Reaction score
1,025
On the NXDN decoding, does it decode the RID and optional text labels that come off of NXDN systems?
Hang on, think I figured something out. Here is an example that is RID 1000 and Labeled as "Live Oak"

Code:
nxdn lich 41 voice 0 facch 3 sacch 1 cac 0 symbol 435398
nxdn: facch: 664108002001900000000000
nxdn: facch: 664108002001900000000000
missing expected NXDN sync, symbol 435408
NXDN: timeout, symbol 435408
resync at count 0 symbol 439891 for protocol NXDN
nxdn lich 51 voice 0 facch 3 sacch 1 cac 0 symbol 440073
nxdn: facch: 66513f688204144c69766500
nxdn: facch: 66513f68820424204f616b00

1000 in decimal is 0x190 hex
nxdn: facch: 664108002001900000000000

For the text label, converting hex to text (not sure if its ascii or utf-8 or other at the moment, but ran this through a website with a converter and found that

nxdn: facch: 66513f688204144c69766500
"Live"
nxdn: facch: 66513f68820424204f616b00
"Oak"
 

KA1RBI

Member
Joined
Aug 15, 2008
Messages
799
Reaction score
135
Location
Portage Escarpment
nxdn: facch: 664108002001900000000000
nxdn: facch: 664108002001900000000000

That's very interesting - it's been a year or two since I've looked at NXDN at all. From your printout I looked up the "nxdn: facch" messages and it appears that all of them ultimately get routed to a function 'process_nxdn_msg()' in multi_rx.py - which in turn can invoke another function 'cac_message()' located in nxdn_trunking.py.

It might be interesting to have you probe to find out how far along that path it gets ... If you have verbosity set higher than 2 it should print out this message:
Code:
sys.stderr.write ('process_nxdn_msg %s lich %x\n' % (msgtype, lich))

If 'msgtype' is 'c' it will treat it as a CAC message - but it may be another type, in which case it'd need to be parsed out.

Max
 

lwvmobile

DSD-FME
Joined
Apr 26, 2020
Messages
1,479
Reaction score
1,025
It might be interesting to have you probe to find out how far along that path it gets ... If you have verbosity set higher than 2 it should print out this message:
Well, I've been poking around a little bit in the code, but maybe I'm not thinking straight, but what function prints this string and which file is it located in
Code:
nxdn: facch: 664108002001900000000000
nxdn: facch: 664108002001900000000000

and what variable is the long hex string?
 

KA1RBI

Member
Joined
Aug 15, 2008
Messages
799
Reaction score
135
Location
Portage Escarpment
lib/rx_sync.cc issues that message; the first two bytes consist of a code prefix added by OP25 and the remainder consist of the received FACCH packet data (after FEC removed). In this case it looks like the code letter (msg type) is 'f' - a type sent to but not currently handled by the python decoder.

However all such messages (including the type 'f') should be forwarded to python-land. There is a general rule in OP25 that data packets such as this that are "low speed" and "asynchronous" are better done in python than C++...
 

lwvmobile

DSD-FME
Joined
Apr 26, 2020
Messages
1,479
Reaction score
1,025
However all such messages (including the type 'f') should be forwarded to python-land. There is a general rule in OP25 that data packets such as this that are "low speed" and "asynchronous" are better done in python than C++...

Well, I've been poking and prodding a little bit, but wanted to ask a few question with how OP25 handles NXDN.

First, when you start up an NXDN with "destination": "udp://127.0.0.1:23456", does it use 2 UDP ports for each NXDN channel, like DMR does? If I specify this, does it use 23456 and 23458?

Second, when I attempt to close OP25 with my config file above, it will usually hang and only close when there is an NXDN sync frame coming in. I usually have to end up closing the terminal window since its faster than waiting for the next NXDN sync frame to come through. Also, I've found that the logging of NXDN information seems to be a bit delayed, while I've been poking around checking the values for different lich codes, I usually have to wait several mintues before the information gets logged in the stderr.log. I was just wondering if OP25 stderrr output is dependent on having a sync frame or having data to decode.

Also, at present, I was able to do a little dumping of the byte code for s for different message types and lich values, and got this:

process_nxdn_msg S lich 57
Sname1 = 6576694c140482683f015753
Sname2 = 5357013f688204144c697665
SNAME = 4c697665
57 bytes = b'SW\x01?h\x82\x04\x14Live'
57name1 = 6576694c140482683f015753
57name2 = 5357013f688204144c697665
57NAME = 4c697665
process_nxdn_msg S lich 57
Sname1 = e803200001015753
Sname2 = 53570101002003e800000000
SNAME = 0
57 bytes = b'SW\x01\x01\x00 \x03\xe8\x00\x00\x00\x00'
57name1 = e803200001015753
57name2 = 53570101002003e800000000
57NAME = 0
process_nxdn_msg S lich 57
Sname1 = 6b614f20240482683f015753
Sname2 = 5357013f68820424204f616b
SNAME = 204f616b
57 bytes = b'SW\x01?h\x82\x04$ Oak'
57name1 = 6b614f20240482683f015753
57name2 = 5357013f68820424204f616b
57NAME = 204f616b
process_nxdn_msg S lich 57
Sname1 = e803200001015753
Sname2 = 53570101002003e800000000
SNAME = 0
57 bytes = b'SW\x01\x01\x00 \x03\xe8\x00\x00\x00\x00'
57name1 = e803200001015753
57name2 = 53570101002003e800000000
57NAME = 0
My working theory on labels is:
Code:
if lich == 0x57:
    name2 = int.from_bytes(s, "big")
    if (name2 & 0xFFFFFFFF) > 0:
        sys.stderr.write ('LABEL HEX = %x\n' % (name2&0xFFFFFFFF))

I was able to determine that the text labels seem to exist on byte code for lich 0x57 and message type S, but no always. Still trying to narrow it down. I do know that even Fast Lane has issues always getting a proper decode on these labels and sometimes you only get a partial label.

I've also been working on RID values existing in 0x41 and/or 'f' msgtype, and not sure how consistent it is at the moment.

2022-01-18 16:47:46.693550
RID?? = 400
2022-01-18 16:47:49.384196
LABEL HEX = 4c697665
2022-01-18 16:47:50.022236
LABEL HEX = 204f616b
2022-01-18 16:47:50.725528
RID?? = 115
2022-01-18 16:47:50.725594
RID?? = 0
2022-01-18 16:47:50.789544
RID?? = 115
2022-01-18 16:47:50.789618
RID?? = 0
2022-01-18 16:47:50.915430
RID?? = 115
2022-01-18 16:47:50.915487
RID?? = 0
2022-01-18 16:47:50.980765
RID?? = 115
2022-01-18 16:47:50.980849
RID?? = 0
2022-01-18 16:47:51.044106
RID?? = 115
2022-01-18 16:47:51.044174
RID?? = 1006
2022-01-18 16:47:51.305935
LABEL HEX = 2cb
2022-01-18 16:47:51.943498
LABEL HEX = 4c697665
2022-01-18 16:47:52.583810
LABEL HEX = 204f616b
2022-01-18 16:47:53.865797
LABEL HEX = 2cb
2022-01-18 16:47:54.503586
LABEL HEX = 4c697665
2022-01-18 16:47:55.142004
LABEL HEX = 204f616b
2022-01-18 16:47:56.423736
LABEL HEX = 2cb
2022-01-18 16:47:57.065855
LABEL HEX = 4c697665
2022-01-18 16:47:57.703692
LABEL HEX = 204f616b
2022-01-18 16:47:58.984881
LABEL HEX = 2cb
2022-01-18 16:47:59.301052
RID?? = 115
2022-01-18 16:47:59.301366
RID?? = 1000
2022-01-18 16:47:59.365470
RID?? = 115
2022-01-18 16:47:59.365795
RID?? = 1000
2022-01-18 16:47:59.490221
RID?? = 115
2022-01-18 16:47:59.490293
RID?? = 1000
2022-01-18 16:48:01.994136
LABEL HEX = 53746120

My working theory on RID is
Code:
if lich == 0x41:
    name2 = int.from_bytes(s, "big")
    if ((name2&0xFFFFF0000000000)>>40) > 0:
        sys.stderr.write ('RID?? = %d\n' % ((name2&0xFFFFF0000000000)>>40))
 
Status
Not open for further replies.
Top