mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 09:17:20 -05:00
Compare commits
58 Commits
20260715
...
rtl_overha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bf60caa6f | ||
|
|
dc055c6585 | ||
|
|
eb02148491 | ||
|
|
6cdcac5db2 | ||
|
|
a2a70bbb15 | ||
|
|
e2a6e351c5 | ||
|
|
4dfe095a7c | ||
|
|
fede9b4e20 | ||
|
|
493800a0f3 | ||
|
|
c0b6f186bd | ||
|
|
8b423a2f42 | ||
|
|
8ec385fdf2 | ||
|
|
da8b9b6116 | ||
|
|
a866d98e75 | ||
|
|
e094f95b03 | ||
|
|
f052271a16 | ||
|
|
09bf040810 | ||
|
|
48b032e47a | ||
|
|
046846337a | ||
|
|
836da1f556 | ||
|
|
dd8620bc39 | ||
|
|
283d000b59 | ||
|
|
5834127ff7 | ||
|
|
2ce089ccf8 | ||
|
|
4e64ef1c11 | ||
|
|
c764cf0eda | ||
|
|
8b4fa81711 | ||
|
|
106aae1814 | ||
|
|
391048a45a | ||
|
|
1b9c3aa659 | ||
|
|
e9b12b8bc6 | ||
|
|
9e9ae95219 | ||
|
|
86d91512bb | ||
|
|
0b00832b1f | ||
|
|
2655fccdea | ||
|
|
ac8f17bf20 | ||
|
|
0cb38d2c25 | ||
|
|
6fc327c3b6 | ||
|
|
00e7c51a21 | ||
|
|
e2402d0328 | ||
|
|
402cef34a9 | ||
|
|
29be104345 | ||
|
|
f3ec024000 | ||
|
|
412a4dc3b1 | ||
|
|
2eedcc125b | ||
|
|
9b399f31da | ||
|
|
2ec3f9a263 | ||
|
|
0decc5934c | ||
|
|
7dd9cd7566 | ||
|
|
30766b34bd | ||
|
|
e700e418be | ||
|
|
c6d3e4cea0 | ||
|
|
62a9372d61 | ||
|
|
cc200ea82c | ||
|
|
03c6b28de3 | ||
|
|
3fe14f3b1d | ||
|
|
3adcf5f449 | ||
|
|
b81e1e6d22 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -9,4 +9,5 @@ examples
|
||||
*.ans
|
||||
*.patch
|
||||
*.zip
|
||||
result
|
||||
result
|
||||
.cache
|
||||
@@ -39,62 +39,6 @@ if (LZ)
|
||||
add_definitions(-DLIMAZULUTWEAKS)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DASCII=ON to enable ASCII only Alias Support in NXDN
|
||||
option(ASCII
|
||||
"Build with ASCII only Alias Support in NXDN Enabled" ON)
|
||||
if (ASCII)
|
||||
add_definitions(-DASCII_DECODE)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DASCII=OFF -DBIG5=ON to enable ASCII and BIG5 Alias Support in NXDN
|
||||
option(BIG5
|
||||
"Build with BIG5 and ASCII Alias Support in NXDN Enabled" OFF)
|
||||
if (BIG5)
|
||||
add_definitions(-DBIG5_DECODE)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DASCII=OFF -DSJIS=ON to enable ASCII and SJIS Alias Support in NXDN
|
||||
option(SJIS
|
||||
"Build with SJIS and ASCII Alias Support in NXDN Enabled" OFF)
|
||||
if (SJIS)
|
||||
add_definitions(-DSJIS_DECODE)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DASCII=OFF -DUTF16=ON to enable ASCII and UTF16BE Alias Support in NXDN
|
||||
option(UTF16
|
||||
"Build with UTF16BE and ASCII Alias Support in NXDN Enabled" OFF)
|
||||
if (UTF16)
|
||||
add_definitions(-DUTF16_DECODE)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DTYPEC=OFF to disable NXDN Type-C and Conventional Decoding (enabled by default)
|
||||
option(TYPEC
|
||||
"Build with NXDN Type-C and Conventional Decoding Enabled" ON)
|
||||
if (TYPEC)
|
||||
add_definitions(-DTYPE_C_DECODE)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DTYPED=OFF to disable NXDN Type-D / IDAS Decoding (enabled by default)
|
||||
option(TYPED
|
||||
"Build with NXDN Type-D / IDAS Decoding Enabled" ON)
|
||||
if (TYPED)
|
||||
add_definitions(-DTYPE_D_DECODE)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DDCR=OFF to disable Japanese DCR (NXDN Variant) Decoding (enabled by default)
|
||||
option(DCR
|
||||
"Build with Japanese DCR (NXDN Variant) Decoding Enabled" ON)
|
||||
if (DCR)
|
||||
add_definitions(-DJPNDCR_DECODE)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DNOS=ON to enable older NXDN sync w/ pos and inv polarity, disable newer seperated (disabled by default)
|
||||
option(NOS
|
||||
"Build with Old NXDN Sync (pos and inv polarity) Enabled" OFF)
|
||||
if (NOS)
|
||||
add_definitions(-DNXDN_OLD_SYNC)
|
||||
endif ()
|
||||
|
||||
#use cmake option -DSID=ON to enable P25p1 Soft ID decoding -- Experimental
|
||||
option(SID
|
||||
"Build with P25p1 LSD/Soft ID Enabled" OFF)
|
||||
@@ -108,13 +52,14 @@ git_describe(GIT_TAG)
|
||||
|
||||
find_package(LibSndFile REQUIRED)
|
||||
find_package(MBE REQUIRED)
|
||||
find_package(ITPP REQUIRED)
|
||||
find_package(RTLSDR)
|
||||
find_package(Curses REQUIRED)
|
||||
find_package(PulseAudio REQUIRED)
|
||||
find_package(CODEC2)
|
||||
|
||||
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIRS})
|
||||
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES})
|
||||
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIRS})
|
||||
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES})
|
||||
|
||||
if(RTLSDR_FOUND)
|
||||
find_package(Threads)
|
||||
|
||||
15
cmake/FindITPP.cmake
Normal file
15
cmake/FindITPP.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
# - Try to find ITPP
|
||||
# Once done this will define
|
||||
#
|
||||
# ITPP_FOUND - System has ITPP
|
||||
# ITPP_INCLUDE_DIR - The ITPP include directory
|
||||
# ITPP_LIBRARY - The library needed to use ITPP
|
||||
#
|
||||
|
||||
find_path(ITPP_INCLUDE_DIR itpp/itcomm.h)
|
||||
|
||||
set(ITPP_NAMES ${ITPP_NAMES} itpp libitpp libitpp.dll)
|
||||
FIND_LIBRARY(ITPP_LIBRARY NAMES ${ITPP_NAMES})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(ITPP DEFAULT_MSG ITPP_LIBRARY ITPP_INCLUDE_DIR)
|
||||
@@ -70,7 +70,7 @@ if [ "$RELEASE" = "y" ]; then
|
||||
cp .profile dsd-fme-portable/dsd-fme/
|
||||
|
||||
#move (cut) the bat files to the portable folder root and delete the other ones so users won't get confused
|
||||
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/*.bat dsd-fme-portable/
|
||||
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/start-dsd-fme.bat dsd-fme-portable/
|
||||
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/example_options.txt dsd-fme-portable/
|
||||
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/complete_usage_options.txt dsd-fme-portable/
|
||||
rm -rf dsd-fme-portable/dsd-fme/examples/cygwin_bat
|
||||
@@ -94,6 +94,7 @@ if [ "$RELEASE" = "y" ]; then
|
||||
|
||||
#compiled items into release dsd-fme folder
|
||||
cp codec2/build/src/cygcodec2* dsd-fme-portable/dsd-fme/
|
||||
cp itpp-4.3.1/build/itpp/cygitpp* dsd-fme-portable/dsd-fme/
|
||||
cp rtl-sdr/build/src/cyg* dsd-fme-portable/dsd-fme/
|
||||
cp mbelib/build/cygmbe* dsd-fme-portable/dsd-fme/
|
||||
|
||||
@@ -127,6 +128,7 @@ if [ "$RELEASE" = "y" ]; then
|
||||
cp /bin/cygdbus* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygedit* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygexpat* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygfam* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygfdisk* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygffi* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygfftw3_threads* dsd-fme-portable/dsd-fme/
|
||||
@@ -150,6 +152,7 @@ if [ "$RELEASE" = "y" ]; then
|
||||
cp /bin/cyggomp* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cyggpg-error* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cyggpgme* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cyggsasl* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cyggsm* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cyggssapi_krb5* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cyggthread* dsd-fme-portable/dsd-fme/
|
||||
@@ -176,6 +179,7 @@ if [ "$RELEASE" = "y" ]; then
|
||||
cp /bin/cygncurses* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygnettle* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygnghttp* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygntlm* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygobjc* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygogg* dsd-fme-portable/dsd-fme/
|
||||
cp /bin/cygopus* dsd-fme-portable/dsd-fme/
|
||||
|
||||
@@ -17,6 +17,11 @@ if [ "$ANSWER" = "y" ]; then
|
||||
sudo pacman -Syu #always run a full update first, partial upgrades aren't supported in Arch -- including downloading dependencies, that may require an updated dependency, and breaks same dependency on another package, a.k.a, dependency hell
|
||||
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget
|
||||
|
||||
printf "Installing itpp 4.3.1 from Arch Strike --Tested on Arch 2023.08.01 and Manjaro XFCE 22.1.3 Minimal\n"
|
||||
wget https://mirror.archstrike.org/x86_64/archstrike/itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||
sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||
sudo rm -R itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||
|
||||
git clone https://github.com/lwvmobile/mbelib
|
||||
cd mbelib
|
||||
git checkout ambe_tones
|
||||
|
||||
@@ -19,6 +19,18 @@ if [ "$ANSWER" = "y" ]; then
|
||||
export LD_LIBRARY_PATH
|
||||
echo $LD_LIBRARY_PATH
|
||||
|
||||
#ITPP
|
||||
cd $cdir
|
||||
printf "Installing itpp 4.3.1 from source http://sourceforge.net/projects/itpp/files/latest/download?source=files\n Please wait!\n"
|
||||
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
|
||||
tar xjf itpp-latest.tar.bz2
|
||||
cd itpp-4.3.1/
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j $(nproc)
|
||||
make install
|
||||
|
||||
#MBELIB
|
||||
cd $cdir
|
||||
printf "Installing mbelib\n Please wait!\n"
|
||||
|
||||
@@ -12,7 +12,13 @@ read -p "Have you viewed the patent notice? y/N " ANSWER
|
||||
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
|
||||
if [ "$ANSWER" = "y" ]; then
|
||||
sudo apt update
|
||||
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncurses-dev libncurses6 libcodec2-dev
|
||||
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncurses-dev libncurses6 libcodec2-dev
|
||||
chkitpp=$(dpkg -l libitpp-dev 2>/dev/null | awk '/^ii/ {print 1} END {if (NR == 0) print 0}')
|
||||
if [ "$chkitpp" -lt "1" ];then
|
||||
printf "\nFor some reason, ITPP did NOT install!\nYou may have to manually install it.\n\n"
|
||||
read -p "Press Enter to exit..." x
|
||||
exit 1
|
||||
fi
|
||||
git clone https://github.com/lwvmobile/mbelib
|
||||
cd mbelib
|
||||
git checkout ambe_tones
|
||||
|
||||
@@ -12,7 +12,13 @@ read -p "Have you viewed the patent notice? y/N " ANSWER
|
||||
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
|
||||
if [ "$ANSWER" = "y" ]; then
|
||||
sudo apt update
|
||||
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncursesw5-dev libcodec2-dev
|
||||
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev libcodec2-dev
|
||||
chkitpp=$(dpkg -l libitpp-dev 2>/dev/null | awk '/^ii/ {print 1} END {if (NR == 0) print 0}')
|
||||
if [ "$chkitpp" -lt "1" ];then
|
||||
printf "\nFor some reason, ITPP did NOT install!\nYou may have to manually install it.\n\n"
|
||||
read -p "Press Enter to exit..." x
|
||||
exit 1
|
||||
fi
|
||||
git clone https://github.com/lwvmobile/mbelib
|
||||
cd mbelib
|
||||
git checkout ambe_tones
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
-f2 P25P2 (may need to specify wacn/sys/nac manually if traffic channel)
|
||||
-fx X2-TDMA
|
||||
-fy YSF
|
||||
-fz M17 Stream
|
||||
-fz M17 Audio
|
||||
```
|
||||
|
||||
## Conventional Frequency Scanner
|
||||
@@ -190,7 +190,7 @@ y - toggle scanner (needs either rtl input, or rigctl connection)
|
||||
1 - Toggle Voice Synthesis in TDMA Slot 1 or FDMA Conventional Voice
|
||||
2 - Toggle Voice Synthesis in TDMA Slot 2
|
||||
w - Toggle Trunking/Playback White List (Allow A Groups Only) / Black List (Block B or DE groups only) Mode
|
||||
T - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25, NXDN)
|
||||
g - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25, NXDN)
|
||||
u - Toggle Trunking Tuning to Private Calls (DMR T3, Cap+, P25)
|
||||
d - Toggle Trunking Tuning to Data Calls (DMR T3, NXDN)
|
||||
e - Toggle Trunking Tuning to Encrypted Calls (P25)
|
||||
@@ -222,12 +222,6 @@ N - Toggle Passband Filter on Analog Voice or Raw Signal Monitoring
|
||||
|
||||
v - Cycle RTL Input Volume Multiplier 1X, 2X, 3X (lower v)
|
||||
|
||||
g - Manually decrease RTL gain
|
||||
G - Manually increase RTL gain
|
||||
|
||||
{ - Manually decrease PPM
|
||||
} - Manually increase PPM
|
||||
|
||||
```
|
||||
|
||||
## Sending Audio to a Icecast 2 Server via FFmpeg (Windows)
|
||||
@@ -317,3 +311,70 @@ On the remote side where dsd-fme is, use the TCP input:
|
||||
`dsd-fme -i tcp:192.168.7.8:1234 -N`
|
||||
|
||||
Note: As always, when using rtl_fm, play with `-l` and `-g` to find a good squelch and gain values for your environment - you will know if the squelch is the right value when `input.wav` does not increase in size in the absence of useful signal on the monitored frequency.
|
||||
|
||||
## Using the Experimental M17 Encoder (and Decoder)
|
||||
|
||||
You can use the experimental M17 Voice Stream and Packet encoder with the following examples.
|
||||
|
||||
Encode/Decode Local Feedback Voice Stream (Single Session):
|
||||
|
||||
`dsd-fme -fZ -i pulse -o pulse -M M17:3:SP5WWP:BROADCAST:48000`
|
||||
|
||||
Using the `-8` option (raw audio monitor) will instead send encoded raw audio to output instead of decoded audio. For example,
|
||||
|
||||
To send a voice stream out of stdout into SOCAT TCP to a second/remote DSD-FME session to decode:
|
||||
|
||||
Encode (Upper Z):
|
||||
|
||||
`dsd-fme -fZ -8 -i pulse -M M17:9:LWVMOBILE:DSD-FME:48000 -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
|
||||
|
||||
Decode (Lower z):
|
||||
|
||||
`dsd-fme -fz -i tcp:192.168.7.8:7355`
|
||||
|
||||
NOTE: When using tcp input for M17 decoding, DSD-FME will keep the TCP connection alive with constant retries, so no need to restart the decoding session on connection failure.
|
||||
|
||||
SMS Packet Encoder (Text Only):
|
||||
|
||||
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it."`
|
||||
|
||||
SMS Packet Encoder (over SOCAT TCP):
|
||||
|
||||
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it and SOCAT to send it over TCP." -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
|
||||
|
||||
Many combinations of input and output can be used as well, and using features like raw audio saving with `-6 m17str.wav` will save a wav file of your encoded audio to be replayed later with `-i m17str.wav`
|
||||
Also note, features function similarly, like -Z for payload, and input options include pulse, oss, stdin, rtl, tcp, etc, using those as the mic in for voice encoding. With the right setup of input/output/input using SOCAT TCP pipes, it is conceivable to use DSD-FME to convert analog audio from NFM into M17 audio, or even change another digital format supported by DSD-FME into M17 audio, if desired (purely for research and fun purposes). Ncurses terminal will function with internal encoder/decoder and wav file creation, with toggle encode/tx with `\` key, but will not function with stdout properly.
|
||||
|
||||
The `-M` switch is used to specify user M17 variables, the string is in the format of M17:CAN:SRC:DST:INPUT_RATE.
|
||||
CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.
|
||||
BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'
|
||||
Input Rate Default is 48000; Use Multiples of 8000 up to 48000.
|
||||
Values not entered into the M17: string are set to default values. M17:7:DSD-FME:DSD-FME:48000
|
||||
|
||||
Fun Example Transmuting OTA Analog Things into M17:
|
||||
|
||||
Start up in sequence.
|
||||
|
||||
Terminal #1 (RTL Input):
|
||||
`dsd-fme -fZ -8 -M M17:1:ANALOG:M17:48000 -i rtl:0:162.425M -o - | socat stdio tcp-listen:7310,forever,reuseaddr`
|
||||
|
||||
Terminal #2:
|
||||
`dsd-fme -fz -i tcp:127.0.0.1:7310`
|
||||
|
||||
Fun Example Transmuting OTA Digital Things into M17:
|
||||
|
||||
Start up all three of these in sequence.
|
||||
|
||||
Terminal #1 (connecting to SDR++):
|
||||
`dsd-fme -fi -i tcp:127.0.0.1:7355 -o - | socat stdio tcp-listen:7300,forever,reuseaddr`
|
||||
|
||||
Terminal #2:
|
||||
`dsd-fme -fZ -8 -M M17:2:NXDN:M17:8000 -i tcp:127.0.0.1:7300 -o - | socat stdio tcp-listen:7301,forever,reuseaddr`
|
||||
|
||||
Terminal #3:
|
||||
`dsd-fme -fz -i tcp:127.0.0.1:7301`
|
||||
|
||||
Note: Transmuting only carries over voice when audio is output, and will not carry any ID values, or any other data payloads.
|
||||
This mode is only meant for educational or fun purposes, please do not retransmit stuff that doesn't belong to you.
|
||||
|
||||
Information on this section will be updated as features are added or modified, etc. If you need help with a particular function and can't find a suitable answer, always open a new issue here, or make contact on the radio reference forum for additional examples, guidance, ideas, help, etc.
|
||||
@@ -43,9 +43,9 @@ Debian/Mint/Ubuntu/Pi
|
||||
```
|
||||
sudo apt update
|
||||
#Ubuntu 22.04 lts / Debian 11 and lower
|
||||
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libcodec2-dev
|
||||
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libcodec2-dev
|
||||
#Ubuntu 24.04 lts / Debian 12
|
||||
#sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncurses-dev libncurses6 libcodec2-dev
|
||||
#sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncurses-dev libncurses6 libcodec2-dev
|
||||
```
|
||||
|
||||
Fedora 36/37 -- from https://github.com/lwvmobile/dsd-fme/issues/99
|
||||
@@ -60,6 +60,8 @@ Arch -- https://github.com/lwvmobile/dsd-fme/issues/153 and https://github.com/l
|
||||
```
|
||||
sudo pacman -Syu
|
||||
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget rtl-sdr
|
||||
wget https://mirror.archstrike.org/x86_64/archstrike/itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||
sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||
```
|
||||
|
||||
## Headless Ubuntu Server/Pi
|
||||
@@ -67,7 +69,24 @@ sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel
|
||||
If running headless, swap out pavucontrol for pulsemixer, and also install pulseaudio as well. Attempting to install pavucontrol in a headless environment may attempt to install a minimal desktop environment. Note: Default behavior of pulseaudio in a headless environment may be to be muted, so check by opening pulsemixer and unmuting and routing audio appropriately.
|
||||
|
||||
```
|
||||
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncursesw5-dev pulsemixer pulseaudio libcodec2-dev
|
||||
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev pulsemixer pulseaudio libcodec2-dev
|
||||
```
|
||||
|
||||
### Build and Install ITPP - ONLY IF NOT IN REPO!!
|
||||
|
||||
```
|
||||
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
|
||||
tar xjf itpp*
|
||||
#if you can't cd into this folder, double check folder name first
|
||||
cd itpp-4.3.1
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
cd ..
|
||||
cd ..
|
||||
```
|
||||
|
||||
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Build Version: AW 2026-30-g43d63b2
|
||||
Build Version: AW 2025-182-g24c1dd9
|
||||
MBElib Version: 1.3.4
|
||||
CODEC2 Support Enabled
|
||||
|
||||
@@ -23,6 +23,8 @@ Input/Output options:
|
||||
rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)
|
||||
tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)
|
||||
tcp:192.168.7.5:7355 for custom address and port
|
||||
m17udp for M17 UDP/IP socket bind input (default host 127.0.0.1; default port 17000)
|
||||
m17udp:192.168.7.8:17001 for M17 UDP/IP bind input (Binding Address and Port
|
||||
filename.bin for OP25/FME capture bin files
|
||||
filename.wav for 48K/1 wav files (SDR++, GQRX)
|
||||
filename.wav -s 96000 for 96K/1 wav files (DSDPlus)
|
||||
@@ -35,6 +37,8 @@ Input/Output options:
|
||||
null for no audio output
|
||||
udp for UDP socket blaster output (default host 127.0.0.1; default port 23456)
|
||||
udp:192.168.7.8:23470 for UDP socket blaster output (Target Address and Port
|
||||
m17udp for M17 UDP/IP socket blaster output (default host 127.0.0.1; default port 17000)
|
||||
m17udp:192.168.7.8:17001 for M17 UDP/IP blaster output (Target Address and Port
|
||||
-d <dir> Create mbe data files, use this directory (TDMA version is experimental)
|
||||
-r <files> Read/Play saved mbe data from file(s)
|
||||
-g <float> Audio Digital Output Gain (Default: 0 = Auto; )
|
||||
@@ -50,8 +54,6 @@ Input/Output options:
|
||||
-w <file> Output synthesized speech to a single static .wav file. (Do not use with -P Per Call Switch)
|
||||
-P Enable Per Call WAV file saving. (Do not use with -w filename.wav single wav file switch)
|
||||
(Per Call works with everything now and doesn't require ncurses terminal!)
|
||||
-j <string> Custom Tag(s), colon delimited, to append to Per Call decoded .wav files.
|
||||
-j TGT_LAPD:TAG_FIRE:FREQ_851987500 (Total 3 Tags, Maximum Letter Length = 100)
|
||||
-a Enable Call Alert Beep
|
||||
(Warning! Might be annoying.)
|
||||
-J <file> Specify Filename for Event Log Output.
|
||||
@@ -79,6 +81,26 @@ RTL-SDR options:
|
||||
Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T
|
||||
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2
|
||||
|
||||
Encoder options:
|
||||
-fZ M17 Stream Voice Encoder
|
||||
Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i pulse -6 m17signal.wav -8 -N 2> m17encoderlog.txt
|
||||
Run M17 Encoding, listening to pulse audio server, with internal decode/playback and output to 48k/1 wav file
|
||||
|
||||
Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i tcp -o pulse -8 -N 2> m17encoderlog.txt
|
||||
Run M17 Encoding, listening to default tcp input, without internal decode/playback and output to 48k/1 analog output device
|
||||
|
||||
-fP M17 Packet Encoder
|
||||
Example: dsd-fme -fP -M M17:9:DSD-FME:LWVMOBILE -6 m17pkt.wav -8 -S 'Hello World'
|
||||
|
||||
-fB M17 BERT Encoder
|
||||
Example: dsd-fme -fB -M M17:9:DSD-FME:LWVMOBILE -6 m17bert.wav -8
|
||||
|
||||
-M M17 Encoding User Configuration String: M17:CAN:SRC:DST:INPUT_RATE:VOX (see examples above).
|
||||
CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.
|
||||
BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'
|
||||
Input Rate Default is 48000; Use Multiples of 8000 up to 48000.
|
||||
VOX Enabled on 1; (Default = 0)
|
||||
Values not entered into the M17: string are set to default values.
|
||||
Decoder options:
|
||||
-fa Auto Detection
|
||||
-fA Passive Analog Audio Monitor
|
||||
@@ -90,6 +112,7 @@ Decoder options:
|
||||
-fx Decode only X2-TDMA
|
||||
-fy Decode only YSF
|
||||
-fz Decode only M17*
|
||||
-fU Decode only M17 UDP/IP Frame***
|
||||
-fi Decode only NXDN48* (6.25 kHz) / IDAS*
|
||||
-fn Decode only NXDN96* (12.5 kHz)
|
||||
-fp Decode only ProVoice*
|
||||
@@ -105,16 +128,15 @@ Decoder options:
|
||||
-xx Expect non-inverted X2-TDMA signal
|
||||
-xr Expect inverted DMR signal
|
||||
-xd Expect inverted ICOM dPMR signal
|
||||
-xn Expect inverted NXDN/DCR/IDAS signal
|
||||
-xz Expect inverted M17 signal
|
||||
|
||||
* denotes frame types that cannot be auto-detected.
|
||||
** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.
|
||||
*** configure UDP Input with -i m17:127.0.0.1:17000
|
||||
|
||||
Advanced Decoder options:
|
||||
-X <hex> Manually Set P2 Parameters (WACN, SYSID, CC/NAC)
|
||||
(-X BEE00ABC123)
|
||||
-_ <dec> Manually Set NXDN PN95 Dibit Scrambler Seed (1-511)
|
||||
(-* 261)
|
||||
-D <dec> Manually Set TIII DMR Location Area n bit len (0-10)(10 max)
|
||||
(Value defaults to max n bit value for site model size)
|
||||
(Setting 0 will show full Site ID, no area/subarea)
|
||||
@@ -128,9 +150,10 @@ Advanced Decoder options:
|
||||
Use this feature to allow MAC_SIGNAL even if CRC errors.
|
||||
-F Relax DMR RAS/CRC CSBK/DATA Pass/Fail
|
||||
Enabling on some systems could lead to bad channel assignments/site data decoding if bad or marginal signal
|
||||
-F Relax M17 LSF/PKT CRC Error Checking
|
||||
|
||||
-b <dec> Manually Enter Basic Privacy Key (Decimal Value of Key Number)
|
||||
(NOTE: This used to be the 'K' option!)
|
||||
(NOTE: This used to be the 'K' option!
|
||||
|
||||
-H <hex> Manually Enter Hytera 10/32/64 Char Basic Privacy Hex Key (see example below)
|
||||
Encapulate in Single Quotation Marks; Space every 16 chars.
|
||||
@@ -138,7 +161,7 @@ Advanced Decoder options:
|
||||
-H '736B9A9C5645288B 243AD5CB8701EF8A'
|
||||
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
|
||||
|
||||
-H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)
|
||||
-H <hex> Manually Enter AES-128 or AES-256 Hex Key (see example below)
|
||||
Encapulate in Single Quotation Marks; Space every 16 chars.
|
||||
-H '736B9A9C5645288B 243AD5CB8701EF8A'
|
||||
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
|
||||
@@ -149,28 +172,14 @@ Advanced Decoder options:
|
||||
|
||||
-2 <hex> Manually Enter and Enforce TYT 16-bit BP Key Value (DMR) (Hex Value)
|
||||
|
||||
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) 128 or 256 bit AP Hex Key (see example below)
|
||||
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) AP Hex Key (see example below)
|
||||
Encapulate in Single Quotation Marks; Space every 16 chars.
|
||||
-! '736B9A9C5645288B 243AD5CB8701EF8A'
|
||||
-! '1111111111111111 1111111111111111 1111111111111111 1111111111111111'
|
||||
|
||||
-@ <hex> Manually Enter and Enforce Retevis Advanced Privacy (RC2) 128 or 256 bit AP Hex Key (see example below)
|
||||
Encapulate in Single Quotation Marks; Space every 16 chars.
|
||||
-@ '736B9A9C5645288B 243AD5CB8701EF8A'
|
||||
-@ '1122334455667788 99AABBCCDDEEFF11 1122334455667788 99AABBCCDDEEFF11'
|
||||
|
||||
-5 <hex> Manually Enter and Enforce TYT Enhanced Privacy (AES-128) EP Hex Key (see example below)
|
||||
Encapulate in Single Quotation Marks; Space every 16 chars.
|
||||
-5 '736B9A9C5645288B 243AD5CB8701EF8A'
|
||||
|
||||
-+ <hex> Manually Enter and Enforce Baofeng Advanced Privacy (PC5-128 or PC5-256) AP Hex Key (see example below)
|
||||
Encapulate in Single Quotation Marks; Space every 16 chars.
|
||||
-+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC'
|
||||
-+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC EF01EF01EF01EF01 EF01EF01EF01EF01'
|
||||
|
||||
-^ <hex> Manually Enter and Enforce Connect Systems 72-bit (9-byte) Extended Encryption Hex Key (see example below)
|
||||
-^ 3BBE782C0430008271
|
||||
|
||||
-9 <dec> Manually Enter and Enforce Kenwood 15-bit Scrambler Key Value (DMR) (Dec Value)
|
||||
|
||||
-A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value)
|
||||
@@ -182,8 +191,6 @@ Advanced Decoder options:
|
||||
-S 49:123456789ABC80
|
||||
For Example, enter 49-bit Keystream (MBP 70) as:
|
||||
-S 49:ED0AED4AED4AED4A
|
||||
For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:
|
||||
-S 49:11AA22BB33CC44
|
||||
|
||||
-k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.
|
||||
Only supports NXDN, DMR Basic Privacy (decimal value).
|
||||
@@ -196,8 +203,6 @@ Advanced Decoder options:
|
||||
|
||||
-0 Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR)
|
||||
|
||||
-M <hex> Force Entered Alg ID Value over Missing PI header/LE Encryption Identifiers (DMR)
|
||||
|
||||
-3 Disable DMR Late Entry Encryption Identifiers (VC6 Single Burst)
|
||||
Note: Disable this if false positives on Voice ENC occur.
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*
|
||||
* Code adapted from http://www.aqdi.com/golay.htm
|
||||
*
|
||||
* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs
|
||||
* expected from the P25 transmissions that I have tested. This implementation does work.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
/**
|
||||
* Code taken and adapted from www.eccpage.com/rs.c
|
||||
* Credit goes to Mr Simon Rockliff.
|
||||
*
|
||||
* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs
|
||||
* expected from the P25 transmissions that I have tested. This implementation does work.
|
||||
*/
|
||||
|
||||
/* This program is an encoder/decoder for Reed-Solomon codes. Encoding is in
|
||||
|
||||
173
include/dsd.h
173
include/dsd.h
@@ -59,9 +59,8 @@
|
||||
#include "p25p1_heuristics.h"
|
||||
|
||||
//OSS support
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__)
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
|
||||
#include <pulse/pulseaudio.h> //PULSE AUDIO
|
||||
#include <pulse/simple.h> //PULSE AUDIO
|
||||
#include <pulse/error.h> //PULSE AUDIO
|
||||
@@ -173,8 +172,8 @@ typedef struct pa_devicelist
|
||||
typedef struct
|
||||
{
|
||||
unsigned long int groupNumber;
|
||||
char groupMode[3];
|
||||
char groupName[100];
|
||||
char groupMode[8]; //char *?
|
||||
char groupName[50];
|
||||
} groupinfo;
|
||||
|
||||
|
||||
@@ -324,7 +323,6 @@ typedef struct
|
||||
char wav_out_file[1024];
|
||||
char wav_out_fileR[1024];
|
||||
char wav_out_file_raw[1024];
|
||||
char wav_custom_tag[100];
|
||||
char symbol_out_file[1024];
|
||||
char lrrp_out_file[1024];
|
||||
char event_out_file[1024];
|
||||
@@ -355,23 +353,25 @@ typedef struct
|
||||
int uvquality;
|
||||
int inverted_x2tdma;
|
||||
int inverted_dmr;
|
||||
int inverted_nxdn;
|
||||
int mod_threshold;
|
||||
int ssize;
|
||||
int msize;
|
||||
int playfiles;
|
||||
int m17encoder;
|
||||
int m17encoderbrt;
|
||||
int m17encoderpkt;
|
||||
int m17decoderip;
|
||||
int delay;
|
||||
int use_cosine_filter;
|
||||
int unmute_encrypted_p25;
|
||||
int rtl_dev_index;
|
||||
int rtl_gain_value;
|
||||
int rtl_gain_actual;
|
||||
int rtl_squelch_level;
|
||||
int rtl_volume_multiplier;
|
||||
int rtl_udp_port;
|
||||
int rtl_bandwidth;
|
||||
int rtl_started;
|
||||
long int rtl_rms;
|
||||
long int rtl_pwr;
|
||||
int monitor_input_audio;
|
||||
int analog_only;
|
||||
int pulse_raw_rate_in;
|
||||
@@ -414,6 +414,7 @@ typedef struct
|
||||
short int aggressive_framesync;
|
||||
|
||||
int frame_m17;
|
||||
int inverted_m17;
|
||||
|
||||
FILE *symbolfile;
|
||||
int call_alert;
|
||||
@@ -430,6 +431,12 @@ typedef struct
|
||||
int udp_portno;
|
||||
char udp_hostname[1024];
|
||||
|
||||
//M17 UDP for IP frame output
|
||||
int m17_use_ip; //if enabled, open UDP and broadcast IP frame
|
||||
int m17_portno; //default is 17000
|
||||
int m17_udp_sock; //actual UDP socket for M17 to send to
|
||||
char m17_hostname[1024];
|
||||
|
||||
//tcp socket for SDR++, etc
|
||||
int tcp_sockfd;
|
||||
int tcp_portno;
|
||||
@@ -636,7 +643,7 @@ typedef struct
|
||||
unsigned long long int K2;
|
||||
unsigned long long int K3;
|
||||
unsigned long long int K4;
|
||||
uint8_t forced_alg_id;
|
||||
int M;
|
||||
int menuopen;
|
||||
|
||||
//AES Key Segments
|
||||
@@ -872,7 +879,7 @@ typedef struct
|
||||
//trunking group and lcn freq list
|
||||
long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully
|
||||
long int trunk_chan_map[0xFFFF]; //NXDN - 10 bit; P25 - 16 bit; DMR up to 12 bit (standard TIII)
|
||||
groupinfo * group_array;
|
||||
groupinfo group_array[0x3FF]; //max supported by Cygwin is 3FFF, I hope nobody actually tries to import this many groups
|
||||
unsigned int group_tally; //tally number of groups imported from CSV file for referencing later
|
||||
int lcn_freq_count;
|
||||
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
|
||||
@@ -891,7 +898,6 @@ typedef struct
|
||||
uint8_t nxdn_sacch_frame_segcrc[4];
|
||||
uint8_t nxdn_alias_block_number;
|
||||
char nxdn_alias_block_segment[4][4][8];
|
||||
uint16_t nxdn_pn95_seed;
|
||||
|
||||
//site/srv/cch info
|
||||
char nxdn_location_category[14];
|
||||
@@ -946,11 +952,15 @@ typedef struct
|
||||
//M17 Storage
|
||||
uint8_t m17_lsf[360];
|
||||
uint8_t m17_pkt[850];
|
||||
uint8_t m17_pbc_ct; //pbc packet counter
|
||||
uint8_t m17_str_dt; //stream contents
|
||||
|
||||
unsigned long long int m17_dst;
|
||||
unsigned long long int m17_src;
|
||||
uint8_t m17_can; //can value that was decoded from signal
|
||||
int m17_can_en; //can value supplied to the encoding side
|
||||
int m17_rate; //sampling rate for audio input
|
||||
int m17_vox; //vox enabled via PWR value
|
||||
|
||||
char m17_dst_csd[20];
|
||||
char m17_src_csd[20];
|
||||
@@ -958,22 +968,18 @@ typedef struct
|
||||
char m17_src_str[50];
|
||||
char m17_dst_str[50];
|
||||
|
||||
uint8_t m17_meta[16]; //packed meta
|
||||
uint8_t m17_aes_iv[16]; //aes iv
|
||||
uint8_t m17_enc; //enc type
|
||||
uint8_t m17_enc_st; //scrambler or data subtye
|
||||
|
||||
char m17_text_string[1024];
|
||||
char m17_gnss_string[1024];
|
||||
char m17_data_string[1024];
|
||||
char m17_meta_string[1024];
|
||||
|
||||
float m17_viterbi_err;
|
||||
uint8_t m17_meta[16]; //packed meta
|
||||
uint8_t m17_enc; //enc type
|
||||
uint8_t m17_enc_st; //scrambler or data subtye
|
||||
int m17encoder_tx; //if TX (encode + decode) M17 Stream is enabled
|
||||
int m17encoder_eot; //signal if we need to send the EOT frame
|
||||
|
||||
//misc str storage
|
||||
char str50a[50];
|
||||
char str50b[50];
|
||||
char str50c[50];
|
||||
char m17dat[50]; //user supplied m17 data input string
|
||||
char m17sms[800]; //user supplied sms text string
|
||||
|
||||
//Codec2
|
||||
#ifdef USE_CODEC2
|
||||
@@ -985,9 +991,6 @@ typedef struct
|
||||
int tyt_ap;
|
||||
int tyt_bp;
|
||||
int tyt_ep;
|
||||
// retrevis rc2
|
||||
int retevis_ap;
|
||||
void *rc2_context;
|
||||
|
||||
//kenwood scrambler on DMR with forced application
|
||||
int ken_sc;
|
||||
@@ -995,12 +998,6 @@ typedef struct
|
||||
//anytone bp
|
||||
int any_bp;
|
||||
|
||||
//baofeng ap
|
||||
int baofeng_ap;
|
||||
|
||||
//connect systems ee
|
||||
int csi_ee;
|
||||
|
||||
//generic ks
|
||||
int straight_ks;
|
||||
int straight_mod;
|
||||
@@ -1017,6 +1014,13 @@ typedef struct
|
||||
//M17 Sync Patterns
|
||||
#define M17_LSF "11113313"
|
||||
#define M17_STR "33331131"
|
||||
//alternating with last symbol opposite of first symbol of LSF
|
||||
#define M17_PRE "31313131"
|
||||
#define M17_PIV "13131313"
|
||||
#define M17_PRE_LSF "3131313133331131" //Preamble + LSF
|
||||
#define M17_PIV_LSF "1313131311113313" //Preamble + LSF
|
||||
#define M17_BRT "31331111"
|
||||
#define M17_PKT "13113333"
|
||||
|
||||
#define FUSION_SYNC "31111311313113131131"
|
||||
#define INV_FUSION_SYNC "13333133131331313313"
|
||||
@@ -1186,7 +1190,7 @@ void openWavOutFileLR (dsd_opts * opts, dsd_state * state); //stereo wav file fo
|
||||
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state);
|
||||
SNDFILE * open_wav_file (char * dir, char * temp_filename, uint16_t sample_rate, uint8_t ext);
|
||||
SNDFILE * close_wav_file(SNDFILE * wav_file);
|
||||
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, char * custom_tag, Event_History_I * event_struct);
|
||||
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, Event_History_I * event_struct);
|
||||
SNDFILE * close_and_delete_wav_file(SNDFILE * wav_file, char * wav_out_filename);
|
||||
void openSymbolOutFile (dsd_opts * opts, dsd_state * state);
|
||||
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state);
|
||||
@@ -1240,6 +1244,13 @@ void processDSTAR_HD (dsd_opts * opts, dsd_state * state); //DSTAR Header
|
||||
void processDSTAR_SD (dsd_opts * opts, dsd_state * state, uint8_t * sd); //DSTAR Slow Data
|
||||
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
|
||||
void processM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR)
|
||||
void processM17PKT(dsd_opts * opts, dsd_state * state); //M17 (PKT)
|
||||
void processM17LSF(dsd_opts * opts, dsd_state * state); //M17 (LSF)
|
||||
void processM17IPF(dsd_opts * opts, dsd_state * state); //M17 (IPF)
|
||||
void encodeM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR) encoder
|
||||
void encodeM17BRT(dsd_opts * opts, dsd_state * state); //M17 (BRT) encoder
|
||||
void encodeM17PKT(dsd_opts * opts, dsd_state * state); //M17 (PKT) encoder
|
||||
void decodeM17PKT(dsd_opts * opts, dsd_state * state, uint8_t * input, int len); //M17 (PKT) decoder
|
||||
void processP2(dsd_opts * opts, dsd_state * state); //P2
|
||||
void processTSBK(dsd_opts * opts, dsd_state * state); //P25 Trunking Single Block
|
||||
void processMPDU(dsd_opts * opts, dsd_state * state); //P25 Multi Block PDU (SAP 0x61 FMT 0x15 or 0x17 for Trunking Blocks)
|
||||
@@ -1265,25 +1276,25 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state);
|
||||
uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c);
|
||||
void ncursesClose ();
|
||||
|
||||
//NXDN Functions Start Here
|
||||
//new NXDN Functions start here!
|
||||
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
||||
void nxdn_pn95_dibit_scrambler(dsd_state * state, uint8_t * dibits, int len);
|
||||
void nxdn_descramble (uint8_t dibits[], int len);
|
||||
//nxdn deinterleaving/depuncturing functions
|
||||
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
|
||||
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
|
||||
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
|
||||
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
|
||||
//type-d 'idas' deinterleaving/depuncturing functions
|
||||
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction);
|
||||
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
|
||||
//DCR Mode
|
||||
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
|
||||
void nxdn_deperm_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
|
||||
//MT and Voice
|
||||
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
|
||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf);
|
||||
|
||||
//All-in-One NXDN Soft Decision Viterbi Function based on libM17
|
||||
uint32_t nxdn_soft_decision_viterbi(uint8_t * bits, const uint16_t * interleave, uint8_t * puncture, int d_len, int p_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
|
||||
//NXDN Conventional and Type-C
|
||||
void nxdn_facch1(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t frame);
|
||||
void nxdn_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
|
||||
void nxdn_cac(dsd_opts * opts, dsd_state * state, uint8_t * bits);
|
||||
void nxdn_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
|
||||
//type-d 'idas'
|
||||
void idas_scch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t direction);
|
||||
void idas_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
|
||||
//Japanese DCR
|
||||
void dcr_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
|
||||
void dcr_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t lich);
|
||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
|
||||
//Osmocom OP25 12 Rate Trellis Decoder (for NXDN, M17, YSF, etc)
|
||||
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len);
|
||||
|
||||
//OP25 NXDN CRC functions
|
||||
int load_i(const uint8_t val[], int len);
|
||||
@@ -1292,18 +1303,15 @@ uint16_t crc12f(const uint8_t buf[], int len);
|
||||
uint16_t crc15(const uint8_t buf[], int len);
|
||||
uint16_t crc16cac(const uint8_t buf[], int len);
|
||||
uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
|
||||
uint32_t nxdn_message_crc32(uint8_t * input, int len);
|
||||
|
||||
//YSF Soft Decision Viterbi
|
||||
uint32_t ysf_soft_decision_viterbi(uint8_t * dbuf, int d_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
|
||||
/* NXDN Convolution functions */
|
||||
void CNXDNConvolution_start(void);
|
||||
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
|
||||
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
|
||||
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
|
||||
void CNXDNConvolution_init();
|
||||
|
||||
//libM17 viterbi decoder
|
||||
|
||||
//libm17 magic soft decision based viterbi
|
||||
#define SYM_PER_PLD 184
|
||||
void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD]);
|
||||
void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2]);
|
||||
void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_PLD*2]);
|
||||
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len);
|
||||
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len);
|
||||
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos);
|
||||
@@ -1311,9 +1319,6 @@ uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len);
|
||||
void viterbi_reset(void);
|
||||
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2);
|
||||
|
||||
//libm17 based soft decision viterbi for variable len and new punctures
|
||||
void slice_symbols_to_len(uint16_t * out, const float * inp, int len);
|
||||
|
||||
//keeping these
|
||||
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
|
||||
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||
@@ -1322,29 +1327,16 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
char * NXDN_Call_Type_To_Str(uint8_t CallType);
|
||||
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
|
||||
void NXDN_Data_Call_Option_To_Str(uint8_t DataCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
|
||||
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
|
||||
//added these
|
||||
void NXDN_decode_Prop(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_Alias16(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void nxdn_sdcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
|
||||
void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
|
||||
//Type-D SCCH Message Decoder
|
||||
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
|
||||
void NXDN_decode_VCALL_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
uint32_t sjis_char_to_unicode(uint16_t sjis);
|
||||
uint32_t big5_char_to_unicode(uint16_t big5);
|
||||
int utf8_encode(int c, char *buf);
|
||||
|
||||
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
||||
|
||||
@@ -1384,7 +1376,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
||||
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
|
||||
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
|
||||
uint32_t viterbi_r34 (uint8_t * input, uint8_t * output);
|
||||
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18]); //simplier trellis decoder
|
||||
void beeper (dsd_opts * opts, dsd_state * state, int lr, int id, int ad, int len);
|
||||
void dmr_gateway_identifier (uint32_t source, uint32_t target); //translate special addresses
|
||||
|
||||
@@ -1406,11 +1398,10 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||
void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||
void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input);
|
||||
void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int type);
|
||||
void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot, int phase);
|
||||
void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src);
|
||||
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot);
|
||||
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input);
|
||||
void utf16_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input);
|
||||
void utf8_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input);
|
||||
uint8_t nmea_sentence_checker(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t slot, int len);
|
||||
|
||||
//"DMR STEREO"
|
||||
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
|
||||
@@ -1589,6 +1580,7 @@ void init_rrc_filter_memory();
|
||||
|
||||
//misc audio filtering for analog
|
||||
long int raw_rms(short *samples, int len, int step);
|
||||
long int raw_pwr(short *samples, int len, int step);
|
||||
void init_audio_filters(dsd_state * state);
|
||||
void lpf(dsd_state * state, short * input, int len);
|
||||
void hpf(dsd_state * state, short * input, int len);
|
||||
@@ -1618,9 +1610,9 @@ int udp_socket_connect(dsd_opts * opts, dsd_state * state);
|
||||
int udp_socket_connectA(dsd_opts * opts, dsd_state * state);
|
||||
void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
|
||||
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
|
||||
|
||||
//15-bit Scrambler
|
||||
void pdu_scrambler_keystream_creation(uint8_t * ks, int lfsr, int len);
|
||||
int m17_socket_receiver(dsd_opts * opts, void * data);
|
||||
int udp_socket_connectM17(dsd_opts * opts, dsd_state * state);
|
||||
int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
|
||||
|
||||
//RC4 function prototypes
|
||||
void rc4_voice_decrypt (int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[]);
|
||||
@@ -1638,33 +1630,25 @@ void aes_cfb_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in,
|
||||
void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
|
||||
void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
|
||||
|
||||
//Tytera / Retevis / Anytone / Kenwood / Misc DMR Encryption Modes
|
||||
//Tytera Encryption Modes
|
||||
void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int fnum);
|
||||
void tyt_ep_aes_keystream_creation(dsd_state * state, char * input);
|
||||
void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input);
|
||||
void retevis_rc2_keystream_creation(dsd_state *state, char *input);
|
||||
|
||||
//Misc Other Encryption Modes
|
||||
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input);
|
||||
void anytone_bp_keystream_creation(dsd_state * state, char * input);
|
||||
void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input);
|
||||
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]);
|
||||
void straight_mod_xor_keystream_creation(dsd_state * state, char * input);
|
||||
|
||||
//Kirisun
|
||||
uint32_t kirisun_lfsr(unsigned long long int mi);
|
||||
void kirisun_adv_keystream_creation(dsd_state *state);
|
||||
void kirisun_uni_keystream_creation(dsd_state *state);
|
||||
|
||||
//Hytera Enhanced
|
||||
void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value);
|
||||
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len);
|
||||
void hytera_enhanced_alg_refresh(dsd_state * state);
|
||||
|
||||
//LFSR to expand either a DMR 32-bit or P25/NXDN 64-bit MI into a 128-bit IV for AES
|
||||
void lfsr_64_to_128(uint8_t * iv);
|
||||
void LFSR128(dsd_state * state);
|
||||
void LFSR128n(dsd_state * state);
|
||||
void LFSR128d(dsd_state * state);
|
||||
void LFSR128npdu(dsd_state * state);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -1677,7 +1661,7 @@ void cleanup_rtlsdr_stream();
|
||||
int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
|
||||
void rtlsdr_sighandler();
|
||||
void rtl_dev_tune(dsd_opts * opts, long int frequency);
|
||||
long int rtl_return_rms();
|
||||
long int rtl_return_pwr();
|
||||
void rtl_clean_queue();
|
||||
#endif
|
||||
|
||||
@@ -1688,9 +1672,6 @@ int ez_rs28_facch (int payload[156], int parity[114]); //ezpwd bridge for FME
|
||||
int ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
|
||||
int isch_lookup (uint64_t isch); //isch map lookup
|
||||
|
||||
//Phase 1 NID Check
|
||||
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
|
||||
// 02110-1301, USA.
|
||||
|
||||
static const uint16_t PERM_12_5[] = {
|
||||
static const uint8_t PERM_12_5[] = {
|
||||
0,12,24,36,48,
|
||||
1,13,25,37,49,
|
||||
2,14,26,38,50,
|
||||
@@ -31,7 +31,7 @@ static const uint16_t PERM_12_5[] = {
|
||||
11,23,35,47,59
|
||||
};
|
||||
|
||||
static const uint16_t PERM_16_9[] = {
|
||||
static const uint8_t PERM_16_9[] = {
|
||||
0, 16, 32, 48, 64, 80, 96, 112, 128,
|
||||
1, 17, 33, 49, 65, 81, 97, 113, 129,
|
||||
2, 18, 34, 50, 66, 82, 98, 114, 130,
|
||||
|
||||
26
include/p25p1_check_nid.h
Normal file
26
include/p25p1_check_nid.h
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
#ifndef P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
|
||||
#define P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks a NID value, returns the fixed NAC, DUID and also an indication if it failed to decode the NID.
|
||||
* \param bch_code Input. An array to the 63 bytes, each containing one bit of the NID. This includes the
|
||||
* NAC (12 bits), DUID (4 bits) and 47 bits of BCH parity.
|
||||
* \param new_nac Output. An address where to store the calculated NAC value after error correction. Should
|
||||
* be large enough to accommodate for an integer.
|
||||
* \param new_duid Output. An address where to store the calculated DUID value after error correction. Should
|
||||
* be able to accommodate 3 chars.
|
||||
* \param parity Input. The parity value read.
|
||||
* \return 0 if there were errors processing the NID.
|
||||
*/
|
||||
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
|
||||
147
include/pc5.h
147
include/pc5.h
@@ -1,147 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef BAOFENG_AP
|
||||
#define BAOFENG_AP
|
||||
|
||||
#define nbround 254
|
||||
#define n1 264
|
||||
|
||||
#define UNUSEDPC5(x) ((void)x)
|
||||
|
||||
/* Structure holding all PC5 state variables */
|
||||
typedef struct {
|
||||
short bits[49], temp[49];
|
||||
uint8_t ptconvert;
|
||||
uint8_t convert[7];
|
||||
uint8_t perm[16][256];
|
||||
uint8_t new1[256];
|
||||
uint8_t array[49];
|
||||
uint8_t array2[49];
|
||||
uint8_t decal[nbround];
|
||||
uint8_t rngxor[nbround][3];
|
||||
uint8_t rngxor2[nbround][3];
|
||||
uint8_t rounds;
|
||||
uint8_t tab[256];
|
||||
uint8_t inv[256];
|
||||
uint8_t permut[3][3];
|
||||
uint64_t bb;
|
||||
uint64_t x;
|
||||
uint8_t tot[3];
|
||||
uint8_t l[2][3], r[2][3];
|
||||
uint8_t y, totb;
|
||||
uint32_t result;
|
||||
uint8_t xyz, count;
|
||||
uint8_t keys[16];
|
||||
unsigned char array_arc4[256];
|
||||
int i_arc4, j_arc4;
|
||||
int x1, x2, i;
|
||||
unsigned char h2[n1];
|
||||
unsigned char h1[n1*3];
|
||||
uint8_t numbers[25];
|
||||
} PC5Context;
|
||||
|
||||
/* Global context accessible from all .c files */
|
||||
extern PC5Context ctxpc5;
|
||||
|
||||
/* Public API */
|
||||
void create_keys_pc5(PC5Context *ctxpc5, unsigned char key1[], size_t size1);
|
||||
void pc5encrypt(PC5Context *ctxpc5);
|
||||
void pc5decrypt(PC5Context *ctxpc5);
|
||||
void binhexpc5(PC5Context *ctxpc5, short *z, int length);
|
||||
void hexbinpc5(PC5Context *ctxpc5, short *q, uint8_t w, uint8_t hex);
|
||||
static void u64_to_bytes_be_pc5(uint64_t val, unsigned char *out);
|
||||
|
||||
/* Encrypt a 49-bit frame using PC5 algorithm (original flow) */
|
||||
static void encrypt_frame_49_pc5(short frame_bits_in[49]) {
|
||||
// Only process first 24 bits for encryption, rest are XORed
|
||||
ctxpc5.ptconvert = 0;
|
||||
binhexpc5(&ctxpc5, frame_bits_in, 24);
|
||||
|
||||
uint8_t convert[6];
|
||||
|
||||
// Store first 3 bytes in convert
|
||||
for (int i = 0; i < 3; i++) convert[i] = ctxpc5.convert[i];
|
||||
|
||||
// Split each byte into two 4-bit nibbles
|
||||
ctxpc5.convert[0] = convert[0] >> 4;
|
||||
ctxpc5.convert[1] = convert[0] & 0xF;
|
||||
ctxpc5.convert[2] = convert[1] >> 4;
|
||||
ctxpc5.convert[3] = convert[1] & 0xF;
|
||||
ctxpc5.convert[4] = convert[2] >> 4;
|
||||
ctxpc5.convert[5] = convert[2] & 0xF;
|
||||
|
||||
pc5encrypt(&ctxpc5);
|
||||
|
||||
// Store encrypted nibbles back in convert
|
||||
for (int i = 0; i < 6; i++) convert[i] = ctxpc5.convert[i];
|
||||
|
||||
// Recombine nibbles into bytes
|
||||
ctxpc5.convert[0] = (convert[0] << 4) + convert[1];
|
||||
ctxpc5.convert[1] = (convert[2] << 4) + convert[3];
|
||||
ctxpc5.convert[2] = (convert[4] << 4) + convert[5];
|
||||
|
||||
// Convert back to bits for first 24 bits
|
||||
for (int q = 0; q < 3; q++) {
|
||||
uint8_t w = (uint8_t)(q * 8);
|
||||
hexbinpc5(&ctxpc5, frame_bits_in, w, ctxpc5.convert[q]);
|
||||
}
|
||||
|
||||
// XOR the remaining bits (positions 24 to 48)
|
||||
for (int i = 24; i < 49; i++) {
|
||||
frame_bits_in[i] = (short)(frame_bits_in[i] ^ ctxpc5.numbers[i - 24]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 49; i++) ctxpc5.bits[i] = frame_bits_in[i];
|
||||
}
|
||||
|
||||
/* Decrypt a 49-bit frame using PC5 algorithm (original flow) */
|
||||
static void decrypt_frame_49_pc5(short frame_bits_in[49]) {
|
||||
// XOR the remaining bits (positions 24 to 48) first
|
||||
for (int i = 24; i < 49; i++) {
|
||||
frame_bits_in[i] = (short)(frame_bits_in[i] ^ ctxpc5.numbers[i - 24]);
|
||||
}
|
||||
|
||||
ctxpc5.ptconvert = 0;
|
||||
binhexpc5(&ctxpc5, frame_bits_in, 24);
|
||||
|
||||
uint8_t convert[6];
|
||||
|
||||
// Store first 3 bytes in convert
|
||||
for (int i = 0; i < 3; i++) convert[i] = ctxpc5.convert[i];
|
||||
|
||||
// Split each byte into two 4-bit nibbles
|
||||
ctxpc5.convert[0] = convert[0] >> 4;
|
||||
ctxpc5.convert[1] = convert[0] & 0xF;
|
||||
ctxpc5.convert[2] = convert[1] >> 4;
|
||||
ctxpc5.convert[3] = convert[1] & 0xF;
|
||||
ctxpc5.convert[4] = convert[2] >> 4;
|
||||
ctxpc5.convert[5] = convert[2] & 0xF;
|
||||
|
||||
pc5decrypt(&ctxpc5);
|
||||
|
||||
// Store decrypted nibbles back in convert
|
||||
for (int i = 0; i < 6; i++) convert[i] = ctxpc5.convert[i];
|
||||
|
||||
// Recombine nibbles into bytes
|
||||
ctxpc5.convert[0] = (convert[0] << 4) + convert[1];
|
||||
ctxpc5.convert[1] = (convert[2] << 4) + convert[3];
|
||||
ctxpc5.convert[2] = (convert[4] << 4) + convert[5];
|
||||
|
||||
// Convert back to bits for first 24 bits
|
||||
for (int q = 0; q < 3; q++) {
|
||||
uint8_t w = (uint8_t)(q * 8);
|
||||
hexbinpc5(&ctxpc5, frame_bits_in, w, ctxpc5.convert[q]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 49; i++) ctxpc5.bits[i] = frame_bits_in[i];
|
||||
}
|
||||
|
||||
/* Convert 64-bit integer to bytes (big-endian) */
|
||||
static void u64_to_bytes_be_pc5(uint64_t val, unsigned char *out) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
out[i] = (unsigned char)((val >> (56 - 8 * i)) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
#ifndef RETEVIS_AP
|
||||
#define RETEVIS_AP
|
||||
|
||||
#include <stdint.h>
|
||||
#include "dsd.h"
|
||||
|
||||
#define n1 264
|
||||
|
||||
typedef struct {
|
||||
uint8_t array_rc4[256];
|
||||
int i_rc4, j_rc4;
|
||||
uint64_t x;
|
||||
uint8_t xyz, count;
|
||||
uint64_t bb;
|
||||
} RC4State;
|
||||
|
||||
typedef struct {
|
||||
int x1, x2;
|
||||
unsigned char h2[n1];
|
||||
unsigned char h1[n1 * 3];
|
||||
} MD2State;
|
||||
|
||||
typedef struct {
|
||||
uint16_t xkey[64];
|
||||
uint8_t plain[8];
|
||||
uint8_t cipher[8];
|
||||
} RC2State;
|
||||
|
||||
typedef struct {
|
||||
RC4State rc4;
|
||||
MD2State md2;
|
||||
RC2State rc2;
|
||||
uint64_t internal_state;
|
||||
uint64_t internal_zero;
|
||||
uint8_t keys[16];
|
||||
} CryptoContext;
|
||||
|
||||
/* Public API */
|
||||
void create_keys_rc2(CryptoContext* ctx, unsigned char key1[], size_t size1);
|
||||
void encryption_rc2(CryptoContext* ctx, uint8_t s1[49]);
|
||||
void decrypt_rc2(CryptoContext* ctx, uint8_t bits[49]);
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@
|
||||
, ncurses
|
||||
, pulseaudio
|
||||
, rtl-sdr
|
||||
, itpp
|
||||
, codec2
|
||||
, portaudioSupport ? true
|
||||
, portaudio ? null
|
||||
@@ -22,6 +23,7 @@ stdenv.mkDerivation {
|
||||
buildInputs = [
|
||||
mbelib
|
||||
libsndfile
|
||||
itpp
|
||||
rtl-sdr
|
||||
ncurses.dev
|
||||
pulseaudio.dev
|
||||
@@ -37,7 +39,7 @@ stdenv.mkDerivation {
|
||||
tap audio and synthesize the decoded speech. Speech synthesis requires
|
||||
mbelib, which is a separate package.
|
||||
'';
|
||||
homepage = "https://github.com/lwvmobile/dsd-fme";
|
||||
homepage = "https://github.com/szechyjs/dsd";
|
||||
license = licenses.mit;
|
||||
platforms = platforms.unix;
|
||||
};
|
||||
|
||||
417
src/crypt-kir.c
417
src/crypt-kir.c
@@ -1,417 +0,0 @@
|
||||
#include "dsd.h"
|
||||
#include "pc4.h"
|
||||
|
||||
/* === Global MD2II buffers === */
|
||||
int x1, x2, i, n1global;
|
||||
unsigned char h2[n1];
|
||||
unsigned char h1[n1*3];
|
||||
|
||||
/* === MD2II functions === */
|
||||
void MD2II_init()
|
||||
{
|
||||
x1 = 0;
|
||||
x2 = 0;
|
||||
for (i = 0; i < n1global; i++)
|
||||
h2[i] = 0;
|
||||
for (i = 0; i < n1global; i++)
|
||||
h1[i] = 0;
|
||||
}
|
||||
|
||||
void MD2II_hashing(unsigned char t1[], size_t b6)
|
||||
{
|
||||
static unsigned char s4[256] =
|
||||
{13,199,11,67,237,193,164,77,115,184,141,222,73,
|
||||
38,147,36,150,87,21,104,12,61,156,101,111,145,
|
||||
119,22,207,35,198,37,171,167,80,30,219,28,213,
|
||||
121,86,29,214,242,6,4,89,162,110,175,19,157,
|
||||
3,88,234,94,144,118,159,239,100,17,182,173,238,
|
||||
68,16,79,132,54,163,52,9,58,57,55,229,192,
|
||||
170,226,56,231,187,158,70,224,233,245,26,47,32,
|
||||
44,247,8,251,20,197,185,109,153,204,218,93,178,
|
||||
212,137,84,174,24,120,130,149,72,180,181,208,255,
|
||||
189,152,18,143,176,60,249,27,227,128,139,243,253,
|
||||
59,123,172,108,211,96,138,10,215,42,225,40,81,
|
||||
65,90,25,98,126,154,64,124,116,122,5,1,168,
|
||||
83,190,131,191,244,240,235,177,155,228,125,66,43,
|
||||
201,248,220,129,188,230,62,75,71,78,34,31,216,
|
||||
254,136,91,114,106,46,217,196,92,151,209,133,51,
|
||||
236,33,252,127,179,69,7,183,105,146,97,39,15,
|
||||
205,112,200,166,223,45,48,246,186,41,148,140,107,
|
||||
76,85,95,194,142,50,49,134,23,135,169,221,210,
|
||||
203,63,165,82,161,202,53,14,206,232,103,102,195,
|
||||
117,250,99,0,74,160,241,2,113};
|
||||
|
||||
int b1, b2, b3, b4 = 0, b5;
|
||||
|
||||
while (b6) {
|
||||
for (; b6 && x2 < n1global; b6--, x2++) {
|
||||
b5 = t1[b4++];
|
||||
h1[x2 + n1global] = b5;
|
||||
h1[x2 + (n1global*2)] = b5 ^ h1[x2];
|
||||
x1 = h2[x2] ^= s4[b5 ^ x1];
|
||||
}
|
||||
|
||||
if (x2 == n1global) {
|
||||
b2 = 0;
|
||||
x2 = 0;
|
||||
for (b3 = 0; b3 < (n1global+2); b3++) {
|
||||
for (b1 = 0; b1 < (n1global*3); b1++)
|
||||
b2 = h1[b1] ^= s4[b2];
|
||||
b2 = (b2 + b3) % 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MD2II_end(unsigned char h4[n1])
|
||||
{
|
||||
unsigned char h3[n1];
|
||||
int i, n4;
|
||||
n4 = n1global - x2;
|
||||
for (i = 0; i < n4; i++) h3[i] = n4;
|
||||
MD2II_hashing(h3, n4);
|
||||
MD2II_hashing(h2, n1global);
|
||||
for (i = 0; i < n1global; i++) h4[i] = h1[i];
|
||||
}
|
||||
|
||||
void print_hex(const unsigned char *buf, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
printf("%02X ", buf[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static inline uint64_t rol48(uint64_t x, int n)
|
||||
{
|
||||
return ((x << n) | (x >> 47)) & 0xffffffffffff;
|
||||
}
|
||||
|
||||
/* === Keystream (A5-like) === */
|
||||
static int threshold(uint64_t r1, uint64_t r2, uint64_t r3)
|
||||
{
|
||||
int total = (((r1 >> 31) & 1) == 1) +
|
||||
(((r2 >> 31) & 1) == 1) +
|
||||
(((r3 >> 31) & 1) == 1);
|
||||
return (total > 1) ? 0 : 1;
|
||||
}
|
||||
|
||||
static uint64_t clock_r1(int ctl, uint64_t r1)
|
||||
{
|
||||
ctl ^= ((r1 >> 31) & 1);
|
||||
if (ctl) {
|
||||
int taps[] = {0,3,5,9,10,11,12,17,18,28,33,34,35,36,37,39,42,43,44,46,47,49,50,57,60,61,62,63};
|
||||
uint64_t fb = 0;
|
||||
for(int i=0;i<sizeof(taps)/sizeof(int);i++) fb ^= (r1>>taps[i])&1ULL;
|
||||
r1 = (r1<<1)&0xFFFFFFFFFFFFFFFFULL;
|
||||
if(fb&1ULL) r1 ^= 1ULL;
|
||||
}
|
||||
return r1;
|
||||
}
|
||||
|
||||
static uint64_t clock_r2(int ctl, uint64_t r2)
|
||||
{
|
||||
ctl ^= ((r2 >> 31) & 1);
|
||||
if (ctl) {
|
||||
int taps[] = {0,3,5,8,9,10,12,13,15,17,19,20,21,22,24,27,30,31,33,34,35,36,37,40,41,42,51,52,55,56,59,60,62,63};
|
||||
uint64_t fb = 0;
|
||||
for(int i=0;i<sizeof(taps)/sizeof(int);i++) fb ^= (r2>>taps[i])&1ULL;
|
||||
r2 = (r2<<1)&0xFFFFFFFFFFFFFFFFULL;
|
||||
if(fb&1ULL) r2 ^= 1ULL;
|
||||
}
|
||||
return r2;
|
||||
}
|
||||
|
||||
static uint64_t clock_r3(int ctl, uint64_t r3)
|
||||
{
|
||||
ctl ^= ((r3 >> 31) & 1);
|
||||
if (ctl) {
|
||||
int taps[] = {1,2,4,5,6,7,8,9,10,14,15,16,17,18,22,23,25,26,27,28,29,31,32,34,35,36,38,41,42,43,44,45,47,48,49,50,51,54,55,59,61,63};
|
||||
uint64_t fb = 0;
|
||||
for(int i=0;i<sizeof(taps)/sizeof(int);i++) fb ^= (r3>>taps[i])&1ULL;
|
||||
r3 = (r3<<1)&0xFFFFFFFFFFFFFFFFULL;
|
||||
if(fb&1ULL) r3 ^= 1ULL;
|
||||
}
|
||||
return r3;
|
||||
}
|
||||
|
||||
void keystream37(unsigned char *key, uint64_t frame, unsigned char *output)
|
||||
{
|
||||
uint64_t r1=0,r2=0,r3=0;
|
||||
for(int i=0;i<8;i++) r1=(r1<<8)|key[i];
|
||||
for(int i=0;i<8;i++) r2=(r2<<8)|key[i+8];
|
||||
for(int i=0;i<8;i++) r3=(r3<<8)|key[i+16];
|
||||
|
||||
for(int i=0;i<64;i++){
|
||||
int ctl=threshold(r1,r2,r3);
|
||||
r1=clock_r1(ctl,r1);
|
||||
r2=clock_r2(ctl,r2);
|
||||
r3=clock_r3(ctl,r3);
|
||||
if(frame&1ULL){ r1^=1ULL; r2^=1ULL; r3^=1ULL;}
|
||||
frame>>=1;
|
||||
}
|
||||
|
||||
for(int i=0;i<384;i++){
|
||||
int ctl=threshold(r1,r2,r3);
|
||||
r1=clock_r1(ctl,r1);
|
||||
r2=clock_r2(ctl,r2);
|
||||
r3=clock_r3(ctl,r3);
|
||||
}
|
||||
|
||||
unsigned char *ptr=output;
|
||||
unsigned char byte=0;
|
||||
int bits=0;
|
||||
for(int i=0;i<1008;i++){
|
||||
int ctl=threshold(r1,r2,r3);
|
||||
r1=clock_r1(ctl,r1);
|
||||
r2=clock_r2(ctl,r2);
|
||||
r3=clock_r3(ctl,r3);
|
||||
int bit=((r1>>63)^(r2>>63)^(r3>>63))&1U;
|
||||
byte=(byte<<1)|bit;
|
||||
bits++;
|
||||
if(bits==8){*ptr++=byte; bits=0; byte=0;}
|
||||
}
|
||||
if(bits) *ptr=byte;
|
||||
|
||||
}
|
||||
|
||||
//dsd-fme -fs -Z -i '/SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/cap-clear.wav/cap-33.wav' -H '3333333333333333 3333333333333333 3333333333333333 3333333333333333'
|
||||
void kirisun_uni_keystream_creation(dsd_state *state)
|
||||
{
|
||||
uint8_t slot = 0;
|
||||
if (state->currentslot == 0)
|
||||
slot = 0;
|
||||
else slot = 1;
|
||||
|
||||
uint32_t lfsr = 0;
|
||||
if (state->currentslot == 0)
|
||||
lfsr = (uint32_t)state->payload_mi;
|
||||
else lfsr = (uint32_t)state->payload_miR;
|
||||
|
||||
unsigned char userkeyhexa[32]; memset(userkeyhexa, 0, sizeof(userkeyhexa));
|
||||
unsigned char realkey[32]; memset(realkey, 0, sizeof(realkey));
|
||||
unsigned char lfsrhexa[4]; memset(lfsrhexa, 0, sizeof(lfsrhexa));
|
||||
uint8_t ks_bytes[126]; memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
|
||||
lfsrhexa[0] = (lfsr >> 24) & 0xFF;
|
||||
lfsrhexa[1] = (lfsr >> 16) & 0xFF;
|
||||
lfsrhexa[2] = (lfsr >> 8) & 0xFF;
|
||||
lfsrhexa[3] = (lfsr >> 0) & 0xFF;
|
||||
|
||||
uint64_t K1 = state->A1[slot];
|
||||
uint64_t K2 = state->A2[slot];
|
||||
uint64_t K3 = state->A3[slot];
|
||||
uint64_t K4 = state->A4[slot];
|
||||
|
||||
if (K1 != 0 && K2 != 0 && K3 != 0 && K4 != 0)
|
||||
state->aes_key_loaded[slot] = 1;
|
||||
else state->aes_key_loaded[slot] = 0;
|
||||
|
||||
if (state->aes_key_loaded[slot] == 1)
|
||||
{
|
||||
|
||||
u64_to_bytes_be(K1, &userkeyhexa[0]);
|
||||
u64_to_bytes_be(K2, &userkeyhexa[8]);
|
||||
u64_to_bytes_be(K3, &userkeyhexa[16]);
|
||||
u64_to_bytes_be(K4, &userkeyhexa[24]);
|
||||
|
||||
for(int i=0;i<32;i++) realkey[i]=userkeyhexa[i];
|
||||
|
||||
/* --- MD2II first pass --- */
|
||||
n1global=32;
|
||||
unsigned char h4[n1]; memset(h4, 0, sizeof(h4));
|
||||
MD2II_init();
|
||||
MD2II_hashing(realkey,32);
|
||||
MD2II_end(h4);
|
||||
for(int i=0;i<32;i++) realkey[i]=h4[i];
|
||||
|
||||
// printf("=== realkey final (32 bytes) ===\n");
|
||||
// print_hex(realkey,32);
|
||||
|
||||
/* --- MD2II with n1global=8, hash lfsr byte by byte --- */
|
||||
unsigned char hashtag[1];
|
||||
|
||||
n1global=8;
|
||||
MD2II_init();
|
||||
for(int i=0;i<4;i++)
|
||||
{
|
||||
hashtag[0] = (lfsr >> (24-8*i)) & 0xFF;
|
||||
MD2II_hashing(hashtag,1);
|
||||
}
|
||||
MD2II_hashing(realkey,32);
|
||||
MD2II_end(h4);
|
||||
|
||||
// printf("h4[0..7]: ");
|
||||
// print_hex(h4,8);
|
||||
|
||||
uint64_t internal_state=0;
|
||||
for(int i=0;i<8;i++)
|
||||
internal_state=(internal_state<<8)+(h4[i]);
|
||||
|
||||
// printf("internal_state=%016llX\n",(unsigned long long)internal_state);
|
||||
|
||||
/* --- MD2II with n1global=24 --- */
|
||||
n1global=24;
|
||||
MD2II_init();
|
||||
for(int i=0;i<4;i++)
|
||||
{
|
||||
hashtag[0] = (lfsr >> (24-8*i)) & 0xFF;
|
||||
MD2II_hashing(hashtag,1);
|
||||
}
|
||||
MD2II_hashing(realkey,32);
|
||||
MD2II_end(h4);
|
||||
|
||||
// printf("realkey (24 bytes):\n");
|
||||
// print_hex(h4,24);
|
||||
|
||||
/* --- keystream output --- */
|
||||
unsigned char output[126]; memset(output, 0, sizeof(output));
|
||||
keystream37(h4, internal_state, output);
|
||||
|
||||
// printf("keystream output (126 bytes):\n");
|
||||
// print_hex(output,126);
|
||||
|
||||
//store ks_bytes to storage
|
||||
for (int i = 0; i < 126; i++)
|
||||
ks_bytes[i] = (uint8_t)output[i];
|
||||
|
||||
if (slot == 0)
|
||||
memcpy(state->ks_octetL, ks_bytes, sizeof(ks_bytes));
|
||||
else memcpy(state->ks_octetR, ks_bytes, sizeof(ks_bytes));
|
||||
|
||||
//debug ks_bytes
|
||||
// fprintf (stderr, "KS: ");
|
||||
// for (int i = 0; i < 126; i++)
|
||||
// {
|
||||
// if ((i != 0) && ((i%16)==0))
|
||||
// fprintf (stderr, "\n ");
|
||||
// fprintf (stderr, "%02X ", ks_bytes[i]);
|
||||
// }
|
||||
// fprintf (stderr, "\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//dsd-fme -fs -Z -i '/SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/better_samples_tg1/kirisun_advanced_tg1.wav' -H 'DC1A7E9F9BF312DB F45010CEC5F7A53A C407D0BFA803617B E426A7254DA9390D'
|
||||
void kirisun_adv_keystream_creation(dsd_state *state)
|
||||
{
|
||||
|
||||
uint8_t slot = 0;
|
||||
if (state->currentslot == 0)
|
||||
slot = 0;
|
||||
else slot = 1;
|
||||
|
||||
uint32_t lfsr = 0;
|
||||
if (state->currentslot == 0)
|
||||
lfsr = (uint32_t)state->payload_mi;
|
||||
else lfsr = (uint32_t)state->payload_miR;
|
||||
|
||||
unsigned char userkeyhexa[32]; memset(userkeyhexa, 0, sizeof(userkeyhexa));
|
||||
unsigned char realkey[32]; memset(realkey, 0, sizeof(realkey));
|
||||
unsigned char lfsrhexa[4]; memset(lfsrhexa, 0, sizeof(lfsrhexa));
|
||||
uint8_t ks_bytes[126]; memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
|
||||
lfsrhexa[0] = (lfsr >> 24) & 0xFF;
|
||||
lfsrhexa[1] = (lfsr >> 16) & 0xFF;
|
||||
lfsrhexa[2] = (lfsr >> 8) & 0xFF;
|
||||
lfsrhexa[3] = (lfsr >> 0) & 0xFF;
|
||||
|
||||
uint64_t K1 = state->A1[slot];
|
||||
uint64_t K2 = state->A2[slot];
|
||||
uint64_t K3 = state->A3[slot];
|
||||
uint64_t K4 = state->A4[slot];
|
||||
|
||||
if (K1 != 0 && K2 != 0 && K3 != 0 && K4 != 0)
|
||||
state->aes_key_loaded[slot] = 1;
|
||||
else state->aes_key_loaded[slot] = 0;
|
||||
|
||||
if (state->aes_key_loaded[slot] == 1)
|
||||
{
|
||||
|
||||
u64_to_bytes_be(K1, &userkeyhexa[0]);
|
||||
u64_to_bytes_be(K2, &userkeyhexa[8]);
|
||||
u64_to_bytes_be(K3, &userkeyhexa[16]);
|
||||
u64_to_bytes_be(K4, &userkeyhexa[24]);
|
||||
|
||||
//init vital portions of PC4Context
|
||||
ctx.x1 = 0;
|
||||
ctx.x2 = 0;
|
||||
for (ctx.i = 0; ctx.i < n1; ctx.i++) ctx.h2[ctx.i] = 0;
|
||||
for (ctx.i = 0; ctx.i < n1; ctx.i++) ctx.h1[ctx.i] = 0;
|
||||
ctx.rounds = nbround;
|
||||
|
||||
//create keys
|
||||
create_keys(&ctx, userkeyhexa, 32);
|
||||
|
||||
/* --- MD2II first pass --- */
|
||||
n1global=32;
|
||||
unsigned char h4[n1]; memset(h4, 0, sizeof(h4));
|
||||
MD2II_init();
|
||||
MD2II_hashing(userkeyhexa,32);
|
||||
MD2II_end(h4);
|
||||
for(int i=0;i<32;i++) realkey[i]=h4[i];
|
||||
|
||||
// printf("=== realkey final (32 bytes) ===\n");
|
||||
// print_hex(realkey,32);
|
||||
|
||||
n1global=32;
|
||||
memset(h4, 0, sizeof(h4));
|
||||
MD2II_init();
|
||||
MD2II_hashing(lfsrhexa,4);
|
||||
MD2II_hashing(realkey,32);
|
||||
MD2II_end(h4);
|
||||
|
||||
// printf("h4[0..7]: ");
|
||||
// print_hex(h4,8);
|
||||
|
||||
uint64_t internal_state=0;
|
||||
for(int i=0;i<6;i++)
|
||||
internal_state=(internal_state<<8)+(h4[i]);
|
||||
|
||||
int k = 0;
|
||||
for (int i = 0; i < 18; i++)
|
||||
{
|
||||
|
||||
//debug internal_state
|
||||
// fprintf (stderr, "ST: %012lX; \n", internal_state);
|
||||
|
||||
ctx.convert[0]=(internal_state>>40)&0xff;
|
||||
ctx.convert[1]=(internal_state>>32)&0xff;
|
||||
ctx.convert[2]=(internal_state>>24)&0xff;
|
||||
ctx.convert[3]=(internal_state>>16)&0xff;
|
||||
ctx.convert[4]=(internal_state>>8)&0xff;
|
||||
ctx.convert[5]=internal_state&0xff;
|
||||
|
||||
pc4encrypt(&ctx);
|
||||
|
||||
internal_state=0;
|
||||
for (int i=0;i<6;i++)
|
||||
internal_state=(internal_state<<8)+(ctx.convert[i]&0xff);
|
||||
|
||||
internal_state=rol48(internal_state,1);
|
||||
|
||||
//keystream
|
||||
for (int i=0;i<6;i++)
|
||||
ks_bytes[k++] = ctx.convert[i];
|
||||
k++; //skip 7th octet
|
||||
|
||||
}
|
||||
|
||||
//store ks_bytes to storage
|
||||
if (slot == 0)
|
||||
memcpy(state->ks_octetL, ks_bytes, sizeof(ks_bytes));
|
||||
else memcpy(state->ks_octetR, ks_bytes, sizeof(ks_bytes));
|
||||
|
||||
//debug ks_bytes
|
||||
// fprintf (stderr, "KS: ");
|
||||
// for (int i = 0; i < 126; i++)
|
||||
// {
|
||||
// if ((i != 0) && ((i%16)==0))
|
||||
// fprintf (stderr, "\n ");
|
||||
// fprintf (stderr, "%02X ", ks_bytes[i]);
|
||||
// }
|
||||
// fprintf (stderr, "\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
468
src/crypt-pc5.c
468
src/crypt-pc5.c
@@ -1,468 +0,0 @@
|
||||
#include "pc5.h"
|
||||
#include "dsd.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/* Global PC5 context instance */
|
||||
PC5Context ctxpc5;
|
||||
|
||||
/* ---------------------------------
|
||||
Internal utility functions
|
||||
----------------------------------- */
|
||||
|
||||
/* Rotate right */
|
||||
static uint32_t ror(uint32_t x, int shift, int bits) {
|
||||
uint32_t m0 = (1u << (bits - shift)) - 1u;
|
||||
uint32_t m1 = (1u << shift) - 1u;
|
||||
return ((x >> shift) & m0) | ((x & m1) << (bits - shift));
|
||||
}
|
||||
|
||||
/* Rotate left */
|
||||
static uint32_t rol(uint32_t x, int shift, int bits) {
|
||||
uint32_t m0 = (1u << (bits - shift)) - 1u;
|
||||
uint32_t m1 = (1u << shift) - 1u;
|
||||
return ((x & m0) << shift) | ((x >> (bits - shift)) & m1);
|
||||
}
|
||||
|
||||
/* SplitMix64 random number generator */
|
||||
static uint64_t next_rng(PC5Context *ctxpc5) {
|
||||
uint64_t z = (ctxpc5->x += 0x9e3779b97f4a7c15ULL);
|
||||
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
|
||||
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
|
||||
return z ^ (z >> 31);
|
||||
}
|
||||
|
||||
/* ARC4 initialization */
|
||||
static void arc4_init(PC5Context *ctxpc5, unsigned char key[]) {
|
||||
int tmp;
|
||||
for (ctxpc5->i_arc4 = 0; ctxpc5->i_arc4 < 256; ctxpc5->i_arc4++)
|
||||
ctxpc5->array_arc4[ctxpc5->i_arc4] = (unsigned char)ctxpc5->i_arc4;
|
||||
|
||||
ctxpc5->j_arc4 = 0;
|
||||
for (ctxpc5->i_arc4 = 0; ctxpc5->i_arc4 < 256; ctxpc5->i_arc4++) {
|
||||
ctxpc5->j_arc4 = (ctxpc5->j_arc4 + ctxpc5->array_arc4[ctxpc5->i_arc4] + key[ctxpc5->i_arc4 % 256]) % 256;
|
||||
tmp = ctxpc5->array_arc4[ctxpc5->i_arc4];
|
||||
ctxpc5->array_arc4[ctxpc5->i_arc4] = ctxpc5->array_arc4[ctxpc5->j_arc4];
|
||||
ctxpc5->array_arc4[ctxpc5->j_arc4] = tmp;
|
||||
}
|
||||
ctxpc5->i_arc4 = 0;
|
||||
ctxpc5->j_arc4 = 0;
|
||||
}
|
||||
|
||||
/* ARC4 output combined with SplitMix64 stream */
|
||||
static unsigned char arc4_output(PC5Context *ctxpc5) {
|
||||
uint8_t rndbyte, decal;
|
||||
int tmp, t;
|
||||
|
||||
ctxpc5->i_arc4 = (ctxpc5->i_arc4 + 1) % 256;
|
||||
ctxpc5->j_arc4 = (ctxpc5->j_arc4 + ctxpc5->array_arc4[ctxpc5->i_arc4]) % 256;
|
||||
tmp = ctxpc5->array_arc4[ctxpc5->i_arc4];
|
||||
ctxpc5->array_arc4[ctxpc5->i_arc4] = ctxpc5->array_arc4[ctxpc5->j_arc4];
|
||||
ctxpc5->array_arc4[ctxpc5->j_arc4] = tmp;
|
||||
t = (ctxpc5->array_arc4[ctxpc5->i_arc4] + ctxpc5->array_arc4[ctxpc5->j_arc4]) % 256;
|
||||
|
||||
if (ctxpc5->xyz == 0) ctxpc5->bb = next_rng(ctxpc5);
|
||||
decal = (uint8_t)(56 - (8 * ctxpc5->xyz));
|
||||
rndbyte = (uint8_t)((ctxpc5->bb >> decal) & 0xffu);
|
||||
ctxpc5->xyz++;
|
||||
if (ctxpc5->xyz == 8) ctxpc5->xyz = 0;
|
||||
|
||||
if (ctxpc5->count == 0) {
|
||||
rndbyte = (uint8_t)(rndbyte ^ ctxpc5->array_arc4[t]);
|
||||
ctxpc5->count = 1;
|
||||
} else {
|
||||
rndbyte = (uint8_t)(rndbyte + ctxpc5->array_arc4[t]);
|
||||
ctxpc5->count = 0;
|
||||
}
|
||||
return rndbyte;
|
||||
}
|
||||
|
||||
/* Initialize MD2-II state */
|
||||
static void md2_init(PC5Context *ctxpc5) {
|
||||
ctxpc5->x1 = 0;
|
||||
ctxpc5->x2 = 0;
|
||||
for (ctxpc5->i = 0; ctxpc5->i < n1; ctxpc5->i++) ctxpc5->h2[ctxpc5->i] = 0;
|
||||
for (ctxpc5->i = 0; ctxpc5->i < n1; ctxpc5->i++) ctxpc5->h1[ctxpc5->i] = 0;
|
||||
}
|
||||
|
||||
/* MD2-II hashing */
|
||||
static void md2_hashing(PC5Context *ctxpc5, unsigned char t1[], size_t b6) {
|
||||
static const unsigned char s4[256] = {
|
||||
13,199,11,67,237,193,164,77,115,184,141,222,73,38,147,36,150,87,21,104,12,61,156,101,111,145,
|
||||
119,22,207,35,198,37,171,167,80,30,219,28,213,121,86,29,214,242,6,4,89,162,110,175,19,157,3,
|
||||
88,234,94,144,118,159,239,100,17,182,173,238,68,16,79,132,54,163,52,9,58,57,55,229,192,170,226,
|
||||
56,231,187,158,70,224,233,245,26,47,32,44,247,8,251,20,197,185,109,153,204,218,93,178,212,137,84,
|
||||
174,24,120,130,149,72,180,181,208,255,189,152,18,143,176,60,249,27,227,128,139,243,253,59,123,172,
|
||||
108,211,96,138,10,215,42,225,40,81,65,90,25,98,126,154,64,124,116,122,5,1,168,83,190,131,191,244,
|
||||
240,235,177,155,228,125,66,43,201,248,220,129,188,230,62,75,71,78,34,31,216,254,136,91,114,106,46,
|
||||
217,196,92,151,209,133,51,236,33,252,127,179,69,7,183,105,146,97,39,15,205,112,200,166,223,45,48,
|
||||
246,186,41,148,140,107,76,85,95,194,142,50,49,134,23,135,169,221,210,203,63,165,82,161,202,53,14,
|
||||
206,232,103,102,195,117,250,99,0,74,160,241,2,113};
|
||||
|
||||
int b1, b2, b3, b4, b5;
|
||||
b4 = 0;
|
||||
while (b6) {
|
||||
for (; b6 && ctxpc5->x2 < n1; b6--, ctxpc5->x2++) {
|
||||
b5 = t1[b4++];
|
||||
ctxpc5->h1[ctxpc5->x2 + n1] = (unsigned char)b5;
|
||||
ctxpc5->h1[ctxpc5->x2 + (n1 * 2)] = (unsigned char)(b5 ^ ctxpc5->h1[ctxpc5->x2]);
|
||||
ctxpc5->x1 = ctxpc5->h2[ctxpc5->x2] ^= s4[b5 ^ ctxpc5->x1];
|
||||
}
|
||||
if (ctxpc5->x2 == n1) {
|
||||
b2 = 0;
|
||||
ctxpc5->x2 = 0;
|
||||
for (b3 = 0; b3 < (n1 + 2); b3++) {
|
||||
for (b1 = 0; b1 < (n1 * 3); b1++)
|
||||
b2 = ctxpc5->h1[b1] ^= s4[b2];
|
||||
b2 = (b2 + b3) % 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize MD2-II */
|
||||
static void md2_end(PC5Context *ctxpc5, unsigned char h4[n1]) {
|
||||
unsigned char h3[n1];
|
||||
int i, n4 = n1 - ctxpc5->x2;
|
||||
for (i = 0; i < n4; i++) h3[i] = (unsigned char)n4;
|
||||
md2_hashing(ctxpc5, h3, (size_t)n4);
|
||||
md2_hashing(ctxpc5, ctxpc5->h2, sizeof(ctxpc5->h2));
|
||||
for (i = 0; i < n1; i++) h4[i] = ctxpc5->h1[i];
|
||||
}
|
||||
|
||||
/* Generate a random index */
|
||||
static int mixy(PC5Context *ctxpc5, int nn2) {
|
||||
return arc4_output(ctxpc5) % nn2;
|
||||
}
|
||||
|
||||
/* Fisher-Yates shuffle */
|
||||
static void mixer(PC5Context *ctxpc5, uint8_t *mixu, int nn) {
|
||||
int ii, jj, tmmp;
|
||||
for (ii = nn - 1; ii > 0; ii--) {
|
||||
jj = mixy(ctxpc5, ii + 1);
|
||||
tmmp = mixu[jj];
|
||||
mixu[jj] = mixu[ii];
|
||||
mixu[ii] = (uint8_t)tmmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Key schedule and S-box generation */
|
||||
void create_keys_pc5(PC5Context *ctxpc5, unsigned char key1[], size_t size1) {
|
||||
int i, w, k;
|
||||
unsigned char h4[n1];
|
||||
|
||||
md2_init(ctxpc5);
|
||||
md2_hashing(ctxpc5, key1, size1);
|
||||
md2_end(ctxpc5, h4);
|
||||
|
||||
for (i = 0; i < 16; i++) ctxpc5->keys[i] = h4[i];
|
||||
arc4_init(ctxpc5, h4);
|
||||
|
||||
ctxpc5->x = 0;
|
||||
for (i = 0; i < 8; i++) ctxpc5->x = (ctxpc5->x << 8) + (uint64_t)(h4[256 + i] & 0xffu);
|
||||
|
||||
ctxpc5->xyz = 0;
|
||||
ctxpc5->count = 0;
|
||||
|
||||
for (i = 0; i < 23000; i++) (void)arc4_output(ctxpc5);
|
||||
|
||||
uint8_t numbers[256];
|
||||
|
||||
for (w = 0; w < 253; w++) {
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
|
||||
for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i;
|
||||
mixer(ctxpc5, numbers, 16);
|
||||
for (i = 0; i < 16; i++) ctxpc5->perm[i][w] = numbers[i];
|
||||
}
|
||||
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
|
||||
for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i;
|
||||
mixer(ctxpc5, numbers, 16);
|
||||
for (i = 0; i < 16; i++) ctxpc5->new1[i] = numbers[i];
|
||||
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
|
||||
for (i = 0; i < nbround; i++) ctxpc5->decal[i] = (uint8_t)((arc4_output(ctxpc5) % 11) + 1);
|
||||
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
|
||||
for (w = 0; w < 3; w++)
|
||||
for (i = 0; i < nbround; i++)
|
||||
ctxpc5->rngxor[i][w] = arc4_output(ctxpc5) % 16;
|
||||
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
|
||||
for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i;
|
||||
mixer(ctxpc5, numbers, 16);
|
||||
for (i = 0; i < 16; i++) {
|
||||
ctxpc5->tab[i] = numbers[i];
|
||||
ctxpc5->inv[ctxpc5->tab[i]] = (unsigned char)i;
|
||||
}
|
||||
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
|
||||
for (w = 0; w < 3; w++) {
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
|
||||
for (i = 0; i < 3; i++) numbers[i] = (uint8_t)i;
|
||||
mixer(ctxpc5, numbers, 3);
|
||||
for (i = 0; i < 3; i++) ctxpc5->permut[w][i] = numbers[i];
|
||||
}
|
||||
|
||||
|
||||
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
|
||||
for (w = 0; w < 3; w++)
|
||||
for (i = 0; i < nbround; i++)
|
||||
ctxpc5->rngxor2[i][w] = arc4_output(ctxpc5) % 16;
|
||||
|
||||
k = arc4_output(ctxpc5) + 256;
|
||||
|
||||
for (w = 0; w < 25; w++) ctxpc5->numbers[w]=arc4_output(ctxpc5) % 2;
|
||||
|
||||
}
|
||||
|
||||
static void compute_pc5(PC5Context *ctxpc5, uint8_t *tab1, uint8_t round) {
|
||||
ctxpc5->tot[0] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[0][0]]][round] +
|
||||
ctxpc5->perm[tab1[ctxpc5->permut[0][1]]][round]) ^
|
||||
ctxpc5->perm[tab1[ctxpc5->permut[0][2]]][round]);
|
||||
ctxpc5->tot[0] = (uint8_t)((ctxpc5->tot[0] + ctxpc5->new1[ctxpc5->tot[0]]) % 16);
|
||||
ctxpc5->tot[1] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[1][0]]][round] +
|
||||
ctxpc5->perm[tab1[ctxpc5->permut[1][1]]][round]) ^
|
||||
ctxpc5->perm[tab1[ctxpc5->permut[1][2]]][round]);
|
||||
ctxpc5->tot[1] = (uint8_t)((ctxpc5->tot[1] + ctxpc5->new1[ctxpc5->tot[1]]) % 16);
|
||||
ctxpc5->tot[2] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[2][0]]][round] +
|
||||
ctxpc5->perm[tab1[ctxpc5->permut[2][1]]][round]) ^
|
||||
ctxpc5->perm[tab1[ctxpc5->permut[2][2]]][round]);
|
||||
ctxpc5->tot[2] = (uint8_t)((ctxpc5->tot[2] + ctxpc5->new1[ctxpc5->tot[2]]) % 16);
|
||||
}
|
||||
|
||||
/* Convert bits to bytes */
|
||||
void binhexpc5(PC5Context *ctxpc5, short *z, int length) {
|
||||
short *b = (short *)z;
|
||||
uint8_t i, j;
|
||||
for (i = 0; i < length; i = j) {
|
||||
uint8_t a = 0;
|
||||
for (j = i; j < i + 8; ++j) {
|
||||
a |= (uint8_t)(b[((short)(7 - (j % 8)) + j) - (j % 8)] << (j - i));
|
||||
}
|
||||
ctxpc5->convert[ctxpc5->ptconvert] = a;
|
||||
ctxpc5->ptconvert++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert byte to bits */
|
||||
void hexbinpc5(PC5Context *ctxpc5, short *q, uint8_t w, uint8_t hex) { // warning: unused parameter ‘ctxpc5’ [-Wunused-parameter]
|
||||
UNUSED(ctxpc5);
|
||||
short *bits = (short *)q;
|
||||
for (uint8_t i = 0; i < 8; ++i) {
|
||||
bits[(short)(7 + w) - i] = (short)((hex >> i) & 1u);
|
||||
}
|
||||
|
||||
// UNUSEDPC4(ctxpc5); //fix above warning
|
||||
}
|
||||
|
||||
/* Encrypt one block */
|
||||
void pc5encrypt(PC5Context *ctxpc5) {
|
||||
int i;
|
||||
int g;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ctxpc5->l[0][i] = ctxpc5->convert[i];
|
||||
ctxpc5->r[0][i] = ctxpc5->convert[i + 3];
|
||||
}
|
||||
|
||||
for (i = 1; i <= ctxpc5->rounds; i++) {
|
||||
ctxpc5->r[(i - 1) % 2][0] = (ctxpc5->r[(i - 1) % 2][0] + ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][0])) % 16;
|
||||
ctxpc5->r[(i - 1) % 2][1] = (ctxpc5->r[(i - 1) % 2][1] ^ ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][1])) % 16;
|
||||
ctxpc5->r[(i - 1) % 2][2] = (ctxpc5->r[(i - 1) % 2][2] + ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][2])) % 16;
|
||||
|
||||
ctxpc5->result = 0;
|
||||
ctxpc5->result += ((uint32_t)ctxpc5->r[(i - 1) % 2][0] << 8);
|
||||
ctxpc5->result += ((uint32_t)ctxpc5->r[(i - 1) % 2][1] << 4);
|
||||
ctxpc5->result += ctxpc5->r[(i - 1) % 2][2];
|
||||
|
||||
ctxpc5->result = rol(ctxpc5->result, ctxpc5->decal[i - 1], 12);
|
||||
|
||||
ctxpc5->r[(i - 1) % 2][0] = (uint8_t)(ctxpc5->result >> 8);
|
||||
ctxpc5->r[(i - 1) % 2][1] = (uint8_t)((ctxpc5->result >> 4) & 0xfu);
|
||||
ctxpc5->r[(i - 1) % 2][2] = (uint8_t)(ctxpc5->result & 0xfu);
|
||||
|
||||
ctxpc5->r[(i - 1) % 2][0] = ctxpc5->tab[ctxpc5->r[(i - 1) % 2][0]];
|
||||
ctxpc5->r[(i - 1) % 2][0] = (ctxpc5->r[(i - 1) % 2][0] ^ ctxpc5->rngxor[i - 1][0]) % 16;
|
||||
|
||||
ctxpc5->r[(i - 1) % 2][1] = ctxpc5->inv[ctxpc5->r[(i - 1) % 2][1]];
|
||||
|
||||
if ((ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) < 0) {
|
||||
g = (ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) + 16;
|
||||
} else {
|
||||
g = (ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) % 16;
|
||||
}
|
||||
ctxpc5->r[(i - 1) % 2][1] = g;
|
||||
|
||||
ctxpc5->r[(i - 1) % 2][2] = ctxpc5->tab[ctxpc5->r[(i - 1) % 2][2]];
|
||||
ctxpc5->r[(i - 1) % 2][2] = (ctxpc5->r[(i - 1) % 2][2] ^ ctxpc5->rngxor[i - 1][2]) % 16;
|
||||
|
||||
compute_pc5(ctxpc5, ctxpc5->r[(i - 1) % 2], (uint8_t)((i - 1) % 253));
|
||||
|
||||
ctxpc5->l[i % 2][0] = ctxpc5->r[(i - 1) % 2][0];
|
||||
|
||||
if ((ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0]) < 0) {
|
||||
g = (ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0]) + 16;
|
||||
} else {
|
||||
g = ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0];
|
||||
}
|
||||
ctxpc5->r[i % 2][0] = g;
|
||||
|
||||
ctxpc5->l[i % 2][1] = ctxpc5->r[(i - 1) % 2][1];
|
||||
ctxpc5->r[i % 2][1] = (ctxpc5->l[(i - 1) % 2][1] ^ ctxpc5->tot[1]) % 16;
|
||||
|
||||
ctxpc5->l[i % 2][2] = ctxpc5->r[(i - 1) % 2][2];
|
||||
|
||||
if ((ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2]) < 0) {
|
||||
g = (ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2]) + 16;
|
||||
} else {
|
||||
g = ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2];
|
||||
}
|
||||
ctxpc5->r[i % 2][2] = g;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ctxpc5->convert[i + 3] = ctxpc5->l[(ctxpc5->rounds - 1) % 2][i];
|
||||
ctxpc5->convert[i] = ctxpc5->r[(ctxpc5->rounds - 1) % 2][i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Decrypt one block */
|
||||
void pc5decrypt(PC5Context *ctxpc5) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ctxpc5->l[0][i] = ctxpc5->convert[i];
|
||||
ctxpc5->r[0][i] = ctxpc5->convert[i + 3];
|
||||
}
|
||||
|
||||
ctxpc5->y = (uint8_t)((ctxpc5->rounds - 1) % 253);
|
||||
if (ctxpc5->y == 0) ctxpc5->y = 253;
|
||||
|
||||
for (i = 1; i <= ctxpc5->rounds; i++) {
|
||||
ctxpc5->y--;
|
||||
compute_pc5(ctxpc5, ctxpc5->r[(i - 1) % 2], ctxpc5->y);
|
||||
if (ctxpc5->y == 0) ctxpc5->y = 253;
|
||||
|
||||
ctxpc5->result = 0;
|
||||
|
||||
ctxpc5->l[(i - 1) % 2][0] ^= ctxpc5->rngxor[ctxpc5->rounds - i][0];
|
||||
ctxpc5->l[(i - 1) % 2][0] %= 16;
|
||||
ctxpc5->l[(i - 1) % 2][0] = ctxpc5->inv[ctxpc5->l[(i - 1) % 2][0]];
|
||||
|
||||
ctxpc5->l[(i - 1) % 2][1] += ctxpc5->rngxor[ctxpc5->rounds - i][1];
|
||||
ctxpc5->l[(i - 1) % 2][1] %= 16;
|
||||
ctxpc5->l[(i - 1) % 2][1] = ctxpc5->tab[ctxpc5->l[(i - 1) % 2][1]];
|
||||
|
||||
ctxpc5->l[(i - 1) % 2][2] ^= ctxpc5->rngxor[ctxpc5->rounds - i][2];
|
||||
ctxpc5->l[(i - 1) % 2][2] %= 16;
|
||||
ctxpc5->l[(i - 1) % 2][2] = ctxpc5->inv[ctxpc5->l[(i - 1) % 2][2]];
|
||||
|
||||
ctxpc5->result += ((uint32_t)ctxpc5->l[(i - 1) % 2][0] << 8);
|
||||
ctxpc5->result += ((uint32_t)ctxpc5->l[(i - 1) % 2][1] << 4);
|
||||
ctxpc5->result += ctxpc5->l[(i - 1) % 2][2];
|
||||
|
||||
ctxpc5->result = ror(ctxpc5->result, ctxpc5->decal[ctxpc5->rounds - i], 12);
|
||||
|
||||
ctxpc5->l[(i - 1) % 2][0] = (uint8_t)(ctxpc5->result >> 8);
|
||||
ctxpc5->l[(i - 1) % 2][1] = (uint8_t)((ctxpc5->result >> 4) & 0xfu);
|
||||
ctxpc5->l[(i - 1) % 2][2] = (uint8_t)(ctxpc5->result & 0xfu);
|
||||
|
||||
int temp;
|
||||
|
||||
temp = ctxpc5->l[(i - 1) % 2][0] - ((uint8_t)(~ctxpc5->rngxor2[i - 1][0]) % 16);
|
||||
if (temp < 0) temp += 16;
|
||||
ctxpc5->l[(i - 1) % 2][0] = temp % 16;
|
||||
|
||||
ctxpc5->l[(i - 1) % 2][1] ^= (uint8_t)(~ctxpc5->rngxor2[i - 1][1]) % 16;
|
||||
ctxpc5->l[(i - 1) % 2][1] %= 16;
|
||||
|
||||
temp = ctxpc5->l[(i - 1) % 2][2] - ((uint8_t)(~ctxpc5->rngxor2[i - 1][2]) % 16);
|
||||
if (temp < 0) temp += 16;
|
||||
ctxpc5->l[(i - 1) % 2][2] = temp % 16;
|
||||
|
||||
ctxpc5->l[i % 2][0] = ctxpc5->r[(i - 1) % 2][0];
|
||||
ctxpc5->r[i % 2][0] = (ctxpc5->l[(i - 1) % 2][0] + ctxpc5->tot[0]) % 16;
|
||||
|
||||
ctxpc5->l[i % 2][1] = ctxpc5->r[(i - 1) % 2][1];
|
||||
ctxpc5->r[i % 2][1] = (ctxpc5->l[(i - 1) % 2][1] ^ ctxpc5->tot[1]) % 16;
|
||||
|
||||
ctxpc5->l[i % 2][2] = ctxpc5->r[(i - 1) % 2][2];
|
||||
ctxpc5->r[i % 2][2] = (ctxpc5->l[(i - 1) % 2][2] + ctxpc5->tot[2]) % 16;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ctxpc5->convert[i + 3] = ctxpc5->l[(ctxpc5->rounds - 1) % 2][i];
|
||||
ctxpc5->convert[i] = ctxpc5->r[(ctxpc5->rounds - 1) % 2][i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Key creation for BAOFENG AP */
|
||||
void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input) {
|
||||
|
||||
char buf[1024];
|
||||
strncpy(buf, input, sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
// Count hexadecimal characters (without spaces)
|
||||
int hex_char_count = 0;
|
||||
for (int i = 0; buf[i] != '\0'; i++) {
|
||||
if (isxdigit(buf[i])) {
|
||||
hex_char_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hex_char_count == 32) { // 128 bits (32 hex characters)
|
||||
unsigned char key1[16] = {0};
|
||||
unsigned char key2[16] = {0};
|
||||
|
||||
char *pEnd;
|
||||
uint64_t K1 = strtoull(buf, &pEnd, 16);
|
||||
uint64_t K2 = strtoull(pEnd, &pEnd, 16);
|
||||
|
||||
u64_to_bytes_be_pc5(K1, &key1[0]);
|
||||
u64_to_bytes_be_pc5(K2, &key1[8]);
|
||||
|
||||
// Reverse key for PC5 (only for 128-bit)
|
||||
for (int i=0;i<16;i++) key2[i] = key1[15-i];
|
||||
|
||||
/* Create key schedule */
|
||||
create_keys_pc5(&ctxpc5, key2, sizeof(key2));
|
||||
ctxpc5.rounds = nbround;
|
||||
|
||||
fprintf(stderr, "DMR BAOFENG AP (PC5) 128-bit Key %016llX%016llX with Forced Application\n",
|
||||
(unsigned long long)K1, (unsigned long long)K2);
|
||||
state->baofeng_ap = 1;
|
||||
|
||||
} else if (hex_char_count == 64) { // 256 bits (64 hex characters)
|
||||
// Remove spaces and store as ASCII string (64 bytes)
|
||||
char ascii_key[65] = {0}; // 64 characters + null terminator
|
||||
int j = 0;
|
||||
for (int i = 0; buf[i] != '\0' && j < 64; i++) {
|
||||
if (isxdigit(buf[i])) {
|
||||
ascii_key[j++] = buf[i];
|
||||
}
|
||||
}
|
||||
ascii_key[64] = '\0';
|
||||
|
||||
/* Create key schedule */
|
||||
create_keys_pc5(&ctxpc5, (unsigned char*)ascii_key, 64);
|
||||
ctxpc5.rounds = nbround;
|
||||
|
||||
fprintf(stderr, "DMR BAOFENG AP (PC5) 256-bit Key: %s with Forced Application\n", ascii_key);
|
||||
state->baofeng_ap = 1;
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Invalid key length. Expected 32 chars (128-bit) or 64 chars (256-bit), got %d hex chars\n", hex_char_count);
|
||||
return;
|
||||
}
|
||||
}
|
||||
404
src/crypt-rc2.c
404
src/crypt-rc2.c
@@ -1,404 +0,0 @@
|
||||
#include "rc2.h"
|
||||
#include "dsd.h"
|
||||
|
||||
static inline uint64_t rol64(uint64_t x, int n) {
|
||||
return ((x << n) | (x >> (63 - n) >> 1)) & 0xffffffffffffffff;
|
||||
}
|
||||
|
||||
void swapbit(uint64_t* internalstate, uint8_t bit) {
|
||||
unsigned char bitB = bit & 1;
|
||||
if (bitB)
|
||||
*internalstate |= bitB;
|
||||
else
|
||||
*internalstate &= (~bitB ^ 1);
|
||||
}
|
||||
|
||||
// MD2 functions
|
||||
void md2_init(MD2State* state) {
|
||||
state->x1 = 0;
|
||||
state->x2 = 0;
|
||||
memset(state->h2, 0, n1);
|
||||
memset(state->h1, 0, n1 * 3);
|
||||
}
|
||||
|
||||
void md2_hashing(MD2State* state, unsigned char t1[], size_t b6) {
|
||||
static unsigned char s4[256] = {
|
||||
13,199,11,67,237,193,164,77,115,184,141,222,73,
|
||||
38,147,36,150,87,21,104,12,61,156,101,111,145,
|
||||
119,22,207,35,198,37,171,167,80,30,219,28,213,
|
||||
121,86,29,214,242,6,4,89,162,110,175,19,157,
|
||||
3,88,234,94,144,118,159,239,100,17,182,173,238,
|
||||
68,16,79,132,54,163,52,9,58,57,55,229,192,
|
||||
170,226,56,231,187,158,70,224,233,245,26,47,32,
|
||||
44,247,8,251,20,197,185,109,153,204,218,93,178,
|
||||
212,137,84,174,24,120,130,149,72,180,181,208,255,
|
||||
189,152,18,143,176,60,249,27,227,128,139,243,253,
|
||||
59,123,172,108,211,96,138,10,215,42,225,40,81,
|
||||
65,90,25,98,126,154,64,124,116,122,5,1,168,
|
||||
83,190,131,191,244,240,235,177,155,228,125,66,43,
|
||||
201,248,220,129,188,230,62,75,71,78,34,31,216,
|
||||
254,136,91,114,106,46,217,196,92,151,209,133,51,
|
||||
236,33,252,127,179,69,7,183,105,146,97,39,15,
|
||||
205,112,200,166,223,45,48,246,186,41,148,140,107,
|
||||
76,85,95,194,142,50,49,134,23,135,169,221,210,
|
||||
203,63,165,82,161,202,53,14,206,232,103,102,195,
|
||||
117,250,99,0,74,160,241,2,113
|
||||
};
|
||||
|
||||
int b4 = 0;
|
||||
|
||||
while (b6) {
|
||||
for (; b6 && state->x2 < n1; b6--, state->x2++) {
|
||||
int b5 = t1[b4++];
|
||||
state->h1[state->x2 + n1] = b5;
|
||||
state->h1[state->x2 + (n1 * 2)] = b5 ^ state->h1[state->x2];
|
||||
state->x1 = state->h2[state->x2] ^= s4[b5 ^ state->x1];
|
||||
}
|
||||
|
||||
if (state->x2 == n1) {
|
||||
int b2 = 0;
|
||||
state->x2 = 0;
|
||||
|
||||
for (int b3 = 0; b3 < (n1 + 2); b3++) {
|
||||
for (int b1 = 0; b1 < (n1 * 3); b1++)
|
||||
b2 = state->h1[b1] ^= s4[b2];
|
||||
b2 = (b2 + b3) % 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void md2_end(MD2State* state, unsigned char h4[n1]) {
|
||||
unsigned char h3[n1];
|
||||
int n4 = n1 - state->x2;
|
||||
for (int i = 0; i < n4; i++) h3[i] = n4;
|
||||
md2_hashing(state, h3, n4);
|
||||
md2_hashing(state, state->h2, sizeof(state->h2));
|
||||
for (int i = 0; i < n1; i++) h4[i] = state->h1[i];
|
||||
}
|
||||
|
||||
// RC4 functions
|
||||
uint64_t next(RC4State* state) {
|
||||
uint64_t z = (state->x += 0x9e3779b97f4a7c15);
|
||||
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
|
||||
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
|
||||
return z ^ (z >> 31);
|
||||
}
|
||||
|
||||
void rc4_init(RC4State* state, unsigned char key[]) {
|
||||
int tmp;
|
||||
|
||||
for (state->i_rc4 = 0; state->i_rc4 < 256; state->i_rc4++) {
|
||||
state->array_rc4[state->i_rc4] = state->i_rc4;
|
||||
}
|
||||
|
||||
state->j_rc4 = 0;
|
||||
for (state->i_rc4 = 0; state->i_rc4 < 256; state->i_rc4++) {
|
||||
state->j_rc4 = (state->j_rc4 + state->array_rc4[state->i_rc4] + key[state->i_rc4 % 256]) % 256;
|
||||
tmp = state->array_rc4[state->i_rc4];
|
||||
state->array_rc4[state->i_rc4] = state->array_rc4[state->j_rc4];
|
||||
state->array_rc4[state->j_rc4] = tmp;
|
||||
}
|
||||
state->i_rc4 = 0;
|
||||
state->j_rc4 = 0;
|
||||
}
|
||||
|
||||
unsigned char rc4_output(RC4State* state) {
|
||||
uint8_t rndbyte, decal;
|
||||
int tmp, t;
|
||||
|
||||
state->i_rc4 = (state->i_rc4 + 1) % 256;
|
||||
state->j_rc4 = (state->j_rc4 + state->array_rc4[state->i_rc4]) % 256;
|
||||
tmp = state->array_rc4[state->i_rc4];
|
||||
state->array_rc4[state->i_rc4] = state->array_rc4[state->j_rc4];
|
||||
state->array_rc4[state->j_rc4] = tmp;
|
||||
t = (state->array_rc4[state->i_rc4] + state->array_rc4[state->j_rc4]) % 256;
|
||||
|
||||
|
||||
if (state->xyz == 0) state->bb = next(state);
|
||||
decal = 56 - (8 * state->xyz);
|
||||
rndbyte = (state->bb >> decal) & 0xff;
|
||||
state->xyz++;
|
||||
if (state->xyz == 8) state->xyz = 0;
|
||||
|
||||
if (state->count == 0) {
|
||||
rndbyte = rndbyte ^ state->array_rc4[t];
|
||||
state->count = 1;
|
||||
} else {
|
||||
rndbyte = rndbyte + state->array_rc4[t];
|
||||
state->count = 0;
|
||||
}
|
||||
|
||||
|
||||
return rndbyte;
|
||||
}
|
||||
|
||||
/* Convert 64-bit integer to bytes (big-endian) */
|
||||
static void u64_to_bytes_be(uint64_t val, unsigned char *out) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
out[i] = (unsigned char)((val >> (56 - 8 * i)) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
// RC2 functions
|
||||
void rc2_keyschedule(RC2State* state) {
|
||||
unsigned i;
|
||||
// Phase 3 - copy to xkey in little-endian order
|
||||
i = 63;
|
||||
do {
|
||||
state->xkey[i] = ((unsigned char *)state->xkey)[2 * i] +
|
||||
(((unsigned char *)state->xkey)[2 * i + 1] << 8);
|
||||
} while (i--);
|
||||
}
|
||||
|
||||
void rc2_encrypt(RC2State* state) {
|
||||
uint16_t x76, x54, x32, x10, i;
|
||||
|
||||
x76 = (state->plain[7] << 8) + state->plain[6];
|
||||
x54 = (state->plain[5] << 8) + state->plain[4];
|
||||
x32 = (state->plain[3] << 8) + state->plain[2];
|
||||
x10 = (state->plain[1] << 8) + state->plain[0];
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
x10 += (x32 & ~x76) + (x54 & x76) + state->xkey[4 * i + 0];
|
||||
x10 = (x10 << 1) + (x10 >> 15 & 1);
|
||||
|
||||
x32 += (x54 & ~x10) + (x76 & x10) + state->xkey[4 * i + 1];
|
||||
x32 = (x32 << 2) + (x32 >> 14 & 3);
|
||||
|
||||
x54 += (x76 & ~x32) + (x10 & x32) + state->xkey[4 * i + 2];
|
||||
x54 = (x54 << 3) + (x54 >> 13 & 7);
|
||||
|
||||
x76 += (x10 & ~x54) + (x32 & x54) + state->xkey[4 * i + 3];
|
||||
x76 = (x76 << 5) + (x76 >> 11 & 31);
|
||||
|
||||
if (i == 4 || i == 10) {
|
||||
x10 += state->xkey[x76 & 63];
|
||||
x32 += state->xkey[x10 & 63];
|
||||
x54 += state->xkey[x32 & 63];
|
||||
x76 += state->xkey[x54 & 63];
|
||||
}
|
||||
}
|
||||
|
||||
state->cipher[0] = (unsigned char)x10;
|
||||
state->cipher[1] = (unsigned char)(x10 >> 8);
|
||||
state->cipher[2] = (unsigned char)x32;
|
||||
state->cipher[3] = (unsigned char)(x32 >> 8);
|
||||
state->cipher[4] = (unsigned char)x54;
|
||||
state->cipher[5] = (unsigned char)(x54 >> 8);
|
||||
state->cipher[6] = (unsigned char)x76;
|
||||
state->cipher[7] = (unsigned char)(x76 >> 8);
|
||||
|
||||
}
|
||||
|
||||
// Main cryptographic functions
|
||||
void create_keys_rc2(CryptoContext* ctx, unsigned char key1[], size_t size1) {
|
||||
unsigned char h4[n1];
|
||||
|
||||
// Initialize MD2 and hash the key
|
||||
md2_init(&ctx->md2);
|
||||
md2_hashing(&ctx->md2, key1, size1);
|
||||
md2_end(&ctx->md2, h4);
|
||||
|
||||
// Copy first 16 bytes to keys
|
||||
for (int i = 0; i < 16; i++) ctx->keys[i] = h4[i];
|
||||
|
||||
// Initialize RC4 with hashed key
|
||||
rc4_init(&ctx->rc4, h4);
|
||||
|
||||
// Initialize RC4 state variables
|
||||
ctx->rc4.x = 0;
|
||||
for (int i = 0; i < 8; i++) ctx->rc4.x = (ctx->rc4.x << 8) + (h4[256 + i] & 0xff);
|
||||
|
||||
ctx->rc4.xyz = 0;
|
||||
ctx->rc4.count = 0;
|
||||
|
||||
// Warm-up RC4
|
||||
for (int i = 0; i < 22000; i++) rc4_output(&ctx->rc4);
|
||||
|
||||
// Generate RC2 keys
|
||||
int k = rc4_output(&ctx->rc4) + 256;
|
||||
for (int i = 0; i < k; i++) rc4_output(&ctx->rc4);
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
ctx->rc2.xkey[i] = (rc4_output(&ctx->rc4) << 8) + rc4_output(&ctx->rc4);
|
||||
}
|
||||
|
||||
// Generate internal zero value
|
||||
k = rc4_output(&ctx->rc4) + 256;
|
||||
for (int i = 0; i < k; i++) rc4_output(&ctx->rc4);
|
||||
|
||||
ctx->internal_zero = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ctx->internal_zero = (ctx->internal_zero << 8) + rc4_output(&ctx->rc4);
|
||||
}
|
||||
|
||||
// Generate RC2 key schedule
|
||||
rc2_keyschedule(&ctx->rc2);
|
||||
}
|
||||
|
||||
void encryption_rc2(CryptoContext* ctx, uint8_t bits[49]) {
|
||||
ctx->internal_state = ctx->internal_zero;
|
||||
|
||||
for (int sso = 0; sso < 49; sso++) {
|
||||
// Prepare plaintext from internal state
|
||||
ctx->rc2.plain[0] = (ctx->internal_state >> 56) & 0xff;
|
||||
ctx->rc2.plain[1] = (ctx->internal_state >> 48) & 0xff;
|
||||
ctx->rc2.plain[2] = (ctx->internal_state >> 40) & 0xff;
|
||||
ctx->rc2.plain[3] = (ctx->internal_state >> 32) & 0xff;
|
||||
ctx->rc2.plain[4] = (ctx->internal_state >> 24) & 0xff;
|
||||
ctx->rc2.plain[5] = (ctx->internal_state >> 16) & 0xff;
|
||||
ctx->rc2.plain[6] = (ctx->internal_state >> 8) & 0xff;
|
||||
ctx->rc2.plain[7] = ctx->internal_state & 0xff;
|
||||
|
||||
// Encrypt with RC2
|
||||
rc2_encrypt(&ctx->rc2);
|
||||
|
||||
// Reconstruct internal state from ciphertext
|
||||
ctx->internal_state = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ctx->internal_state = (ctx->internal_state << 8) + (ctx->rc2.cipher[i] & 0xff);
|
||||
}
|
||||
|
||||
// XOR the bit and update internal state
|
||||
bits[48 - sso] = bits[48 - sso] ^ (ctx->internal_state & 1);
|
||||
ctx->internal_state = rol64(ctx->internal_state, 1);
|
||||
swapbit(&ctx->internal_state, bits[48 - sso]);
|
||||
}
|
||||
}
|
||||
|
||||
void decrypt_rc2(CryptoContext* ctx, uint8_t bits[49]) {
|
||||
uint8_t tempy;
|
||||
ctx->internal_state = ctx->internal_zero;
|
||||
|
||||
|
||||
for (int sso = 0; sso < 49; sso++) {
|
||||
// Prepare plaintext from internal state
|
||||
ctx->rc2.plain[0] = (ctx->internal_state >> 56) & 0xff;
|
||||
ctx->rc2.plain[1] = (ctx->internal_state >> 48) & 0xff;
|
||||
ctx->rc2.plain[2] = (ctx->internal_state >> 40) & 0xff;
|
||||
ctx->rc2.plain[3] = (ctx->internal_state >> 32) & 0xff;
|
||||
ctx->rc2.plain[4] = (ctx->internal_state >> 24) & 0xff;
|
||||
ctx->rc2.plain[5] = (ctx->internal_state >> 16) & 0xff;
|
||||
ctx->rc2.plain[6] = (ctx->internal_state >> 8) & 0xff;
|
||||
ctx->rc2.plain[7] = ctx->internal_state & 0xff;
|
||||
|
||||
// Encrypt with RC2
|
||||
rc2_encrypt(&ctx->rc2);
|
||||
|
||||
// Reconstruct internal state from ciphertext
|
||||
ctx->internal_state = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ctx->internal_state = (ctx->internal_state << 8) + (ctx->rc2.cipher[i] & 0xff);
|
||||
}
|
||||
|
||||
// XOR the bit and update internal state
|
||||
tempy = bits[48 - sso];
|
||||
bits[48 - sso] = bits[48 - sso] ^ (ctx->internal_state & 1);
|
||||
ctx->internal_state = rol64(ctx->internal_state, 1);
|
||||
swapbit(&ctx->internal_state, tempy);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Key creation for Retevis AP */
|
||||
void retevis_rc2_keystream_creation(dsd_state *state, char *input) {
|
||||
|
||||
unsigned char key1[64]; memset(key1, 0, sizeof(key1));
|
||||
unsigned char key2[64]; memset(key2, 0, sizeof(key2));
|
||||
|
||||
char buf[1024];
|
||||
strncpy(buf, input, sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
char *pEnd;
|
||||
uint64_t K1 = strtoull(buf, &pEnd, 16);
|
||||
uint64_t K2 = strtoull(pEnd, &pEnd, 16);
|
||||
uint64_t K3 = strtoull(pEnd, &pEnd, 16);
|
||||
uint64_t K4 = strtoull(pEnd, &pEnd, 16);
|
||||
|
||||
if (K3 != 0 || K4 != 0) //256-bit keys loaded as ASCII characters
|
||||
{
|
||||
//start loading K1, K2, K3, and K4 as ASCII to fill 64 bytes of key1
|
||||
int k = 0; uint8_t x = 0;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
x = ((K1 >> (60-(i*4))) & 0xF);
|
||||
if (x >= 0 && x <= 9) //numbers 0-9
|
||||
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
|
||||
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
|
||||
key1[k++] = x;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
x = ((K2 >> (60-(i*4))) & 0xF);
|
||||
if (x >= 0 && x <= 9) //numbers 0-9
|
||||
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
|
||||
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
|
||||
key1[k++] = x;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
x = ((K3 >> (60-(i*4))) & 0xF);
|
||||
if (x >= 0 && x <= 9) //numbers 0-9
|
||||
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
|
||||
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
|
||||
key1[k++] = x;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
x = ((K4 >> (60-(i*4))) & 0xF);
|
||||
if (x >= 0 && x <= 9) //numbers 0-9
|
||||
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
|
||||
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
|
||||
key1[k++] = x;
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "ASCII: ");
|
||||
// for (int i = 0; i < 64; i++)
|
||||
// fprintf (stderr, " %02X", key1[i]);
|
||||
// fprintf (stderr, "\n");
|
||||
|
||||
// Initialize RC2 context
|
||||
static CryptoContext rc2_ctx;
|
||||
create_keys_rc2(&rc2_ctx, key1, 64);
|
||||
|
||||
// Store context in DSD state
|
||||
state->rc2_context = malloc(sizeof(CryptoContext));
|
||||
memcpy(state->rc2_context, &rc2_ctx, sizeof(CryptoContext));
|
||||
|
||||
fprintf(stderr, "DMR RETEVIS AP (RC2) 256-bit Key %016llX%016llX%016llX%016llX with Forced Application\n",
|
||||
(unsigned long long)K1, (unsigned long long)K2, (unsigned long long)K3, (unsigned long long)K4);
|
||||
|
||||
state->retevis_ap = 1;
|
||||
}
|
||||
|
||||
else //128-bit loaded as reverse bytes
|
||||
{
|
||||
|
||||
u64_to_bytes_be(K1, &key1[0]);
|
||||
u64_to_bytes_be(K2, &key1[8]);
|
||||
|
||||
// reverse load the key bytes into key2
|
||||
for (int i=0;i<16;i++)
|
||||
key2[i] = key1[15-i];
|
||||
|
||||
// Initialize RC2 context
|
||||
static CryptoContext rc2_ctx;
|
||||
create_keys_rc2(&rc2_ctx, key2, 16);
|
||||
|
||||
// Store context in DSD state
|
||||
state->rc2_context = malloc(sizeof(CryptoContext));
|
||||
memcpy(state->rc2_context, &rc2_ctx, sizeof(CryptoContext));
|
||||
|
||||
fprintf(stderr, "DMR RETEVIS AP (RC2) 128-bit Key %016llX%016llX with Forced Application\n",
|
||||
(unsigned long long)K1, (unsigned long long)K2);
|
||||
|
||||
state->retevis_ap = 1;
|
||||
}
|
||||
}
|
||||
146
src/crypt-tyt.c
146
src/crypt-tyt.c
@@ -159,96 +159,28 @@ void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int
|
||||
|
||||
void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input)
|
||||
{
|
||||
unsigned char key1[64]; memset(key1, 0, sizeof(key1));
|
||||
unsigned char key2[64]; memset(key2, 0, sizeof(key2));
|
||||
|
||||
unsigned char key1[16] = {0};
|
||||
unsigned char key2[16] = {0};
|
||||
|
||||
char buf[1024];
|
||||
strncpy(buf, input, sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
|
||||
char *pEnd;
|
||||
uint64_t K1 = strtoull(buf, &pEnd, 16);
|
||||
uint64_t K2 = strtoull(pEnd, &pEnd, 16);
|
||||
uint64_t K3 = strtoull(pEnd, &pEnd, 16);
|
||||
uint64_t K4 = strtoull(pEnd, &pEnd, 16);
|
||||
|
||||
if (K3 != 0 || K4 != 0) //256-bit keys loaded as ASCII characters
|
||||
{
|
||||
//start loading K1, K2, K3, and K4 as ASCII to fill 64 bytes of key1
|
||||
int k = 0; uint8_t x = 0;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
x = ((K1 >> (60-(i*4))) & 0xF);
|
||||
if (x >= 0 && x <= 9) //numbers 0-9
|
||||
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
|
||||
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
|
||||
key1[k++] = x;
|
||||
}
|
||||
u64_to_bytes_be(K1, &key1[0]);
|
||||
u64_to_bytes_be(K2, &key1[8]);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
x = ((K2 >> (60-(i*4))) & 0xF);
|
||||
if (x >= 0 && x <= 9) //numbers 0-9
|
||||
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
|
||||
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
|
||||
key1[k++] = x;
|
||||
}
|
||||
for (int i=0;i<16;i++) key2[i] = key1[15-i];
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
x = ((K3 >> (60-(i*4))) & 0xF);
|
||||
if (x >= 0 && x <= 9) //numbers 0-9
|
||||
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
|
||||
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
|
||||
key1[k++] = x;
|
||||
}
|
||||
/* Create key schedule */
|
||||
create_keys(&ctx, key2, sizeof(key2));
|
||||
ctx.rounds = nbround;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
x = ((K4 >> (60-(i*4))) & 0xF);
|
||||
if (x >= 0 && x <= 9) //numbers 0-9
|
||||
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
|
||||
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
|
||||
key1[k++] = x;
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "ASCII: ");
|
||||
// for (int i = 0; i < 64; i++)
|
||||
// fprintf (stderr, " %02X", key1[i]);
|
||||
// fprintf (stderr, "\n");
|
||||
|
||||
/* Create key schedule */
|
||||
create_keys(&ctx, key1, 64);
|
||||
ctx.rounds = nbround;
|
||||
|
||||
fprintf(stderr, "DMR TYT AP (PC4) 256-bit Key %016llX%016llX%016llX%016llX with Forced Application\n",
|
||||
(unsigned long long)K1, (unsigned long long)K2, (unsigned long long)K3, (unsigned long long)K4);
|
||||
|
||||
state->tyt_ap = 1;
|
||||
}
|
||||
|
||||
else //128-bit loaded as reverse bytes
|
||||
{
|
||||
|
||||
u64_to_bytes_be(K1, &key1[0]);
|
||||
u64_to_bytes_be(K2, &key1[8]);
|
||||
|
||||
// reverse load the key bytes into key2
|
||||
for (int i=0;i<16;i++)
|
||||
key2[i] = key1[15-i];
|
||||
|
||||
/* Create key schedule */
|
||||
create_keys(&ctx, key2, 16);
|
||||
ctx.rounds = nbround;
|
||||
|
||||
fprintf (stderr,"DMR TYT AP (PC4) 128-bit Key %016llX%016llX with Forced Application\n",
|
||||
(unsigned long long int)K1, (unsigned long long int)K2);
|
||||
|
||||
state->tyt_ap = 1;
|
||||
}
|
||||
|
||||
fprintf (stderr,"DMR TYT AP (PC4) 128-bit Key %016llX%016llX with Forced Application\n", (unsigned long long int)K1, (unsigned long long int)K2);
|
||||
state->tyt_ap = 1;
|
||||
|
||||
}
|
||||
|
||||
@@ -306,56 +238,4 @@ void tyt_ep_aes_keystream_creation(dsd_state * state, char * input)
|
||||
fprintf (stderr,"DMR TYT EP (AES-128) Key %016llX%016llX with Forced Application\n", K1, K2);
|
||||
state->tyt_ep = 1;
|
||||
|
||||
}
|
||||
|
||||
//connect systems 72-bit (9-byte) Extended Encryption //TODO: Move this later
|
||||
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24])
|
||||
{
|
||||
|
||||
char interleaved[72];
|
||||
memset (interleaved, 0, sizeof(interleaved));
|
||||
|
||||
//interleave the frame
|
||||
const int *w, *x, *y, *z;
|
||||
w = rW; x = rX; y = rY; z = rZ;
|
||||
|
||||
for (int8_t i = 0; i < 36; i++)
|
||||
{
|
||||
interleaved[(i*2)+0] = ambe_fr[*w][*x];
|
||||
interleaved[(i*2)+1] = ambe_fr[*y][*z];
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
|
||||
uint8_t ks_bytes[9]; memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
uint8_t ks[72]; memset(ks, 0, sizeof(ks));
|
||||
|
||||
//keys are loaded in reverse byte order, stored in state->static_ks_bits[0]
|
||||
for (int i = 0; i < 9; i++)
|
||||
ks_bytes[i] = state->static_ks_bits[0][8-i];
|
||||
|
||||
//convert byte array into a bit array
|
||||
unpack_byte_array_into_bit_array(ks_bytes, ks, 9);
|
||||
|
||||
//apply keystream to interleave
|
||||
for (int8_t i = 0; i < 72; i++)
|
||||
interleaved[i] ^= ks[71-i];
|
||||
|
||||
//deinterleave back into ambe_fr frame
|
||||
w = rW; x = rX; y = rY; z = rZ;
|
||||
int k = 0;
|
||||
for (int8_t i = 0; i < 36; i++)
|
||||
{
|
||||
ambe_fr[*w][*x] = interleaved[k++];
|
||||
ambe_fr[*y][*z] = interleaved[k++];
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
476
src/dmr_34.c
476
src/dmr_34.c
@@ -1,35 +1,12 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_34.c
|
||||
* DMR (and P25) 3/4 Rate Viterbi Trellis Decoder (Improved)
|
||||
*
|
||||
* Code Sourced From: Unknown
|
||||
* No License Provided
|
||||
* DMR (and P25) 3/4 Rate Simple Trellis Decoder
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2026-02 DSD-FME Florida Man Edition
|
||||
* 2023-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#define INRANGE 8 // Serial input is grouped in 3 bits, each input value ranges 0~7
|
||||
#define INSTATIC 8 // Number of possible states in the state machine, range 0~7
|
||||
#define INDATA 49 // Input 144 bits, grouped into 3-bit units → outputs 48 values (0~15) + one final 0 to determine end state
|
||||
#define INSYMBOL 49 // Number of input symbols
|
||||
#define PATH 64 // For the i-th (48≥i≥2) input, there are 64 branch metrics → 64 possible paths
|
||||
#define SELECTPATH 8 // Among the 64 paths, select the 8 with smallest metrics, then choose the smallest one as final output
|
||||
#define BITELEN 9 //
|
||||
|
||||
/******************************************************************************
|
||||
* Header file includes
|
||||
*******************************************************************************/
|
||||
#include "dsd.h"
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/******************************************************************************
|
||||
* Global variable definitions
|
||||
*******************************************************************************/
|
||||
|
||||
uint8_t interleave[98] = {
|
||||
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
|
||||
@@ -37,288 +14,111 @@ uint8_t interleave[98] = {
|
||||
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
|
||||
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
|
||||
|
||||
static int16_t staticX[INSTATIC][INRANGE] =
|
||||
{
|
||||
{ 1,-3,-3,1,3,-1,-1,3 },
|
||||
{ -3,1,3,-1,-1,3,1,-3 },
|
||||
{ -1,3,3,-1,-3,1,1,-3 },
|
||||
{ 3,-1,-3,1,1,-3,-1,3 },
|
||||
{ -3,1,1,-3,-1,3,3,-1 },
|
||||
{ 1,-3,-1,3,3,-1,-3,1 },
|
||||
{ 3,-1,-1,3,1,-3,-3,1 },
|
||||
{ -1,3,1,-3,-3,1,3,-1 }
|
||||
|
||||
//this is a convertion table for converting the dibit pairs into constellation points
|
||||
uint8_t constellation_map[16] = {
|
||||
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||
};
|
||||
|
||||
static int16_t staticY[INSTATIC][INRANGE] =
|
||||
//digitized dibit to OTA symbol conversion for reference
|
||||
//0 = +1; 1 = +3;
|
||||
//2 = -1; 3 = -3;
|
||||
|
||||
//finite state machine values
|
||||
uint8_t fsm[64] = {
|
||||
0, 8, 4, 12, 2, 10, 6, 14,
|
||||
4, 12, 2, 10, 6, 14, 0, 8,
|
||||
1, 9, 5, 13, 3, 11, 7, 15,
|
||||
5, 13, 3, 11, 7, 15, 1, 9,
|
||||
3, 11, 7, 15, 1, 9, 5, 13,
|
||||
7, 15, 1, 9, 5, 13, 3, 11,
|
||||
2, 10, 6, 14, 0, 8, 4, 12,
|
||||
6, 14, 0, 8, 4, 12, 2, 10};
|
||||
|
||||
//attempt to find the surviving path, or the 'best' path available (most positions gained)
|
||||
uint8_t fix_34(uint8_t * p, uint8_t state, int position)
|
||||
{
|
||||
{ -1,3,-1,3,-3,1,-3,1 },
|
||||
{ -1,3,-3,1,-3,1,-1,3 },
|
||||
{ -1,3,-1,3,-3,1,-3,1 },
|
||||
{ -1,3,-3,1,-3,1,-1,3 },
|
||||
{ -3,1,-3,1,-1,3,-1,3 },
|
||||
{ -3,1,-1,3,-1,3,-3,1 },
|
||||
{ -3,1,-3,1,-1,3,-1,3 },
|
||||
{ -3,1,-1,3,-1,3,-3,1 }
|
||||
int i, j, k, counter, best_p, best_v, survivors;
|
||||
uint8_t temp_s, tri, t;
|
||||
|
||||
};
|
||||
//status of surviving paths -- debug
|
||||
int s[8];
|
||||
memset (s, 0, 8*sizeof(int));
|
||||
|
||||
//assign all potentially correct points to temporary storage
|
||||
uint8_t temp_p[8];
|
||||
temp_p[0] = (p[position] ^ 1) & 0xF;
|
||||
temp_p[1] = (p[position] ^ 3) & 0xF;
|
||||
temp_p[2] = (p[position] ^ 5) & 0xF;
|
||||
temp_p[3] = (p[position] ^ 7) & 0xF;
|
||||
temp_p[4] = (p[position] ^ 9) & 0xF;
|
||||
temp_p[5] = (p[position] ^ 11) & 0xF;
|
||||
temp_p[6] = (p[position] ^ 13) & 0xF;
|
||||
temp_p[7] = (p[position] ^ 15) & 0xF;
|
||||
|
||||
/**
|
||||
* @brief Convert 0~7 symbols into 3-bit (0/1) bitstream
|
||||
*
|
||||
* @Param [in] psymbol Input decimal symbol values
|
||||
* @Param [in] num Number of input symbols
|
||||
* @Param [out] pbit Output 3-bit stream
|
||||
*
|
||||
*/
|
||||
void TriBit(uint16_t *psymbol, uint16_t num, uint16_t *pbit)
|
||||
{
|
||||
int16_t temp, i;
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
temp = psymbol[i];
|
||||
*pbit++ = (temp >> 2) & 0x01;
|
||||
*pbit++ = (temp >> 1) & 0x01;
|
||||
*pbit++ = temp & 0x01;
|
||||
}
|
||||
best_p = 0; //best position
|
||||
best_v = 0; //best path value
|
||||
|
||||
for (k = 0; k < 8; k++)
|
||||
{
|
||||
temp_s = state;
|
||||
counter = 0;
|
||||
tri = 0;
|
||||
for (i = position; i < 49; i++)
|
||||
{
|
||||
//assign temp t either as our temp_p, or next point of i
|
||||
if (i == position) t = temp_p[k];
|
||||
else t = p[i];
|
||||
|
||||
if (tri != 0xFF) //while the path survives
|
||||
{
|
||||
tri = 0xFF;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
if ( fsm[(temp_s*8)+j] == t )
|
||||
{
|
||||
//return our tribit value and state for the next point
|
||||
tri = temp_s = (uint8_t)j;
|
||||
counter++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (counter > best_p)
|
||||
{
|
||||
best_p = counter;
|
||||
best_v = k; //if we make it further on current path, assign as best path
|
||||
}
|
||||
|
||||
// surviving path -- made it to 49
|
||||
if (i == 48) s[k] = 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//NOTE: If there are no surviving paths, then that means there is another bit/point err
|
||||
//Ideally, at the end of a single or multiple fixes, there should only be one survivor, but in practice,
|
||||
//I've seen up to 4 surviving paths and somehow, get a good CRC and a good LRRP or LOCN decode
|
||||
//multiple survivors seems common when the starting err position is very close to 49
|
||||
|
||||
//tally number of surviving paths
|
||||
survivors = 0;
|
||||
for (k = 0; k < 8; k++)
|
||||
{
|
||||
if (s[k] == 1) survivors++;
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "START: %d; BEST_P: %d; BEST_V: %d; Survivors: %d; Point: %d; ", position, best_p, best_v, survivors, temp_p[best_v]);
|
||||
|
||||
return temp_p[best_v]; //return the point value of the best path's starting point value
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Organize the decoded 3-bit stream into 16-bit word format for output
|
||||
*
|
||||
* @Param [in] ptriBitDecRslt Input 3-bit decoded bitstream
|
||||
* @Param [out] poutdata Output packed 16-bit words
|
||||
*
|
||||
*/
|
||||
void TriBitToShort(uint16_t *ptriBitDecRslt, uint16_t *poutdata)
|
||||
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
|
||||
{
|
||||
int16_t i, j, k = 0;
|
||||
uint16_t bitArray[144] = { 0 };
|
||||
memset(poutdata, 0, sizeof(uint16_t) * 9);
|
||||
|
||||
TriBit(ptriBitDecRslt, 48, bitArray);
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
for (j = 15; j >= 0; j--)
|
||||
{
|
||||
poutdata[i] += bitArray[k++] << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Among 8 paths, select the one with the smallest metric and return its index
|
||||
*
|
||||
* @Param [in] pdata Array of 8 path metrics
|
||||
* @return Index of the path with minimum metric
|
||||
*
|
||||
*/
|
||||
uint16_t MinPath(uint16_t *pdata)
|
||||
{
|
||||
uint16_t i, j = 0;
|
||||
uint16_t min = pdata[0]; // Take first value as initial minimum
|
||||
|
||||
for (i = 0; i < SELECTPATH; i++)
|
||||
{
|
||||
if (pdata[i] < min) // If current value is smaller
|
||||
{
|
||||
min = pdata[i]; // Update minimum
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
return j; // Return index of minimum path
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief From 64 paths, select the 8 paths with smallest metrics,
|
||||
* and output corresponding state and input values
|
||||
*
|
||||
* @Param [in] parrary Array of 64 branch metrics
|
||||
* @Param [out] pipath_ed Sorted 8 smallest path metrics
|
||||
* @Param [out] pstate States corresponding to the 8 best paths
|
||||
* @Param [out] pdata Input values corresponding to the 8 best paths
|
||||
*
|
||||
*/
|
||||
void PathFunc(uint16_t *parrary, uint16_t *pipath_ed, uint16_t *pstate, uint16_t *pdata)
|
||||
{
|
||||
int16_t i, j, locT = 0, ValT = 0;
|
||||
uint16_t dataT[PATH] = { 0 };
|
||||
|
||||
memcpy(dataT, parrary, sizeof(uint16_t)*PATH);
|
||||
|
||||
for (i = 0; i < INRANGE; i++)
|
||||
{
|
||||
ValT = dataT[0];
|
||||
locT = 0;
|
||||
for (j = 1; j < PATH; j++)
|
||||
{
|
||||
if (dataT[j] < ValT)
|
||||
{
|
||||
ValT = dataT[j];
|
||||
locT = j;
|
||||
}
|
||||
}
|
||||
dataT[locT] = 10000;
|
||||
pipath_ed[i] = ValT;
|
||||
pstate[i] = locT / 8; // quotient → state
|
||||
pdata[i] = locT % 8; // remainder → input symbol
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Calculate path metric (Manhattan distance)
|
||||
*
|
||||
* @Param [in] dataAX X-coordinate of received constellation point A
|
||||
* @Param [in] dataBX X-coordinate of reference constellation point B
|
||||
* @Param [in] dataAY Y-coordinate of received constellation point A
|
||||
* @Param [in] dataBY Y-coordinate of reference constellation point B
|
||||
* @return Path metric result
|
||||
*
|
||||
*/
|
||||
uint16_t DistanceCal(int16_t dataAX, int16_t dataBX, int16_t dataAY, int16_t dataBY)
|
||||
{
|
||||
uint16_t DistanceRslt = 0;
|
||||
DistanceRslt = abs(dataAX - dataBX) + abs(dataAY - dataBY);
|
||||
//DistanceRslt = sqrt((dataAX - dataBX)*(dataAX - dataBX) + (dataAY - dataBY)*(dataAY - dataBY));
|
||||
return DistanceRslt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert 2-bit value to constellation symbol (±1, ±3)
|
||||
*
|
||||
* @Param [in] InBit Input 2-bit value (0~3)
|
||||
* @return Constellation symbol value
|
||||
*
|
||||
*/
|
||||
int16_t BitToSymbol(int16_t InBit)
|
||||
{
|
||||
switch (InBit)
|
||||
{
|
||||
case 0:
|
||||
return 1;
|
||||
case 1:
|
||||
return 3;
|
||||
case 2:
|
||||
return -1;
|
||||
case 3:
|
||||
return -3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Map input symbols (0~15) to constellation points (±1, ±3) in I/Q
|
||||
*
|
||||
* @Param [in] pinstar Input symbol array (0~15)
|
||||
* @Param [in] num Number of symbols
|
||||
* @Param [out] pdataX Output I-channel (X) coordinates
|
||||
* @Param [out] pdataY Output Q-channel (Y) coordinates
|
||||
*
|
||||
*/
|
||||
void MapStar(uint16_t *pinstar, uint16_t num, int16_t *pdataX, int16_t *pdataY)
|
||||
{
|
||||
int16_t i, itemp;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
itemp = (pinstar[i] >> 2) & 0x03;
|
||||
*pdataX++ = BitToSymbol(itemp);
|
||||
itemp = pinstar[i] & 0x03;
|
||||
*pdataY++ = BitToSymbol(itemp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Viterbi decoding function — selects the most likely sequence
|
||||
*
|
||||
* @Param [in] pindata Input symbol sequence (values 0~15)
|
||||
* @Param [out] outdata Decoded output (packed 16-bit words)
|
||||
*
|
||||
*/
|
||||
void Virterbi(uint16_t *pindata, uint16_t outdata[])
|
||||
{
|
||||
int16_t i, p, j = 1;
|
||||
uint16_t lastDistnc[INRANGE] = { 0 }; // Branch metrics for final input (8 values)
|
||||
uint16_t crntDistnc[64] = { 0 }; // All 64 possible branch metrics for current step
|
||||
uint16_t DecodeOut[INDATA][INRANGE] = { {0 }}; // Possible input sequences up to current step (8 survivors)
|
||||
uint16_t DecodeOutEnd[INDATA][INRANGE] = { {0 }}; // Temporary buffer for path history
|
||||
uint16_t exInDistnc[INRANGE] = { 0 }; // 8 survivor path metrics
|
||||
uint16_t triBitDecRslt[INDATA] = { 0 }; // Final decoded 3-bit symbols
|
||||
int16_t starX[INDATA] = { 0 }, starY[INDATA] = { 0 }; // Constellation point coordinates (I/Q)
|
||||
|
||||
uint16_t minDistnc = 0;
|
||||
uint16_t ConstelPData[INRANGE] = { 0 }; // Input symbols leading to next states
|
||||
uint16_t ConstelPState[INSTATIC] = { 0 }; // Survivor states
|
||||
|
||||
MapStar(pindata, INDATA, starX, starY); // Map input symbols to constellation points
|
||||
|
||||
// Branch metrics for the first input symbol (starting from state 0)
|
||||
for (i = 0; i < INRANGE; i++)
|
||||
{
|
||||
ConstelPData[i] = i; // Next state value
|
||||
DecodeOut[0][i] = i; // Possible input symbols from state 0
|
||||
exInDistnc[i] = DistanceCal(starX[0], staticX[0][i], starY[0], staticY[0][i]);
|
||||
}
|
||||
|
||||
// Compute branch metrics for each subsequent input symbol
|
||||
for (j = 1; j < INDATA - 1; j++) // for each input symbol
|
||||
{
|
||||
for (p = 0; p < INSTATIC; p++) // for each current survivor state
|
||||
{
|
||||
for (i = 0; i < INRANGE; i++) // for each possible input
|
||||
{
|
||||
crntDistnc[p * 8 + i] = DistanceCal(starX[j], staticX[ConstelPData[p]][i], starY[j], staticY[ConstelPData[p]][i]) + exInDistnc[p];
|
||||
}
|
||||
}
|
||||
|
||||
PathFunc(crntDistnc, exInDistnc, ConstelPState, ConstelPData); // Select 8 best paths
|
||||
|
||||
for (p = 0; p <= j; p++)
|
||||
{
|
||||
for (i = 0; i < INRANGE; i++)
|
||||
{
|
||||
if (p < j)
|
||||
{
|
||||
DecodeOutEnd[p][i] = DecodeOut[p][ConstelPState[i]];
|
||||
}
|
||||
else
|
||||
{
|
||||
DecodeOut[p][i] = ConstelPData[i];
|
||||
DecodeOutEnd[p][i] = DecodeOut[p][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
memcpy(DecodeOut, DecodeOutEnd, j * 8 * sizeof(int16_t));
|
||||
}
|
||||
|
||||
// Final step — force ending in state 0
|
||||
for (i = 0; i < INRANGE; i++)
|
||||
{
|
||||
lastDistnc[i] = DistanceCal(starX[INDATA - 1], staticX[DecodeOut[INDATA - 2][i]][0], starY[INDATA - 1], staticY[DecodeOut[INDATA - 2][i]][0]) + exInDistnc[i];
|
||||
}
|
||||
|
||||
minDistnc = MinPath(lastDistnc); // Find best final path
|
||||
|
||||
for (i = 0; i < INDATA - 1; i++)
|
||||
{
|
||||
triBitDecRslt[i] = DecodeOutEnd[i][minDistnc];
|
||||
}
|
||||
|
||||
TriBitToShort(triBitDecRslt, outdata);
|
||||
}
|
||||
|
||||
uint32_t viterbi_r34 (uint8_t * input, uint8_t * output)
|
||||
{
|
||||
|
||||
int i = 0;
|
||||
int i, j;
|
||||
uint32_t irr_err = 0; //irrecoverable errors
|
||||
|
||||
uint8_t deinterleaved_dibits[98];
|
||||
@@ -335,20 +135,80 @@ uint32_t viterbi_r34 (uint8_t * input, uint8_t * output)
|
||||
for (i = 0; i < 49; i++)
|
||||
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
|
||||
|
||||
uint16_t pindata[49]; memset (pindata, 0, sizeof(pindata));
|
||||
uint16_t outdata[18]; memset (outdata, 0, sizeof(outdata));
|
||||
//convert our dibit pairs into constellation point values
|
||||
uint8_t point[49];
|
||||
memset (point, 0xFF, sizeof(point));
|
||||
|
||||
for (int i = 0; i < 49; i++)
|
||||
pindata[i] = (uint16_t)nibs[i];
|
||||
for (i = 0; i < 49; i++)
|
||||
point[i] = constellation_map[nibs[i]];
|
||||
|
||||
Virterbi(pindata, outdata);
|
||||
//debug view points
|
||||
// fprintf (stderr, "\n P =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", point[i]);
|
||||
|
||||
int k = 0;
|
||||
for (int i = 0; i < 9; i++)
|
||||
//convert constellation points into tribit values using the FSM
|
||||
uint8_t state = 0;
|
||||
uint32_t tribits[49];
|
||||
memset (tribits, 0xF, sizeof(tribits));
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
{
|
||||
output[k++] = (outdata[i] >> 8) & 0xFF;
|
||||
output[k++] = (outdata[i] >> 0) & 0xFF;
|
||||
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
if ( fsm[(state*8)+j] == point[i] )
|
||||
{
|
||||
//return our tribit value and state for the next point
|
||||
tribits[i] = state = (uint8_t)j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if tribit value is greater than 7, then we have a decoding error
|
||||
if (tribits[i] > 7)
|
||||
{
|
||||
irr_err++; //tally number of errors
|
||||
|
||||
//debug point, position of error, and state value
|
||||
// fprintf (stderr, "\n P: %d, %d:%d; ", point[i], i, state);
|
||||
|
||||
//attempt to fix point by looking for the best path from here
|
||||
point[i] = fix_34(point, state, i);
|
||||
|
||||
//Make a hard decision and flip point to fit in current state
|
||||
// point[i] ^= 7; //lucky number 7 (0111) //fallback
|
||||
|
||||
//decrement one and resume decoding
|
||||
i--;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return irr_err; //TODO: Need to pull viterbi metrics from upper functions
|
||||
//debug view tribits/states
|
||||
// fprintf (stderr, "\n T =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", tribits[i]);
|
||||
|
||||
//convert tribits into a return payload
|
||||
uint32_t temp = 0;
|
||||
|
||||
//break into chunks of 24 bit values and shuffle into 8-bit (byte) treturn values
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
temp = (tribits[(i*8)+0] << 21) + (tribits[(i*8)+1] << 18) + (tribits[(i*8)+2] << 15) + (tribits[(i*8)+3] << 12) +
|
||||
(tribits[(i*8)+4] << 9) + (tribits[(i*8)+5] << 6) + (tribits[(i*8)+6] << 3) + (tribits[(i*8)+7] << 0);
|
||||
|
||||
treturn[(i*3)+0] = (temp >> 16) & 0xFF;
|
||||
treturn[(i*3)+1] = (temp >> 8) & 0xFF;
|
||||
treturn[(i*3)+2] = (temp >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
//trellis point/state err tally
|
||||
// if (irr_err != 0)
|
||||
// fprintf (stderr, " P_ERR = %d", irr_err);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
142
src/dmr_block.c
142
src/dmr_block.c
@@ -270,12 +270,13 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||
{
|
||||
strcat (rsp_string, "NACK - ");
|
||||
if (r_type == 0) strcat (rsp_string, "Illegal Format");
|
||||
if (r_type == 1) strcat (rsp_string, "Packet CRC ERR");
|
||||
if (r_type == 2) strcat (rsp_string, "Memory Full");
|
||||
if (r_type == 3) strcat (rsp_string, "FSN Out of Seq");
|
||||
if (r_type == 4) strcat (rsp_string, "Undeliverable");
|
||||
if (r_type == 5) strcat (rsp_string, "PKT Out of Seq");
|
||||
if (r_type == 6) strcat (rsp_string, "Invalid User");
|
||||
if (r_type == 1) strcat (rsp_string, "Illegal Format");
|
||||
if (r_type == 2) strcat (rsp_string, "Packet CRC ERR");
|
||||
if (r_type == 3) strcat (rsp_string, "Memory Full");
|
||||
if (r_type == 4) strcat (rsp_string, "FSN Out of Seq");
|
||||
if (r_type == 5) strcat (rsp_string, "Undeliverable");
|
||||
if (r_type == 6) strcat (rsp_string, "PKT Out of Seq");
|
||||
if (r_type == 7) strcat (rsp_string, "Invalid User");
|
||||
}
|
||||
if (r_class == 2) strcat (rsp_string, "SACK - Retry");
|
||||
// if (r_status) strcat (rsp_string, " - %d", r_status);
|
||||
@@ -292,19 +293,8 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||
//Confirmed or Unconfirmed Data Packets Header
|
||||
if (dpf == 2 || dpf == 3)
|
||||
{
|
||||
|
||||
uint8_t fsn_number = (fsn & 7) + 1;
|
||||
|
||||
char fsn_string[50];
|
||||
memset(fsn_string, 0, sizeof(fsn_string));
|
||||
|
||||
if (fsn == 0) sprintf(fsn_string, "%s", "Unconfirmed data single fragment");
|
||||
else if (fsn == 8) sprintf(fsn_string, "%s", "Confirmed data single fragment");
|
||||
else if (fsn >= 9) sprintf(fsn_string, "%s", "Last confirmed data fragment with number");
|
||||
else sprintf(fsn_string, "%s", "Subsequent confirmed data fragment with number");
|
||||
|
||||
if (dpf == 2) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN: [%X] - %s %d; ", sap, sap_string, f, bf, poc, fsn, fsn_string, fsn_number);
|
||||
if (dpf == 3) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN: [%X] - %s %d; ", sap, sap_string, f, bf, poc, s, ns, fsn, fsn_string, fsn_number);
|
||||
if (dpf == 2) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN %d", sap, sap_string, f, bf, poc, fsn);
|
||||
if (dpf == 3) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN %d", sap, sap_string, f, bf, poc, s, ns, fsn);
|
||||
state->data_header_blocks[slot] = bf;
|
||||
if (dpf == 3) state->data_conf_data[slot] = 1; //set confirmed data delivery flag for additional CRC checks, block assembly, etc.
|
||||
|
||||
@@ -345,7 +335,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||
//The SAP found here is the actual SAP of the message (like a P25 ndary SAP, and can chain together according to ETSI)
|
||||
fprintf (stderr, " - SAP %02d [%s] - MFID %02X [%s]", p_sap, sap_string, p_mfid, mfid_string);
|
||||
|
||||
//p_sap 1 on mfid 10 MNIS Header
|
||||
//p_sap 1 on mfid 10 (moto) has been observed as the first block of LRRP data (unique fixed format?)
|
||||
if (p_mfid == 0x10 && p_sap == 1)
|
||||
{
|
||||
//add the header to the first 10 bytes of the storage (sans this header's CRC)
|
||||
@@ -375,42 +365,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||
}
|
||||
|
||||
//Start Setting DMR Data Packet Encryption Variables
|
||||
if (p_sap != 1 && p_mfid == 0x77)
|
||||
{
|
||||
|
||||
fprintf (stderr, "\n Vertex Standard PDU ENC Header:");
|
||||
fprintf (stderr, " MFID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8));
|
||||
|
||||
//Unsure of how similar this is to the DMRA Enc Header, but using
|
||||
//guess work and best judgment from single sample of Vertex Enhanced
|
||||
|
||||
//main issue is, I don't know if the 0x01 signalled is a key id (which is known to be 0x01)
|
||||
//or if it is an encryption alg id (alg may have to be inferred based on what is loaded in radio)
|
||||
|
||||
//set to 0x100 so it won't trigger any weird flags, but still has a non-zero value to be checked later
|
||||
if (state->currentslot == 0) state->dmr_so = 0x100;
|
||||
else state->dmr_soR = 0x100;
|
||||
|
||||
if (state->currentslot == 0)
|
||||
state->payload_keyid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
|
||||
else state->payload_keyidR = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
|
||||
fprintf (stderr, " Key ID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8));
|
||||
|
||||
//going to use algid of 0x7 to signal a vertex standard enc method
|
||||
if (state->currentslot == 0)
|
||||
state->payload_algid = 0x7;
|
||||
else state->payload_algidR = 0x7;
|
||||
|
||||
//unknown if MI, or IV is present on this header (need the AES256 Vertex PDU to compare)
|
||||
if (state->currentslot == 0)
|
||||
state->payload_mi = (unsigned long long int)ConvertBitIntoBytes(&dheader_bits[48], 32);
|
||||
else state->payload_miR = (unsigned long long int)ConvertBitIntoBytes(&dheader_bits[48], 32);
|
||||
|
||||
//reset ks start value
|
||||
state->data_ks_start[slot] = 0;
|
||||
|
||||
}
|
||||
else if (p_sap != 1 && p_mfid == 0x10)
|
||||
if (p_sap != 1 && p_mfid == 0x10)
|
||||
{
|
||||
|
||||
//check ENC bit, assuming this is an ENC bit, or SVC OPT like thing (or could be an opcode for the rest of the extended header)
|
||||
@@ -421,7 +376,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||
else state->dmr_soR = 0x100;
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n DMRA PDU ENC Header:");
|
||||
fprintf (stderr, "\n PDU ENC Header:");
|
||||
fprintf (stderr, " MFID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8));
|
||||
fprintf (stderr, " ENC: %X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[20], 4));
|
||||
|
||||
@@ -1038,9 +993,9 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
R = state->rkey_array[state->payload_keyidR];
|
||||
|
||||
//loader for aes keys
|
||||
uint8_t kaes[32]; memset(kaes, 0, sizeof(kaes));
|
||||
uint8_t empt[32]; memset(empt, 0, sizeof(empt));
|
||||
uint8_t maes[16]; memset(maes, 0, sizeof(maes));
|
||||
uint8_t kaes[32];
|
||||
uint8_t empt[32];
|
||||
uint8_t maes[16];
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
kaes[i+0] = ((state->rkey_array[kid+0x000]) >> (56-(i*8))) & 0xFF;
|
||||
@@ -1052,21 +1007,6 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
if (memcmp(kaes, empt, sizeof(kaes)) != 0) akl = 1;
|
||||
}
|
||||
|
||||
//if still not akl, see if there is anything manually loaded into K1, K2, K3, K4 instead
|
||||
if (akl == 0)
|
||||
{
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
kaes[i+0] = (state->K1) >> (56-(i*8)) & 0xFF;
|
||||
kaes[i+8] = (state->K2) >> (56-(i*8)) & 0xFF;
|
||||
kaes[i+16] = (state->K3) >> (56-(i*8)) & 0xFF;
|
||||
kaes[i+24] = (state->K4) >> (56-(i*8)) & 0xFF;
|
||||
}
|
||||
|
||||
//if kaes is loaded with a key, then flag on the key loaded variable
|
||||
if (memcmp(kaes, empt, sizeof(kaes)) != 0) akl = 1;
|
||||
}
|
||||
|
||||
if (R == 0 && state->R != 0) R = state->R;
|
||||
|
||||
//easier to manually load up rather than make a loop (RC4)
|
||||
@@ -1082,25 +1022,19 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
|
||||
//print alg/key and value if loaded
|
||||
fprintf (stderr, "\n PDU ALG: %02X; Key ID: %02X;", alg, kid);
|
||||
if (alg != 0 && mi != 0) fprintf (stderr, " MI(32): %08llX;", mi);
|
||||
if (alg != 0) fprintf (stderr, " MI(32): %08llX;", mi);
|
||||
if (alg == 0) fprintf (stderr, " Moto BP;");
|
||||
if (alg == 1) fprintf (stderr, " RC4;");
|
||||
if (alg == 2) fprintf (stderr, " DES;");
|
||||
if (alg == 4) fprintf (stderr, " AES128;");
|
||||
if (alg == 5) fprintf (stderr, " AES256;");
|
||||
if (alg == 7) fprintf (stderr, " VTX STD;");
|
||||
if (R && alg != 0) fprintf (stderr, " Key: %010llX;", R);
|
||||
|
||||
//generate 128-bit IV from 32-bit MI
|
||||
//expand 32-bit MI to 128-bit IV for AES mode data decryption
|
||||
//we only want to do this at the moment of keystream generation
|
||||
if (alg == 4 || alg == 5)
|
||||
if (alg == 5)
|
||||
{
|
||||
//for the LFSR128d function to show the correct alg
|
||||
if (state->currentslot == 0)
|
||||
state->payload_algid += 0x20;
|
||||
else state->payload_algidR += 0x20;
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
LFSR128d(state);
|
||||
if (state->currentslot == 0)
|
||||
@@ -1115,54 +1049,30 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
decrypted_pdu = 1;
|
||||
}
|
||||
|
||||
else if (alg == 5 && akl == 1 && mi != 0) //AES-256 OFB
|
||||
if (alg == 5 && akl == 1) //AES-256
|
||||
{
|
||||
int nblocks = (state->data_byte_ctr[slot] / 16) + 1;
|
||||
aes_ofb_keystream_output (maes, kaes, ob, 2, nblocks);
|
||||
decrypted_pdu = 1;
|
||||
}
|
||||
|
||||
else if (alg == 5 && akl == 1 && mi == 0) //AES-256 ECB (CCR without an IV)
|
||||
{
|
||||
//void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de)
|
||||
int nblocks = (state->data_byte_ctr[slot] / 16) + 0; //was +1
|
||||
for (i = 0; i < nblocks; i++)
|
||||
{
|
||||
aes_ecb_bytewise_payload_crypt(state->dmr_pdu_sf[slot]+start, kaes, state->dmr_pdu_sf[slot]+start, 2, 0);
|
||||
start += 16;
|
||||
}
|
||||
decrypted_pdu = 1;
|
||||
}
|
||||
|
||||
//other algs available, not sure if others will be used in any DMRA offerings
|
||||
else if (alg == 2 && R != 0) //Tait DES
|
||||
if (alg == 2 && R != 0) //Tait DES
|
||||
{
|
||||
int nblocks = (state->data_byte_ctr[slot] / 8) + 1;
|
||||
des_multi_keystream_output (mi, R, ob, 1, nblocks);
|
||||
decrypted_pdu = 1;
|
||||
}
|
||||
|
||||
else if (alg == 4 && akl == 1 && mi != 0) //AES-128 OFB
|
||||
if (alg == 4 && akl == 1) //AES-128
|
||||
{
|
||||
int nblocks = (state->data_byte_ctr[slot] / 16) + 1;
|
||||
aes_ofb_keystream_output (maes, kaes, ob, 0, nblocks);
|
||||
decrypted_pdu = 1;
|
||||
}
|
||||
|
||||
else if (alg == 4 && akl == 1 && mi == 0) //AES-128 ECB (CCR without an IV)
|
||||
{
|
||||
//void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de)
|
||||
int nblocks = (state->data_byte_ctr[slot] / 16) + 0; //was +1
|
||||
for (i = 0; i < nblocks; i++)
|
||||
{
|
||||
aes_ecb_bytewise_payload_crypt(state->dmr_pdu_sf[slot]+start, kaes, state->dmr_pdu_sf[slot]+start, 0, 0);
|
||||
start += 16;
|
||||
}
|
||||
decrypted_pdu = 1;
|
||||
}
|
||||
|
||||
//NOTE: Observed that keystream should not be applied to pad bytes or CRC
|
||||
//apply keystream here, only if key is available and keystream created, not ECB.
|
||||
//apply keystream here, only if alg is 1 or 4 AND key is available!
|
||||
if (alg == 1 && R != 0)
|
||||
{
|
||||
for (i = 0; i < end; i++)
|
||||
@@ -1174,17 +1084,15 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
for (i = 0; i < end; i++)
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
|
||||
}
|
||||
|
||||
else if (alg == 4 && akl != 0 && mi != 0)
|
||||
else if (alg == 4 && akl != 0)
|
||||
{
|
||||
for (i = 0; i < end; i++)
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[(i+16)%3096];
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
|
||||
}
|
||||
|
||||
else if (alg == 5 && akl != 0 && mi != 0)
|
||||
else if (alg == 5 && akl != 0)
|
||||
{
|
||||
for (i = 0; i < end; i++)
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[(i+16)%3096];
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
|
||||
}
|
||||
|
||||
//BP key application
|
||||
|
||||
22
src/dmr_bs.c
22
src/dmr_bs.c
@@ -502,13 +502,6 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
|
||||
}
|
||||
|
||||
if (state->csi_ee == 1)
|
||||
{
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr);
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr2);
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr3);
|
||||
}
|
||||
|
||||
#ifdef PRINT_AMBE72
|
||||
ambe2_codeword_print_i(opts, ambe_fr);
|
||||
ambe2_codeword_print_i(opts, ambe_fr2);
|
||||
@@ -702,8 +695,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else if (state->payload_algid == 0x02)
|
||||
{
|
||||
state->currentslot = 0;
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
state->currentslot = 0;
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
if (state->payload_algidR >= 0x21)
|
||||
@@ -713,8 +706,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else if (state->payload_algidR == 0x02)
|
||||
{
|
||||
state->currentslot = 1;
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
state->currentslot = 1;
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
|
||||
@@ -961,13 +954,6 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
|
||||
}
|
||||
|
||||
if (state->csi_ee == 1)
|
||||
{
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr);
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr2);
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr3);
|
||||
}
|
||||
|
||||
#ifdef PRINT_AMBE72
|
||||
ambe2_codeword_print_i(opts, ambe_fr);
|
||||
ambe2_codeword_print_i(opts, ambe_fr2);
|
||||
@@ -1069,10 +1055,10 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
state->currentslot = 1;
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
else if (state->payload_algidR == 0x02)
|
||||
else if (state->payload_algid == 0x02)
|
||||
{
|
||||
state->currentslot = 1;
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
state->currentslot = 1;
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
|
||||
uint8_t TrellisReturn[18];
|
||||
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
||||
IrrecoverableErrors = viterbi_r34(tdibits, TrellisReturn);
|
||||
IrrecoverableErrors = dmr_34(tdibits, TrellisReturn);
|
||||
|
||||
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
|
||||
//they may have been successfully corrected, the CRC will reveal as much
|
||||
|
||||
@@ -72,15 +72,6 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
so ^= 0x40;
|
||||
}
|
||||
|
||||
//Kirisun
|
||||
if (*IrrecoverableErrors == 0 && CRCCorrect == 1 && fid == 0x0A) //&& (so & 0x40) == 0x40
|
||||
{
|
||||
//disable late entry for DMRA, Flag 3 if encrypted, 0 if not
|
||||
if (so & 0x40)
|
||||
opts->dmr_le = 3;
|
||||
else opts->dmr_le = 0;
|
||||
}
|
||||
|
||||
//read ahead a little to get this for the xpt flag
|
||||
if (*IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68)
|
||||
{
|
||||
@@ -319,8 +310,8 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
}
|
||||
|
||||
//unknown other manufacturer or OTA ENC, etc.
|
||||
//removed tait from the list, added hytera 0x08, added Kirisun and KW Scrambler LC
|
||||
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && fid != 0x0A && is_kenwood_sc == 0)
|
||||
//removed tait from the list, added hytera 0x08
|
||||
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && is_kenwood_sc == 0)
|
||||
{
|
||||
if (type == 1) fprintf (stderr, "%s \n", KYEL);
|
||||
if (type == 2) fprintf (stderr, "%s \n", KYEL);
|
||||
@@ -560,7 +551,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
if (opts->p25_trunk == 1 && opts->trunk_tune_enc_calls == 0) //&& type != 2
|
||||
{
|
||||
int i, lo = 0;
|
||||
uint32_t t = 0; char gm[3]; char gn[100];
|
||||
uint32_t t = 0; char gm[8]; char gn[50];
|
||||
|
||||
//check to see if this group already exists, or has already been locked out, or is allowed
|
||||
for (i = 0; i <= state->group_tally; i++)
|
||||
@@ -663,7 +654,6 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
}
|
||||
|
||||
//should rework this back into the upper portion
|
||||
if (fid == 0x0A) fprintf (stderr, "Kirisun ");
|
||||
if (fid == 0x68) fprintf (stderr, "Hytera ");
|
||||
if (is_xpt) fprintf (stderr, "XPT ");
|
||||
if (fid == 0x68 && flco == 0x00)
|
||||
|
||||
90
src/dmr_le.c
90
src/dmr_le.c
@@ -14,6 +14,23 @@ void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc,
|
||||
|
||||
uint8_t slot = state->currentslot;
|
||||
|
||||
//enforce RC4 due to missing PI header, but with valid SVC Opts
|
||||
//ideally, this would be handled by VC-F single burst, but its not fully reliable compared to this
|
||||
//due to multiple signalling occurring inside of it, depending on system type
|
||||
if (state->M == 0x21)
|
||||
{
|
||||
if (slot == 0 && state->dmr_so & 0x40)
|
||||
{
|
||||
state->payload_algid = 0x21;
|
||||
state->payload_keyid = 0xFF;
|
||||
}
|
||||
if (slot == 1 && state->dmr_soR & 0x40)
|
||||
{
|
||||
state->payload_algidR = 0x21;
|
||||
state->payload_keyidR = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
//collect our fragments and place them into storage
|
||||
state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
|
||||
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
|
||||
@@ -219,23 +236,6 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
if (state->payload_algid == 0x35 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
|
||||
{
|
||||
state->DMRvcL = 0;
|
||||
state->payload_mi = kirisun_lfsr(state->payload_mi);
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 1");
|
||||
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid);
|
||||
fprintf (stderr, " MI(32): %08llX;", state->payload_mi);
|
||||
fprintf (stderr, " Kirisun");
|
||||
if (state->payload_algid == 0x36)
|
||||
fprintf (stderr, " Advanced;");
|
||||
else if (state->payload_algid == 0x37)
|
||||
fprintf (stderr, " Universal;");
|
||||
else fprintf (stderr, " Encryption;");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
}
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
@@ -266,23 +266,6 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
if (state->payload_algidR == 0x35 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
|
||||
{
|
||||
state->DMRvcR = 0;
|
||||
state->payload_miR = kirisun_lfsr(state->payload_miR);
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 2");
|
||||
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR);
|
||||
fprintf (stderr, " MI(32): %08llX;", state->payload_miR);
|
||||
fprintf (stderr, " Kirisun");
|
||||
if (state->payload_algidR == 0x36)
|
||||
fprintf (stderr, " Advanced;");
|
||||
else if (state->payload_algidR == 0x37)
|
||||
fprintf (stderr, " Universal;");
|
||||
else fprintf (stderr, " Encryption;");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -407,41 +390,6 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
//NOTE: on above, I belive that we need to check by opcode as well, as a CRC3 can have multiple collisions
|
||||
//so we need to exclude op/alg 0 and 3 from the check (does algID 0x03/0x23 even exist?)
|
||||
|
||||
//Kirisun Placeholder
|
||||
if (opts->dmr_le == 3)
|
||||
{
|
||||
if (irr_err != 0)
|
||||
{
|
||||
uint32_t sbrcpl = 0;
|
||||
for(i = 0; i < 32; i++)
|
||||
{
|
||||
sbrcpl = sbrcpl << 1;
|
||||
sbrcpl |= sbrc_interleaved[i] & 1;
|
||||
}
|
||||
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM);
|
||||
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||
}
|
||||
else if (irr_err == 0 && sbrc_hex != 0) //first batch had sbrc_hex 0x5F1, second batch had 0x5E1 instead, what changed?
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " Slot %d", state->currentslot+1);
|
||||
fprintf (stderr, " DMR LE SB Kirisun Encryption Identifier;");
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
if (state->payload_algid == 0 && state->dmr_so & 0x40)
|
||||
state->payload_algid = 0x35; //placeholder value
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state->payload_algidR == 0 && state->dmr_soR & 0x40)
|
||||
state->payload_algidR = 0x35; //placeholder value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->dmr_le == 1)
|
||||
{
|
||||
if (irr_err != 0)
|
||||
@@ -518,7 +466,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
if (state->dmr_so & 0x40 && key != 0 && alg != 0)
|
||||
{
|
||||
//if we aren't forcing a particular alg or privacy key set
|
||||
if (state->forced_alg_id == 0)
|
||||
if (state->M == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
@@ -544,7 +492,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
if (state->dmr_soR & 0x40 && key != 0 && alg != 0)
|
||||
{
|
||||
//if we aren't forcing a particular alg or privacy key set
|
||||
if (state->forced_alg_id == 0)
|
||||
if (state->M == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
|
||||
24
src/dmr_ms.c
24
src/dmr_ms.c
@@ -167,16 +167,6 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
}
|
||||
|
||||
//Kirisun Check VC-F for Enc Identifiers -- has bad fec, unless its loaded differently
|
||||
//SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun
|
||||
// if (opts->dmr_le == 3 && vc == 6)
|
||||
// {
|
||||
// bool kokay = 0;
|
||||
// // kokay = Golay_24_12_decode(syncdata);
|
||||
// unsigned long long int kirisun = (unsigned long long int)ConvertBitIntoBytes(&syncdata[0], 48);
|
||||
// fprintf (stderr, "Kiri: %012llX; Golay Okay: %d; \n", kirisun, kokay);
|
||||
// }
|
||||
|
||||
for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i];
|
||||
for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40];
|
||||
|
||||
@@ -263,13 +253,6 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
||||
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
|
||||
}
|
||||
|
||||
if (state->csi_ee == 1)
|
||||
{
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr);
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr2);
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr3);
|
||||
}
|
||||
|
||||
#ifdef PRINT_AMBE72
|
||||
ambe2_codeword_print_i(opts, ambe_fr);
|
||||
ambe2_codeword_print_i(opts, ambe_fr2);
|
||||
@@ -581,13 +564,6 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
|
||||
}
|
||||
|
||||
if (state->csi_ee == 1)
|
||||
{
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr);
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr2);
|
||||
csi72_ambe2_codeword_keystream(state, ambe_fr3);
|
||||
}
|
||||
|
||||
#ifdef PRINT_AMBE72
|
||||
ambe2_codeword_print_i(opts, ambe_fr);
|
||||
ambe2_codeword_print_i(opts, ambe_fr2);
|
||||
|
||||
@@ -482,34 +482,6 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
|
||||
sprintf (state->dmr_lrrp_gps[slot], "ETSI TMS SRC: %d; DST: %d; ", src24, dst24);
|
||||
utf16_to_text(state, 1, len, input+28);
|
||||
}
|
||||
//Vertex Standard TMS Port
|
||||
else if (port1 == 5007 && port2 == 5007)
|
||||
{
|
||||
|
||||
//get pad amount to see where to stop parsing UTF16 text
|
||||
int pad = state->data_block_poc[slot];
|
||||
|
||||
//sanity check
|
||||
if (len > (50+pad))
|
||||
len = len - pad - 50;
|
||||
|
||||
fprintf (stderr, "VTX STD TMS; ");
|
||||
|
||||
//+28, get 9 bytes for unknown "header" information
|
||||
//unknown elements 0E0002000000000000, could include a len value or type (utf-16, utf-8, is07?)
|
||||
//the sample has a len of 8 utf-16 chars, only way to get that is on the 2 if not byte aligned (1000)
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
// fprintf (stderr, "Len: %03d; ", len);
|
||||
fprintf (stderr, "??: ");
|
||||
for (int i = 0; i < 9; i++)
|
||||
fprintf (stderr, "%02X", input[i+28]);
|
||||
fprintf (stderr, "; Text: ");
|
||||
}
|
||||
|
||||
sprintf (state->dmr_lrrp_gps[slot], "VTX TMS SRC: %d; DST: %d; ", src24, dst24);
|
||||
utf16_to_text(state, 1, len, input+49);
|
||||
}
|
||||
else if (port1 == 5017 && port2 == 5017)
|
||||
{
|
||||
//sanity check
|
||||
@@ -521,12 +493,6 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
|
||||
lip_protocol_decoder(opts, state, bits);
|
||||
}
|
||||
//known P25 Ports
|
||||
else if (port1 == 9361 && port2 == 9361)
|
||||
{
|
||||
sprintf (state->dmr_lrrp_gps[slot], "P25 Atlas SRC(IP): %d.%d.%d.%d; DST(IP): %d.%d.%d.%d; ",
|
||||
input[12], input[13], input[14], input[15], input[16], input[17], input[18], input[19]);
|
||||
fprintf (stderr, "Atlas Data Registration Server;"); //EF Johnson Atlas
|
||||
}
|
||||
else if (port1 == 49198 && port2 == 49198)
|
||||
{
|
||||
sprintf (state->dmr_lrrp_gps[slot], "P25 Tier 2 LOCN SRC(IP): %d.%d.%d.%d; DST(IP): %d.%d.%d.%d; ",
|
||||
@@ -1001,9 +967,8 @@ void dmr_locn (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * DMR_P
|
||||
//write to LRRP file
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
char * datestr = getDateS();
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
FILE * pFile; //file pointer
|
||||
|
||||
92
src/dmr_pi.c
92
src/dmr_pi.c
@@ -12,6 +12,7 @@
|
||||
|
||||
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||
{
|
||||
UNUSED2(opts, CRCCorrect);
|
||||
|
||||
uint8_t MFID = PI_BYTE[1];
|
||||
|
||||
@@ -25,57 +26,6 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
}
|
||||
|
||||
if (MFID == 0x0A && CRCCorrect == 1) //Kirisun
|
||||
{
|
||||
uint8_t so = PI_BYTE[2]; //in VLC and PI, this byte is 0x40, so thinking this could be SVC_OPT
|
||||
uint8_t alg = PI_BYTE[0]; //observed 0x36 and 0x37 for Kirisun Advanced and Universal Privacy
|
||||
// uint8_t kid = PI_BYTE[2]; //user info conveyed there is only 16 allotments for Key, and keys are channel specific, so no key id value
|
||||
uint32_t target = ((unsigned long long int)PI_BYTE[7] << 16) | ((unsigned long long int)PI_BYTE[8] << 8) | ((unsigned long long int)PI_BYTE[9] << 0);
|
||||
|
||||
//TODO: Use ALG and TGT value here to produce a key id via hashing it
|
||||
uint8_t hash = alg * target % 256; //more complex hash later (honestly, probably not)
|
||||
|
||||
//MI only appears to be 32-bit
|
||||
uint32_t mi = ((uint32_t)PI_BYTE[3] << 24) | ((uint32_t)PI_BYTE[4] << 16) |
|
||||
((uint32_t)PI_BYTE[5] << 8) | ((uint32_t)PI_BYTE[6] << 0);
|
||||
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
state->dmr_so = so;
|
||||
state->payload_algid = alg;
|
||||
state->payload_keyid = hash;
|
||||
state->payload_mi = mi;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->dmr_soR = so;
|
||||
state->payload_algidR = alg;
|
||||
state->payload_keyidR = hash;
|
||||
state->payload_miR = mi;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
fprintf (stderr, "\n Slot %d", state->currentslot+1);
|
||||
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08X;", alg, hash, mi);
|
||||
|
||||
fprintf (stderr, " Kirisun ");
|
||||
if (alg == 0x36)
|
||||
fprintf (stderr, "Advanced;");
|
||||
else if (alg == 0x37)
|
||||
fprintf (stderr, "Universal;");
|
||||
else fprintf (stderr, "Encryption;");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//disable late entry for DMRA, Flag 3 for future check of VC-F 48-bit region with Golay 24,12 Encoding
|
||||
//SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun
|
||||
//tested patent above with information provided, VC-F had a SB, but the 48-bit
|
||||
//full value yielded bad Golay results, and user submitted info contradicts this patent for samples provided
|
||||
//Late Entry MI does appear to work, however, and reports a good CRC for that as well,
|
||||
//reverse engineered LFSR for Kirisun lines up with the late entry for 32-bit MI values.
|
||||
opts->dmr_le = 3;
|
||||
|
||||
}
|
||||
|
||||
if (MFID == 0x68) //Hytera Enhanced
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
@@ -473,13 +423,13 @@ unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len)
|
||||
{
|
||||
uint8_t bit = (mi[i] >> 7) & 1;
|
||||
mi[i] <<= 1;
|
||||
if (bit) mi[i] ^= taps[i%len];
|
||||
if (bit) mi[i] ^= taps[i%5];
|
||||
mi[i] |= bit;
|
||||
|
||||
}
|
||||
|
||||
unsigned long long int mi_value = 0;
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
for (uint8_t i = 0; i < 5; i++)
|
||||
{
|
||||
mi_value <<= 8;
|
||||
mi_value |= mi[i];
|
||||
@@ -520,38 +470,4 @@ void hytera_enhanced_alg_refresh(dsd_state * state)
|
||||
if (state->currentslot == 0)
|
||||
state->payload_mi = mi_value;
|
||||
else state->payload_miR = mi_value;
|
||||
}
|
||||
|
||||
uint32_t kirisun_lfsr(unsigned long long int mi)
|
||||
{
|
||||
|
||||
uint32_t taps = 0xD459C4F1;
|
||||
uint32_t lfsr = (uint32_t)mi;
|
||||
uint32_t new_mi = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
|
||||
uint8_t byte = 0;
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
uint32_t temp = (lfsr << 1);
|
||||
uint8_t msb = (lfsr >> 31) & 1;
|
||||
|
||||
if (msb)
|
||||
{
|
||||
byte |= (1 << j);
|
||||
lfsr = temp ^ taps;
|
||||
}
|
||||
else lfsr = temp; //was previously temp ^ 1, but that isn't a primitive
|
||||
}
|
||||
|
||||
new_mi <<=8;
|
||||
new_mi |= byte;
|
||||
|
||||
}
|
||||
|
||||
return new_mi;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -581,15 +581,11 @@ void tait_iso7_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
{
|
||||
alias[i] = (uint8_t)ConvertBitIntoBytes(&input[16+(i*7)], 7);
|
||||
|
||||
// fprintf (stderr, "%X,", alias[i]); //debug
|
||||
|
||||
fprintf (stderr, "%c", alias[i]);
|
||||
if (alias[i] == 0x2C) //change a comma to a dot
|
||||
alias[i] = 0x2E;
|
||||
else if (alias[i] < 0x20) //change any garble / control chars to a space
|
||||
alias[i] = 0x20;
|
||||
|
||||
fprintf (stderr, "%c", alias[i]);
|
||||
}
|
||||
|
||||
//flag to indicate this already exists in import or group struct
|
||||
@@ -798,18 +794,22 @@ void dmr_talker_alias_lc_decode (dsd_opts * opts, dsd_state * state, uint8_t slo
|
||||
}
|
||||
else if (char_size == 16)
|
||||
{
|
||||
int ptr = 0;
|
||||
setlocale(LC_ALL, ""); //needed when encoded alias contains Chinese (or probably any non-roman charset that isn't default on users terminal)
|
||||
for (i = 0; i < end; i++)
|
||||
{
|
||||
uint16_t character = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][(i*16)], 16);
|
||||
char ch[2]; ch[0] = character & 0xFF; ch[1] = 0;
|
||||
|
||||
if (character >= 0x20 && character != 0x7F)
|
||||
fprintf (stderr, "%lc", character);
|
||||
else fprintf (stderr, " ");
|
||||
|
||||
//Encode to UTF-8
|
||||
ptr += utf8_encode(character, alias_string+ptr);
|
||||
if (character == 0)
|
||||
strcat (alias_string, " ");
|
||||
else if (character >= 0x20 && character <= 0xFE)
|
||||
strcat (alias_string, ch);
|
||||
else
|
||||
strcat (alias_string, "*");
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " [%04X], ", character);
|
||||
|
||||
@@ -137,11 +137,9 @@ void openPulseInput(dsd_opts * opts)
|
||||
lt.maxlength = -1;
|
||||
lt.prebuf = -1;
|
||||
lt.tlength = -1;
|
||||
// if (opts->m17encoder == 1)
|
||||
// opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME4", PA_STREAM_RECORD, dev, "M17 Voice Input", &cc, NULL, <, &err);
|
||||
// else opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, <, &err);
|
||||
|
||||
opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, <, &err);
|
||||
if (opts->m17encoder == 1)
|
||||
opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME4", PA_STREAM_RECORD, dev, "M17 Voice Input", &cc, NULL, <, &err);
|
||||
else opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, <, &err);
|
||||
|
||||
if (err != 0)
|
||||
{
|
||||
@@ -189,14 +187,6 @@ void parse_pulse_output_string (dsd_opts * opts, char * input)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// macOS does not support OSS (/dev/dsp).
|
||||
// Provide a stub so callers can link while using Pulse/CoreAudio backends.
|
||||
void openOSSOutput(dsd_opts *opts)
|
||||
{
|
||||
(void)opts;
|
||||
}
|
||||
#else
|
||||
void openOSSOutput (dsd_opts * opts)
|
||||
{
|
||||
int fmt;
|
||||
@@ -215,7 +205,6 @@ void openOSSOutput (dsd_opts * opts)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
fmt = 0;
|
||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
|
||||
{
|
||||
@@ -312,7 +301,6 @@ void openOSSOutput (dsd_opts * opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
processAudio (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
@@ -100,7 +100,7 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
|
||||
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[0] == 1)
|
||||
@@ -126,7 +126,7 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
|
||||
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[1] == 1)
|
||||
@@ -716,7 +716,7 @@ void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state)
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
|
||||
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3)
|
||||
{
|
||||
if (state->aes_key_loaded[0] == 1)
|
||||
{
|
||||
@@ -1131,7 +1131,7 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
|
||||
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[0] == 1)
|
||||
@@ -1157,7 +1157,7 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
|
||||
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[1] == 1)
|
||||
@@ -2194,8 +2194,6 @@ void agsm (dsd_opts * opts, dsd_state * state, short * input, int len)
|
||||
|
||||
state->aout_gainA = coeff; //store for internal use
|
||||
|
||||
UNUSED(avg);
|
||||
|
||||
}
|
||||
|
||||
//until analog agc is fixed, going to use a manual gain control on this
|
||||
|
||||
@@ -218,13 +218,13 @@ void watchdog_event_history (dsd_opts * opts, dsd_state * state, uint8_t slot)
|
||||
|
||||
if (slot == 0 && opts->wav_out_f != NULL)
|
||||
{
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, event_struct);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, event_struct);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
|
||||
}
|
||||
|
||||
else if (slot == 1 && opts->wav_out_fR != NULL)
|
||||
{
|
||||
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, event_struct);
|
||||
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, event_struct);
|
||||
opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0);
|
||||
}
|
||||
|
||||
@@ -695,7 +695,7 @@ void watchdog_event_current (dsd_opts * opts, dsd_state * state, uint8_t slot)
|
||||
|
||||
if (state->ea_mode == 1)
|
||||
{
|
||||
sprintf (event_string, "%s %s %s TGT: %07d; SRC: %07d; LCN: %02d; SITE: %d:%d.%04X; %s; ", datestr, timestr, sys_string, target_id, source_id, channel, sys_id1, sys_id2, sys_id3, sup_str);
|
||||
sprintf (event_string, "%s %s %s TGT: %07d; SRC: %07d; LCN: %02d; SITE: %d:%d.%04X; %s;", datestr, timestr, sys_string, target_id, source_id, channel, sys_id1, sys_id2, sys_id3, sup_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
493
src/dsd_file.c
493
src/dsd_file.c
@@ -18,7 +18,6 @@
|
||||
#include "dsd.h"
|
||||
#include "p25p1_const.h" //for imbe fr (7200)
|
||||
#include "dmr_const.h" //for ambe+2 fr
|
||||
#include "bp.h" //for bp key table
|
||||
|
||||
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
||||
{
|
||||
@@ -502,7 +501,7 @@ SNDFILE * close_wav_file(SNDFILE * wav_file)
|
||||
return wav_file;
|
||||
}
|
||||
|
||||
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, char * custom_tag, Event_History_I * event_struct)
|
||||
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, Event_History_I * event_struct)
|
||||
{
|
||||
sf_close(wav_file);
|
||||
|
||||
@@ -538,46 +537,11 @@ SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename,
|
||||
char new_filename[2000];
|
||||
memset (new_filename, 0, sizeof(new_filename));
|
||||
|
||||
//parse any possible custom tags or aliasing (up to 3)
|
||||
char custom_string_temp[100];
|
||||
memset(custom_string_temp, 0, sizeof(custom_string_temp));
|
||||
memcpy(custom_string_temp, custom_tag, sizeof(custom_string_temp));
|
||||
char * token = strtok(custom_string_temp, ":");
|
||||
char tag[3][25];
|
||||
memset(tag, 0, sizeof(tag));
|
||||
sprintf(tag[0], "%s", "");
|
||||
sprintf(tag[1], "%s", "");
|
||||
sprintf(tag[2], "%s", "");
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
|
||||
if (token != NULL)
|
||||
{
|
||||
tag[i][0] = '_';
|
||||
strncpy(tag[i]+1, token, 23);
|
||||
tag[i][24] = '\0';
|
||||
token = strtok(NULL, ":");
|
||||
|
||||
//look for _TGT_, convert to strings
|
||||
if (strncmp("_TGT_", tag[i], 5) == 0)
|
||||
{
|
||||
sprintf (tgt_str, "%s", tag[i]+5);
|
||||
sprintf (src_str, "%d", source_id);
|
||||
is_string = 1;
|
||||
memset(tag[i], 0, sizeof(tag[i]));
|
||||
sprintf(tag[i], "%s", "");
|
||||
}
|
||||
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
//check for String based TGT and SRC values (M17, YSF, DSTAR)
|
||||
if (is_string == 1)
|
||||
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s%s%s%s_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tag[0], tag[1], tag[2], tgt_str, src_str);
|
||||
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tgt_str, src_str);
|
||||
else //is a numerical value
|
||||
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s%s%s%s_TGT_%d_SRC_%d.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tag[0], tag[1], tag[2], target_id, source_id);
|
||||
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s_TGT_%d_SRC_%d.wav", dir, datestr, timestr, random_number, sys_str, gi_str, target_id, source_id);
|
||||
|
||||
if (timestr != NULL)
|
||||
{
|
||||
@@ -964,12 +928,9 @@ uint16_t parse_raw_user_string (char * input, uint8_t * output)
|
||||
return len;
|
||||
}
|
||||
|
||||
//DMRA late entry items
|
||||
static int ambe2_counter;
|
||||
static int dmra_le;
|
||||
|
||||
uint16_t ambe2_str_to_decode(dsd_opts * opts, dsd_state * state, char * ambe_str, uint8_t * ks, uint16_t ks_idx, uint8_t dmra, uint8_t is_enc, uint8_t ks_available)
|
||||
{
|
||||
UNUSED(opts);
|
||||
|
||||
char ambe_fr[4][24]; memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||
uint8_t dibit_pair = 0;
|
||||
@@ -1015,39 +976,6 @@ uint16_t ambe2_str_to_decode(dsd_opts * opts, dsd_state * state, char * ambe_str
|
||||
|
||||
}
|
||||
|
||||
//if using DMRA Late Entry IV mechanics, evaluate for IV
|
||||
if (dmra_le)
|
||||
{
|
||||
|
||||
//make a copy of ambe_fr codeword 3 (MI fragment)
|
||||
uint8_t c3[24]; memset (c3, 0, sizeof(c3));
|
||||
for (int i = 0; i < 24; i++)
|
||||
c3[i] = ambe_fr[3][i];
|
||||
|
||||
//force slot to 0
|
||||
state->currentslot = 0;
|
||||
|
||||
uint8_t c3_hex = (uint64_t)ConvertBitIntoBytes(c3, 4);
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "\n AMBE#: %02d / %d / %d; F: %X;", ambe2_counter, ambe2_counter/3, ambe2_counter%3, c3_hex);
|
||||
|
||||
//use div and mod 3 to set current storage fragment
|
||||
state->late_entry_mi_fragment[0][(ambe2_counter/3)+1][ambe2_counter%3] = c3_hex;
|
||||
|
||||
//run LFSR evaluate stored codewords for matching IV or replace if not match or not set
|
||||
if (ambe2_counter == 17)
|
||||
{
|
||||
if (state->payload_mi != 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
LFSR(state);
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
dmr_late_entry_mi (opts, state);
|
||||
}
|
||||
}
|
||||
|
||||
char ambe_d[49]; memset(ambe_d, 0, sizeof(ambe_d));
|
||||
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||
state->errs2 = state->errs;
|
||||
@@ -1124,6 +1052,7 @@ uint16_t ambe2_str_to_decode(dsd_opts * opts, dsd_state * state, char * ambe_str
|
||||
|
||||
uint16_t imbe_str_to_decode(dsd_opts * opts, dsd_state * state, char * imbe_str, uint8_t * ks, uint16_t ks_idx, uint8_t is_enc, uint8_t ks_available)
|
||||
{
|
||||
UNUSED(opts);
|
||||
|
||||
char imbe_fr[8][23]; memset (imbe_fr, 0, sizeof(imbe_fr));
|
||||
uint8_t dibit_pair = 0;
|
||||
@@ -1270,16 +1199,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
state->synctype = -1;
|
||||
state->lastsynctype = -1;
|
||||
|
||||
//reset encryption variables
|
||||
state->payload_mi = 0;
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
if (state->keyloader == 1)
|
||||
{
|
||||
state->R = 0;
|
||||
state->aes_key_loaded[0] = 0;
|
||||
}
|
||||
|
||||
//watchdog for event history
|
||||
watchdog_event_history(opts, state, 0);
|
||||
watchdog_event_current(opts, state, 0);
|
||||
@@ -1291,12 +1210,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
uint8_t ks_i[3000]; memset (ks_i, 0, sizeof(ks_i));
|
||||
uint16_t ks_idx_i = 808; //keystream index value IMBE (start at 808 for out of order ESS)
|
||||
int imbe_counter = 0; //count IMBE frames for when to skip 2 bytes of ks and juggle keystreams
|
||||
|
||||
//Used in conjunction with DMR's DMRA Late Entry IV
|
||||
ambe2_counter = 0; //count AMBE+2 frames for DMRA late entry IV mechanics (static)
|
||||
dmra_le = 0; //whether or not to attempt looking for the DMRA Late Entry
|
||||
if (state->forced_alg_id != 0)
|
||||
state->payload_mi = 0;
|
||||
|
||||
source_size = fread (source_str, 1, 0x100000, opts->mbe_in_f);
|
||||
|
||||
@@ -1380,18 +1293,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
state->lastsynctype = 10;
|
||||
}
|
||||
|
||||
if (strncmp ("NXDN", str_buffer, 4) == 0)
|
||||
{
|
||||
//set AMBE+2 protocol here
|
||||
protocol = 3;
|
||||
|
||||
//disable dmra (+7 on ks_idx)
|
||||
is_dmra = 0;
|
||||
|
||||
state->synctype = 28;
|
||||
state->lastsynctype = 28;
|
||||
}
|
||||
|
||||
//open .imb or .amb file, if desired, but only after setting a synctype
|
||||
if (state->synctype != -1)
|
||||
{
|
||||
@@ -1402,57 +1303,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
}
|
||||
|
||||
if (strncmp ("tag", str_buffer, 3) == 0)
|
||||
{
|
||||
|
||||
str_buffer = strtok(NULL, "\n"); //copies to End of Line
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " TAG: %s", str_buffer);
|
||||
|
||||
//NOTE: Nothing coded for SDRTrunk MBE files provides information regarding
|
||||
//possible FACCH1 steals on voice frames, so keystrem index position here can
|
||||
//become unsynced on some voice frames if trunking or other does FACCH1 steals
|
||||
//when the system is really busy, or radio drops out. Writing code to read ahead
|
||||
//would still not tell us if the FACCH1 steal was in vocoder socket 1 or 2
|
||||
|
||||
//NOTE: Above may become even more noticeable on DES or AES as those keystreams
|
||||
//are generated for two voice superframes, and the decryption may become unsynced
|
||||
//until the next VCALL_IV is read in from the MBE file.
|
||||
|
||||
//Check NXDN tag value, set the ks_idx to proper position
|
||||
//by looking for SACCH_1, SACCH_2, SACCH_3, or SACCH_4 for scrambler
|
||||
if (alg_id == 1 && protocol == 3)
|
||||
{
|
||||
if (strncmp ("SACCH 1", str_buffer+4, 7) == 0)
|
||||
{
|
||||
ks_idx = 0;
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n PF 1/4");
|
||||
}
|
||||
else if (strncmp ("SACCH 2", str_buffer+4, 7) == 0)
|
||||
{
|
||||
ks_idx = 49*4;
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n PF 2/4");
|
||||
}
|
||||
else if (strncmp ("SACCH 3", str_buffer+4, 7) == 0)
|
||||
{
|
||||
ks_idx = 49*8;
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n PF 3/4");
|
||||
}
|
||||
else if (strncmp ("SACCH 4", str_buffer+4, 7) == 0)
|
||||
{
|
||||
ks_idx = 49*12;
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n PF 4/4");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (strncmp ("call_type", str_buffer, 9) == 0)
|
||||
{
|
||||
str_buffer = strtok(NULL, " : \""); //next value after any : "" string
|
||||
@@ -1486,29 +1336,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
key_id = 0;
|
||||
iv_hex = 0;
|
||||
|
||||
//NXDN Forced Scrambler
|
||||
if (is_enc == 1 && state->forced_alg_id == 1 && protocol == 3)
|
||||
{
|
||||
|
||||
alg_id = state->forced_alg_id;
|
||||
|
||||
//Key Load Forced Scrambler Key by TGT or SRC value
|
||||
if (state->forced_alg_id == 1 && state->keyloader == 1)
|
||||
{
|
||||
if (state->rkey_array[target] != 0)
|
||||
state->R = state->rkey_array[target];
|
||||
else if (state->rkey_array[source] != 0)
|
||||
state->R = state->rkey_array[source];
|
||||
}
|
||||
|
||||
if (alg_id == 1 && state->R != 0)
|
||||
{
|
||||
pdu_scrambler_keystream_creation(ks, state->R, 16*49);
|
||||
ks_available = 1;
|
||||
}
|
||||
//DES and AES still require the IV for KS creation
|
||||
}
|
||||
|
||||
//debug set value
|
||||
// fprintf (stderr, " ENC: %d;", is_enc);
|
||||
|
||||
@@ -1517,73 +1344,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
}
|
||||
|
||||
//if forcing a DMRA EP mode, turn on dmra_le and set alg here
|
||||
if (state->forced_alg_id >= 0x21 && state->forced_alg_id <= 0x25)
|
||||
{
|
||||
is_dmra = 1;
|
||||
dmra_le = 1;
|
||||
is_enc = 1;
|
||||
alg_id = state->forced_alg_id;
|
||||
state->payload_algid = alg_id;
|
||||
rc4_db = 256;
|
||||
rc4_mod = 9;
|
||||
|
||||
//since we can't keyring on a forced alg, we can perhaps look it up
|
||||
//via the TO field for the TG value, as long as it isn't greated than 0x1FFFF (upper bound of the array)
|
||||
if (state->keyloader == 1 && state->lasttg != 0 && state->lasttg < 0x1FFFF && state->rkey_array[state->lasttg] != 0)
|
||||
state->R = state->rkey_array[state->lasttg];
|
||||
|
||||
if (alg_id == 0x21 && state->R != 0 && state->payload_mi != 0)
|
||||
{
|
||||
|
||||
//test when we don't have an encryption_mi in the .mbe file for DMR
|
||||
//TODO: Handle multi keystream creation with a new function
|
||||
uint8_t ks_bytes[375]; memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
uint8_t kiv[32]; memset(kiv, 0, sizeof(kiv));
|
||||
|
||||
//load key into key portion of kiv
|
||||
kiv[0] = ((state->R & 0xFF00000000) >> 32);
|
||||
kiv[1] = ((state->R & 0xFF000000) >> 24);
|
||||
kiv[2] = ((state->R & 0xFF0000) >> 16);
|
||||
kiv[3] = ((state->R & 0xFF00) >> 8);
|
||||
kiv[4] = ((state->R & 0xFF) >> 0);
|
||||
|
||||
//load IV from late entry state->payload_mi
|
||||
kiv[5] = ((state->payload_mi & 0xFF000000) >> 24);
|
||||
kiv[6] = ((state->payload_mi & 0xFF0000) >> 16);
|
||||
kiv[7] = ((state->payload_mi & 0xFF00) >> 8);
|
||||
kiv[8] = ((state->payload_mi & 0xFF) >> 0);
|
||||
|
||||
rc4_block_output (rc4_db, rc4_mod, 200, kiv, ks_bytes);
|
||||
unpack_byte_array_into_bit_array(ks_bytes, ks, 200);
|
||||
|
||||
ks_available = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//TODO: This
|
||||
else if (state->forced_alg_id == 1) //Basic Privacy
|
||||
{
|
||||
//This is where the key loaded comes into play for moto vs hytera vs scrambler
|
||||
if (state->K != 0)
|
||||
{
|
||||
uint64_t k = 0;
|
||||
k = BPK[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
k <<= 1;
|
||||
k += (k >> 48) & 1;
|
||||
|
||||
//load that into ks
|
||||
for (int i = 0; i < 18*49; i++)
|
||||
ks[i] = (k >> (i%49)) & 1;
|
||||
}
|
||||
else if (state->H != 0)
|
||||
{
|
||||
//Hytera BP
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp ("to", str_buffer, 2) == 0)
|
||||
{
|
||||
str_buffer = strtok(NULL, " : \""); //next value after any : "" string
|
||||
@@ -1640,36 +1400,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
key_id = strtol (str_buffer, NULL, 10);
|
||||
|
||||
//NXDN Scrambler Setup
|
||||
if (alg_id == 1 && protocol == 3)
|
||||
{
|
||||
|
||||
state->payload_keyid = key_id;
|
||||
|
||||
//Scrambler Key Loading
|
||||
if (state->keyloader == 1)
|
||||
keyring(opts, state);
|
||||
|
||||
//If Scrambler Key still not available on KID
|
||||
if (state->R == 0)
|
||||
{
|
||||
//Key Load Scrambler Key by TGT or SRC value
|
||||
if (state->forced_alg_id == 1 && state->keyloader == 1)
|
||||
{
|
||||
if (state->rkey_array[target] != 0)
|
||||
state->R = state->rkey_array[target];
|
||||
else if (state->rkey_array[source] != 0)
|
||||
state->R = state->rkey_array[source];
|
||||
}
|
||||
}
|
||||
|
||||
if (state->R != 0)
|
||||
{
|
||||
pdu_scrambler_keystream_creation(ks, state->R, 16*49);
|
||||
ks_available = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//debug set value
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n Key ID: %04X;", key_id);
|
||||
@@ -1715,9 +1445,9 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//TODO: Handle multi keystream creation with a new function
|
||||
uint8_t ks_bytes[375]; memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
uint8_t kiv[32]; memset(kiv, 0, sizeof(kiv));
|
||||
uint8_t kiv[15]; memset(kiv, 0, sizeof(kiv));
|
||||
|
||||
//RC4 (v1 tested and working on P25, v2 should be okay now)
|
||||
//Test: Setup a simple RC4 for now (working)
|
||||
if ( (alg_id == 0xAA || alg_id == 0x21) && state->R != 0 )
|
||||
{
|
||||
|
||||
@@ -1750,157 +1480,19 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
ks_available = 1;
|
||||
|
||||
}
|
||||
|
||||
//P25 DES (v1 tested and working on P1, v2 should be okay now)
|
||||
else if (alg_id == 0x81 && state->R != 0)
|
||||
{
|
||||
} //end test
|
||||
|
||||
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256
|
||||
//NOTE: Regarding SDRTrunk .mbe format, the ESS Encryption Sync
|
||||
//is in the correct location on P25p2, but for P25p1, the ESS
|
||||
//information preceeds LDU2, but should be AFTER the LDU2 IMBE frames,
|
||||
//so, for now, we utilize a reverse lfsr function (Crypthings coming in handy)
|
||||
//and recover the previous LFSR and make two keystreams in order
|
||||
//to properly decrypt the initial frame, and then juggle the
|
||||
//keystreams in code to provide a smooth decryption session of P25p1.
|
||||
|
||||
if (protocol == 1) //Phase 1 IMBE start on 19 for DES-OFB (8 discard + 9 LCW + 2 reserved)
|
||||
unpack_byte_array_into_bit_array(ks_bytes+19, ks, 256-19); //unpack starting after discard
|
||||
else if (protocol == 2) //Phase 2 AMBE+2 start on 8 after discard round for DES-OFB
|
||||
unpack_byte_array_into_bit_array(ks_bytes+8, ks, 256-8); //unpack starting after discard
|
||||
|
||||
//reverse lfsr on IV and create keystream with that as well
|
||||
//due to out of order execution on P25p1 ESS sync.
|
||||
if (protocol == 1 && version == 1)
|
||||
{
|
||||
|
||||
//load the str_buffer into the IV portion of kiv (just borrowing for DES)
|
||||
parse_raw_user_string(str_buffer, kiv+5);
|
||||
|
||||
reverse_lfsr_64_to_len (opts, kiv+5, 64);
|
||||
|
||||
//convert bytes back into value
|
||||
iv_hex = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
iv_hex <<= 8;
|
||||
iv_hex |= kiv[i+5];
|
||||
}
|
||||
|
||||
memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
|
||||
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256
|
||||
|
||||
unpack_byte_array_into_bit_array(ks_bytes+19, ks_i, 256-19); //unpack starting after discard
|
||||
}
|
||||
|
||||
ks_available = 1;
|
||||
|
||||
}
|
||||
|
||||
//P25 AES (v1 and v2 tested working on P1)
|
||||
else if ( (alg_id == 0x84 || alg_id == 0x89) && state->aes_key_loaded[0] == 1 )
|
||||
{
|
||||
|
||||
uint8_t aes_key[32];
|
||||
memset(aes_key, 0, sizeof(aes_key));
|
||||
|
||||
//Load key from A1 - A4
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
|
||||
aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
|
||||
aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
|
||||
aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t aes_iv[16];
|
||||
memset(aes_iv, 0, sizeof(aes_iv));
|
||||
|
||||
//load the str_buffer into the IV portion of aes_iv
|
||||
parse_raw_user_string(str_buffer, aes_iv);
|
||||
|
||||
//backup copy of current IV to reverse and expand, if needed
|
||||
uint8_t aes_last_iv[16];
|
||||
memset(aes_last_iv, 0, sizeof(aes_last_iv));
|
||||
for (int i = 0; i < 8; i++)
|
||||
aes_last_iv[i] = aes_iv[i];
|
||||
|
||||
//Resolve the longer IV from the shorter one
|
||||
lfsr_64_to_128(aes_iv);
|
||||
|
||||
if (alg_id == 0x89) //128, or 256
|
||||
aes_ofb_keystream_output(aes_iv, aes_key, ks_bytes, 0, 16); //16*16=256
|
||||
else aes_ofb_keystream_output(aes_iv, aes_key, ks_bytes, 2, 16); //16*16=256
|
||||
|
||||
if (protocol == 1) //Phase 1 IMBE start on 27 for AES-OFB (16 discard + 9 LCW + 2 reserved)
|
||||
unpack_byte_array_into_bit_array(ks_bytes+27, ks, 256-27); //unpack starting after discard
|
||||
else if (protocol == 2) //Phase 2 AMBE+2 start on 16 after discard round for AES-OFB
|
||||
unpack_byte_array_into_bit_array(ks_bytes+16, ks, 256-16); //unpack starting after discard
|
||||
|
||||
//reverse lfsr on IV and create keystream with that as well
|
||||
//due to out of order execution on P25p1 ESS sync.
|
||||
if (protocol == 1 && version == 1)
|
||||
{
|
||||
|
||||
reverse_lfsr_64_to_len (opts, aes_last_iv, 64);
|
||||
|
||||
//Resolve the longer IV from the shorter one
|
||||
lfsr_64_to_128(aes_last_iv);
|
||||
|
||||
memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
|
||||
if (alg_id == 0x89) //128, or 256
|
||||
aes_ofb_keystream_output(aes_last_iv, aes_key, ks_bytes, 0, 16); //16*16=256
|
||||
else aes_ofb_keystream_output(aes_last_iv, aes_key, ks_bytes, 2, 16); //16*16=256
|
||||
|
||||
unpack_byte_array_into_bit_array(ks_bytes+27, ks_i, 256-27); //unpack starting after discard
|
||||
}
|
||||
|
||||
ks_available = 1;
|
||||
|
||||
}
|
||||
|
||||
//WIP: Add handling for NXDN DES and AES256
|
||||
//NOTE: This hinges on where SDRTrunk places the IV for the call,
|
||||
//ideally needs to be after the SACCH4 voice frames
|
||||
//NOTE: This will NOT WORK on Type-D with its 22-bit IV
|
||||
//I doubt anybody will ever test that theory out here either
|
||||
else if (protocol == 3 && alg_id == 2 && state->R != 0)
|
||||
{
|
||||
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32);
|
||||
|
||||
unpack_byte_array_into_bit_array(ks_bytes+8, ks, 256-8);
|
||||
|
||||
ambe2_counter = 0;
|
||||
ks_idx = 0;
|
||||
|
||||
ks_available = 1;
|
||||
}
|
||||
else if (protocol == 3 && alg_id == 3 && state->aes_key_loaded[0] == 1)
|
||||
{
|
||||
|
||||
uint8_t aes_key[32];
|
||||
memset (aes_key, 0, sizeof(aes_key));
|
||||
|
||||
//Load key from A1 - A4
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
|
||||
aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
|
||||
aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
|
||||
aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
|
||||
}
|
||||
|
||||
//Generate 128-bit IV for AES
|
||||
LFSR128npdu(state); //npdu variant uses payload_mi here, and loads to aes_ivR
|
||||
|
||||
aes_ofb_keystream_output (state->aes_ivR, aes_key, ks_bytes, 2, 16);
|
||||
|
||||
unpack_byte_array_into_bit_array(ks_bytes+16, ks, 256-16);
|
||||
|
||||
ambe2_counter = 0;
|
||||
ks_idx = 0;
|
||||
|
||||
ks_available = 1;
|
||||
}
|
||||
|
||||
//Pull request: https://github.com/DSheirer/sdrtrunk/pull/2273
|
||||
//Merged into SDRTrunk nightly so should be able to handle this better now
|
||||
//Update: There is a pull request available now, and using a version value,
|
||||
//we will be able to do either format (ESS out of order vs ESS in correct order)
|
||||
|
||||
//reset ks_idx to 0
|
||||
ks_idx = 0;
|
||||
@@ -1924,9 +1516,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
imbe_counter++;
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "\n IMBE# %02d; KS_IDX: %d; KS_IDX_I: %d; ", imbe_counter, ks_idx, ks_idx_i);
|
||||
|
||||
//36 hex characters on 'hex' which is the IMBE interleaved C codewords
|
||||
if (version == 1)
|
||||
ks_idx_i = imbe_str_to_decode(opts, state, str_buffer, ks_i, ks_idx_i, is_enc, ks_available);
|
||||
@@ -1934,10 +1523,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//skip LSD bits in-between these two IMBE voice frames
|
||||
if (imbe_counter == 8 || imbe_counter == 17)
|
||||
{
|
||||
ks_idx_i += 16;
|
||||
ks_idx += 16;
|
||||
}
|
||||
|
||||
//juggle keystreams and reset the I counter (if version 1)
|
||||
if (imbe_counter == 9 && version == 1)
|
||||
@@ -1958,7 +1544,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "\n IMBE# %02d; KS_IDX: %d; KS_IDX_I: %d; ", imbe_counter, ks_idx, ks_idx_i);
|
||||
// fprintf (stderr, " # %02d; KS_IDX_I: %04d;", imbe_counter, ks_idx_i);
|
||||
|
||||
//debug
|
||||
// if (is_enc == 1 && ks_available == 0)
|
||||
@@ -1967,7 +1553,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
// fprintf (stderr, " Enc Play;");
|
||||
|
||||
}
|
||||
else if (protocol == 2) //P25p2 or DMR AMBE+2
|
||||
else if (protocol == 2) //P25p2 AMBE
|
||||
{
|
||||
//debug print current str_buffer
|
||||
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
|
||||
@@ -1981,47 +1567,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
// else if (is_enc == 1 && ks_available == 1)
|
||||
// fprintf (stderr, " Enc Play;");
|
||||
|
||||
//increment AMBE+2 counter
|
||||
ambe2_counter++;
|
||||
|
||||
//reset if over for DMR 18 AMBE+2 frames
|
||||
if (dmra_le && ambe2_counter == 18)
|
||||
{
|
||||
ambe2_counter = 0;
|
||||
ks_idx = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (protocol == 3) //NXDN AMBE+2 (EHR)
|
||||
{
|
||||
//debug print current str_buffer
|
||||
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
|
||||
|
||||
//18 hex characters on 'hex' which is the AMBE interleaved C codewords
|
||||
ks_idx = ambe2_str_to_decode(opts, state, str_buffer, ks, ks_idx, is_dmra, is_enc, ks_available);
|
||||
|
||||
//debug
|
||||
// if (is_enc == 1 && ks_available == 0)
|
||||
// fprintf (stderr, " Enc Mute;");
|
||||
// else if (is_enc == 1 && ks_available == 1)
|
||||
// fprintf (stderr, " Enc Play;");
|
||||
|
||||
//increment AMBE+2 counter
|
||||
ambe2_counter++;
|
||||
|
||||
//Really actually need the Structure Field (PF values here)
|
||||
//reset if over for NXDN 16 AMBE+2 frames for Scrambler
|
||||
if (alg_id == 1 && ambe2_counter == 16)
|
||||
{
|
||||
ambe2_counter = 0;
|
||||
ks_idx = 0;
|
||||
}
|
||||
else if (alg_id > 1 && ambe2_counter == 32)
|
||||
{
|
||||
ambe2_counter = 0;
|
||||
ks_idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ dmr_filter(short sample)
|
||||
short
|
||||
nxdn_filter(short sample)
|
||||
{
|
||||
return dsd_input_filter(sample, 2);
|
||||
return dsd_input_filter(sample, 4);
|
||||
}
|
||||
|
||||
short
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#include "p25p1_check_nid.h"
|
||||
|
||||
void
|
||||
printFrameInfo (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
@@ -260,10 +262,21 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
processYSF(opts, state);
|
||||
return;
|
||||
}
|
||||
//M17 STR
|
||||
else if (state->synctype == 16)
|
||||
//M17
|
||||
else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) ||
|
||||
(state->synctype == 76) || (state->synctype == 77) || (state->synctype == 86) || (state->synctype == 87) ||
|
||||
(state->synctype == 99) || (state->synctype == 98) )
|
||||
{
|
||||
processM17STR(opts, state);
|
||||
if (state->synctype == 98 || state->synctype == 99) //preamble only
|
||||
skipDibit(opts, state, 8); //skip dibits to prime the demodulator
|
||||
else if (state->synctype == 16 || state->synctype == 17)
|
||||
processM17STR(opts, state);
|
||||
else if (state->synctype == 76 || state->synctype == 77) {} //Not available yet
|
||||
// processM17BRT(opts, state); //Not available yet
|
||||
else if (state->synctype == 86 || state->synctype == 87)
|
||||
processM17PKT(opts, state);
|
||||
else
|
||||
processM17LSF(opts, state);
|
||||
return;
|
||||
}
|
||||
//P25 P2
|
||||
|
||||
@@ -162,6 +162,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
char synctest20[21]; //YSF
|
||||
char synctest48[49]; //EDACS
|
||||
char synctest8[9]; //M17
|
||||
char synctest16[17]; //M17 Preamble
|
||||
char modulation[8];
|
||||
char *synctest_p;
|
||||
char synctest_buf[10240]; //what actually is assigned to this, can't find its use anywhere?
|
||||
@@ -207,8 +208,9 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
t = 0;
|
||||
synctest10[10] = 0;
|
||||
synctest[24] = 0;
|
||||
synctest8[8] = 0;
|
||||
synctest8[8] = 0; //M17, wasn't initialized or terminated (source of much pain and frustration in Cygwin)
|
||||
synctest12[12] = 0;
|
||||
synctest16[16] = 0; //M17, wasn't initialized or terminated (source of much pain and frustration in Cygwin)
|
||||
synctest48[48] = 0;
|
||||
synctest32[32] = 0;
|
||||
synctest20[20] = 0;
|
||||
@@ -379,19 +381,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
lbuf2[i] = lbuf[i];
|
||||
}
|
||||
qsort (lbuf2, t_max, sizeof (int), comp);
|
||||
|
||||
//on shorter sync patterns, we need to look at the edges since we don't have many to pull from
|
||||
if (t_max < 13)
|
||||
{
|
||||
lmin = (lbuf2[0] + lbuf2[1] + lbuf2[2]) / 3;
|
||||
lmax = (lbuf2[t_max - 3] + lbuf2[t_max - 2] + lbuf2[t_max - 1]) / 3;
|
||||
}
|
||||
//on longer sync patterns, we can get more towards the middle and eliminate outlier values
|
||||
else
|
||||
{
|
||||
lmin = (lbuf2[2] + lbuf2[3] + lbuf2[4]) / 3;
|
||||
lmax = (lbuf2[t_max - 5] + lbuf2[t_max - 4] + lbuf2[t_max - 3]) / 3;
|
||||
}
|
||||
lmin = (lbuf2[1] + lbuf2[2] + lbuf2[3]) / 3;
|
||||
lmax = (lbuf2[t_max - 3] + lbuf2[t_max - 2] + lbuf2[t_max - 1]) / 3;
|
||||
|
||||
if (state->rf_mod == 1)
|
||||
{
|
||||
@@ -428,8 +419,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
|
||||
//if using an rtl input method, do not look for sync patterns if the rms value is lower than our 'soft squelch' level
|
||||
if (opts->audio_in_type == 3 && opts->rtl_rms < opts->rtl_squelch_level)
|
||||
//if using an rtl input method, do not look for sync patterns if the pwr value is lower than our 'soft squelch' level
|
||||
if (opts->audio_in_type == 3 && opts->rtl_pwr < opts->rtl_squelch_level)
|
||||
{
|
||||
if (opts->frame_nxdn48 == 1 || opts->frame_nxdn96 == 1 || opts->frame_dpmr == 1 || opts->frame_m17 == 1)
|
||||
{
|
||||
@@ -575,24 +566,139 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
//end YSF sync
|
||||
|
||||
//M17 Sync -- Just STR
|
||||
//M17 Sync -- Just STR and LSF for now
|
||||
strncpy(synctest16, (synctest_p - 15), 16);
|
||||
strncpy(synctest8, (synctest_p - 7), 8);
|
||||
if(opts->frame_m17 == 1)
|
||||
{
|
||||
if (strcmp(synctest8, M17_STR) == 0)
|
||||
//preambles will skip dibits in an attempt to prime the
|
||||
//demodulator but not attempt any decoding
|
||||
if (strcmp(synctest8, M17_PRE) == 0)
|
||||
{
|
||||
printFrameSync (opts, state, "+M17 STR", synctest_pos + 1, modulation);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->lastsynctype == 16)
|
||||
if (opts->inverted_m17 == 0)
|
||||
{
|
||||
state->lastsynctype = 16;
|
||||
return (16);
|
||||
printFrameSync (opts, state, "+M17 PREAMBLE", synctest_pos + 1, modulation);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
state->lastsynctype = 98;
|
||||
fprintf (stderr, "\n");
|
||||
return (98);
|
||||
}
|
||||
}
|
||||
else if (strcmp(synctest8, M17_PIV) == 0)
|
||||
{
|
||||
if (opts->inverted_m17 == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "-M17 PREAMBLE", synctest_pos + 1, modulation);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
state->lastsynctype = 99;
|
||||
fprintf (stderr, "\n");
|
||||
return (99);
|
||||
}
|
||||
}
|
||||
else if (strcmp(synctest8, M17_PKT) == 0)
|
||||
{
|
||||
if (opts->inverted_m17 == 0)
|
||||
{
|
||||
printFrameSync (opts, state, "+M17 PKT", synctest_pos + 1, modulation);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->lastsynctype == 86 || state->lastsynctype == 8)
|
||||
{
|
||||
state->lastsynctype = 86;
|
||||
return (86);
|
||||
}
|
||||
state->lastsynctype = 86;
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
// else //unknown, -BRT?
|
||||
// {
|
||||
// printFrameSync (opts, state, "-M17 BRT", synctest_pos + 1, modulation);
|
||||
// state->carrier = 1;
|
||||
// state->offset = synctest_pos;
|
||||
// state->max = ((state->max) + lmax) / 2;
|
||||
// state->min = ((state->min) + lmin) / 2;
|
||||
// if (state->lastsynctype == 77)
|
||||
// {
|
||||
// state->lastsynctype = 77;
|
||||
// return (77);
|
||||
// }
|
||||
// state->lastsynctype = 77;
|
||||
// fprintf (stderr, "\n");
|
||||
// }
|
||||
}
|
||||
else if (strcmp(synctest8, M17_STR) == 0)
|
||||
{
|
||||
if (opts->inverted_m17 == 0)
|
||||
{
|
||||
printFrameSync (opts, state, "+M17 STR", synctest_pos + 1, modulation);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->lastsynctype == 16 || state->lastsynctype == 8)
|
||||
{
|
||||
state->lastsynctype = 16;
|
||||
return (16);
|
||||
}
|
||||
state->lastsynctype = 16;
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printFrameSync (opts, state, "-M17 LSF", synctest_pos + 1, modulation);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->lastsynctype == 99)
|
||||
{
|
||||
state->lastsynctype = 9;
|
||||
return (9);
|
||||
}
|
||||
state->lastsynctype = 9;
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
else if (strcmp(synctest8, M17_LSF) == 0)
|
||||
{
|
||||
if (opts->inverted_m17 == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "-M17 STR", synctest_pos + 1, modulation);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->lastsynctype == 17 || state->lastsynctype == 9)
|
||||
{
|
||||
state->lastsynctype = 17;
|
||||
return (17);
|
||||
}
|
||||
state->lastsynctype = 17;
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printFrameSync (opts, state, "+M17 LSF", synctest_pos + 1, modulation);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->lastsynctype == 98)
|
||||
{
|
||||
state->lastsynctype = 8;
|
||||
return (8);
|
||||
}
|
||||
state->lastsynctype = 8;
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
state->lastsynctype = 16;
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
//end M17
|
||||
@@ -1151,8 +1257,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
}
|
||||
#ifdef NXDN_OLD_SYNC
|
||||
//NXDN (previous sync, now a cmake option -DNOS=ON ..)
|
||||
|
||||
//NXDN
|
||||
else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
|
||||
{
|
||||
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
|
||||
@@ -1166,14 +1272,11 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
)
|
||||
{
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " %s ", synctest10);
|
||||
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->lastsynctype == 28)
|
||||
{
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (28);
|
||||
}
|
||||
@@ -1191,72 +1294,9 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
)
|
||||
{
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " %s ", synctest10);
|
||||
|
||||
if (state->lastsynctype == 29)
|
||||
{
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (29);
|
||||
}
|
||||
state->lastsynctype = 29;
|
||||
}
|
||||
}
|
||||
|
||||
#else //new sync
|
||||
//NXDN (positive sync only)
|
||||
else if (opts->inverted_nxdn == 0 && ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) )
|
||||
{
|
||||
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
|
||||
if (
|
||||
(strcmp (synctest10, "3131331131") == 0 )
|
||||
|| (strcmp (synctest10, "3331331131") == 0 )
|
||||
|| (strcmp (synctest10, "3131331111") == 0 )
|
||||
// || (strcmp (synctest10, "3331331111") == 0 )
|
||||
// || (strcmp (synctest10, "3131311131") == 0 )
|
||||
)
|
||||
{
|
||||
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " %s ", synctest10);
|
||||
|
||||
if (state->lastsynctype == 28)
|
||||
{
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (28);
|
||||
}
|
||||
state->lastsynctype = 28;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//NXDN (inverted sync only)
|
||||
else if (opts->inverted_nxdn == 1 && ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) )
|
||||
{
|
||||
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
|
||||
if (
|
||||
(strcmp (synctest10, "1313113313") == 0 )
|
||||
|| (strcmp (synctest10, "1113113313") == 0 )
|
||||
|| (strcmp (synctest10, "1313113333") == 0 )
|
||||
// || (strcmp (synctest10, "1113113333") == 0 )
|
||||
// || (strcmp (synctest10, "1313133313") == 0 )
|
||||
)
|
||||
{
|
||||
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " %s ", synctest10);
|
||||
|
||||
if (state->lastsynctype == 29)
|
||||
{
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
@@ -1264,9 +1304,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
state->lastsynctype = 29;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
//Provoice Conventional -- Some False Positives due to shortened frame sync pattern, so use squelch if possible
|
||||
#ifdef PVCONVENTIONAL
|
||||
|
||||
597
src/dsd_gps.c
597
src/dsd_gps.c
@@ -129,8 +129,8 @@ void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input)
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
char * datestr = getDate();
|
||||
char * timestr = getTime();
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
@@ -247,8 +247,8 @@ void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
char * datestr = getDate();
|
||||
char * timestr = getTime();
|
||||
|
||||
int s = (int)fkph; //rounded interger format for the log report
|
||||
int a = 0;
|
||||
@@ -281,146 +281,131 @@ void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint
|
||||
|
||||
}
|
||||
|
||||
//harris location on PTT (MAC) 3.100
|
||||
void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot, int phase)
|
||||
//restructured the Harris GPS to flow more like the DMR UDT NMEA Format when possible
|
||||
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot)
|
||||
{
|
||||
|
||||
//I really wonder about the engineers who laid out this message
|
||||
//GPS working, Time Working (old method), COG and Speed are logical on due west sample
|
||||
//TODO: Fiddle/Test various ns and ew bits for proper signs in case its located elsewhere
|
||||
//UPDATE: Found two contiguous bits that always work on all my accumulated samples
|
||||
//the bits also place it with the 'lone' flagged bit observed prior
|
||||
|
||||
//NOTE: I have only seen the MAC variation of this message description, I cannot 100% confirm
|
||||
//the two block LCW format is identical, but it does carry the same amount of relevant bytes
|
||||
//observation shows the GPS coordinates are accurate on LCW format, but I cannot
|
||||
//vouch for the COG,SPEED,TIME, EMG and GPS Quality elements when in LCW format.
|
||||
uint8_t nmea_ns = input[88]; //north/south (lat sign) //72 previously used bit
|
||||
uint8_t nmea_ew = input[89]; //east/west (lon sign) //88 previously used bit
|
||||
uint8_t nmea_q = input[147]; //Quality Indicator (last fix or current fix)
|
||||
// uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 7); //129,7
|
||||
uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 6); //this is one bit shorter than called for, but I believe it works
|
||||
|
||||
//starting at relevant information, bytes 0,1,2,3
|
||||
uint16_t lat_min_dec = (uint16_t)ConvertBitIntoBytes(input+0, 16);
|
||||
uint16_t lat_ns = input[16]; //0 == N, 1 == S
|
||||
uint16_t lat_min_int = (uint16_t)ConvertBitIntoBytes(input+17, 7);
|
||||
uint16_t lat_deg_int = (uint16_t)ConvertBitIntoBytes(input+24, 8);
|
||||
//Latitude Bits
|
||||
uint8_t nmea_ndeg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //Latitude Degrees
|
||||
uint8_t nmea_nmin = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //Latitude Minutes
|
||||
uint16_t nmea_nminf = (uint16_t)ConvertBitIntoBytes(&input[42], 14); //Latitude Fractions of Minutes
|
||||
|
||||
//starting at relevant information, bytes 4,5,6,7
|
||||
uint16_t lon_min_dec = (uint16_t)ConvertBitIntoBytes(input+32, 16);
|
||||
uint16_t lon_ew = input[48]; //0 == N, 1 == S
|
||||
uint16_t lon_min_int = (uint16_t)ConvertBitIntoBytes(input+49, 7);
|
||||
uint16_t lon_deg_int = (uint16_t)ConvertBitIntoBytes(input+56, 8);
|
||||
//Longitude Bits
|
||||
uint8_t nmea_edeg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //Longitude Degrees
|
||||
uint8_t nmea_emin = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //Longitude Minutes
|
||||
uint16_t nmea_eminf = (uint16_t)ConvertBitIntoBytes(&input[74], 14); //Longitude Fractions of Minutes
|
||||
|
||||
//starting at relevant information, bytes 8,9
|
||||
uint32_t time_secs_low = (uint32_t)ConvertBitIntoBytes(input+64, 16);
|
||||
//NOTE: Removed UTC hh.mm.ss6 format found in DMR UDT, could not make it work
|
||||
//with bits already established for time and it works extremely well as is
|
||||
|
||||
//starting at relevant information, bytes 10,11,12,13
|
||||
uint16_t course_low = (uint16_t)ConvertBitIntoBytes(input+80, 8);
|
||||
uint16_t speed_low = (uint16_t)ConvertBitIntoBytes(input+88, 8);
|
||||
uint16_t course_high = (uint16_t)ConvertBitIntoBytes(input+96+4, 4); //says 3,2,1,0,
|
||||
uint16_t speed_high = (uint16_t)ConvertBitIntoBytes(input+96+0, 4); //says 7,6,5,4
|
||||
//Course Over Ground in Degrees (0-360)
|
||||
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[135], 9) % 360; //course over ground in degrees //working on due west sample without %360
|
||||
|
||||
//starting at relevant information, byte 14
|
||||
uint32_t time_secs_high = input[104];
|
||||
uint8_t status_emergency = input[105];
|
||||
uint8_t gps_quality = (uint16_t)ConvertBitIntoBytes(input+106, 2);
|
||||
uint8_t gps_sat_num = (uint16_t)ConvertBitIntoBytes(input+108, 4);
|
||||
|
||||
//start calculating things
|
||||
float lat_DDMMmm = (float)lat_deg_int + ((float)lat_min_int / 60.0f) + ((float)lat_min_dec / 600000.0f);
|
||||
float lon_DDMMmm = (float)lon_deg_int + ((float)lon_min_int / 60.0f) + ((float)lon_min_dec / 600000.0f);
|
||||
|
||||
if (lat_ns)
|
||||
lat_DDMMmm *= -1.0f;
|
||||
if (lon_ew)
|
||||
lon_DDMMmm *= -1.0f;
|
||||
|
||||
//course conversion
|
||||
uint32_t course = (course_high << 4) | course_low; //tenths of degrees
|
||||
float course_deg = (float)course / 10.0f; //actual value
|
||||
|
||||
//speed conversion
|
||||
uint32_t speed_knots = (speed_high << 4) | speed_low; //tenths of degrees
|
||||
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
|
||||
fmps = ((float)speed_knots / 10.0f) * 0.514444; UNUSED(fmps);
|
||||
fmph = ((float)speed_knots / 10.0f) * 1.15078f; UNUSED(fmph);
|
||||
fkph = ((float)speed_knots / 10.0f) * 1.852f; UNUSED(fkph);
|
||||
|
||||
//seconds since midnight as a 17-bit representation and calculation to HHMMSS format
|
||||
uint32_t sec_m = (time_secs_high << 16) | time_secs_low;
|
||||
uint32_t thour = sec_m / 3600;
|
||||
uint32_t tmin = (sec_m % 3600) / 60;
|
||||
uint32_t tsec = (sec_m % 3600) % 60;
|
||||
//timestamp (16-bit value w/ appended 17th bit to MSB)
|
||||
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16);
|
||||
rtime |= input[144] << 16; //this bit is flagged on after 0xFFFF rollover in the late afternoon
|
||||
uint32_t thour = rtime / 3600;
|
||||
uint32_t tmin = (rtime % 3600) / 60;
|
||||
uint32_t tsec = (rtime % 3600) % 60;
|
||||
|
||||
//lat and lon conversion
|
||||
char deg_glyph[4];
|
||||
sprintf (deg_glyph, "%s", "°");
|
||||
float latitude = 0.0f;
|
||||
float longitude = 0.0f;
|
||||
float m_unit = 1.0f / 60.0f;
|
||||
float mm_unit = 1.0f / 600000.0f;
|
||||
|
||||
//speed conversion
|
||||
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
|
||||
fmps = (float)nmea_speed * 0.514444; UNUSED(fmps);
|
||||
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
|
||||
fkph = (float)nmea_speed * 1.852f; UNUSED(fkph);
|
||||
|
||||
//calculate decimal representation of latidude and longitude (need some samples to test)
|
||||
latitude = ( (float)nmea_ndeg + ((float)nmea_nmin*m_unit) + ((float)nmea_nminf*mm_unit) );
|
||||
longitude = ( (float)nmea_edeg + ((float)nmea_emin*m_unit) + ((float)nmea_eminf*mm_unit) );
|
||||
|
||||
//This is opposite (seemingly) compared to the version specified by DMR UDT NMEA, could still be different bits involved (to be tested)
|
||||
// if (nmea_ns) latitude *= -1.0f; //1 is South, 0 is North
|
||||
// if (nmea_ew) longitude *= -1.0f; //1 is West, 0 is East
|
||||
|
||||
//DMR UDT NMEA original version (working now with new bit selection 88 and 89 like how DMR UDT NMEA format specifies)
|
||||
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
|
||||
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
|
||||
|
||||
//is this from LCW, or MAC?
|
||||
uint16_t header = (uint16_t)ConvertBitIntoBytes(&input[0], 16);
|
||||
|
||||
//print results
|
||||
fprintf (stderr, "\n");
|
||||
if (phase == 2)
|
||||
if (header == 0x2AA4)
|
||||
fprintf (stderr, " SRC: %08d;", src);
|
||||
else
|
||||
fprintf (stderr, " VCH: %d - SRC: %08d;", slot, src);
|
||||
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph);
|
||||
|
||||
//Speed in Knots (assuming in knots, and not in mps, or kph)
|
||||
if (header != 0x2AA4) //can't yet verify this is present or accurate on LCW
|
||||
{
|
||||
fprintf (stderr, " LCH: %d;", slot);
|
||||
if (src != 0)
|
||||
fprintf (stderr, " SRC: %08d;", src);
|
||||
if (nmea_speed > 126)
|
||||
fprintf (stderr, " SPD > 126 knots or %f MPH;", fmph); //using MPH here, its Harris, they're in the U.S.
|
||||
else
|
||||
fprintf (stderr, " SRC: UNK;");
|
||||
|
||||
if (gps_quality < 3) //GPS hardware present
|
||||
{
|
||||
fprintf (stderr, " GPS: %f%s, %f%s;", lat_DDMMmm, deg_glyph, lon_DDMMmm, deg_glyph);
|
||||
if (gps_quality == 0)
|
||||
fprintf (stderr, " Last Fix;"); //No fix still holds last values
|
||||
else if (gps_quality == 1)
|
||||
fprintf (stderr, " Curr Fix;");
|
||||
else if (gps_quality == 2)
|
||||
fprintf (stderr, " Diff Fix;"); //presumably either lat or lon only changed, not both?
|
||||
|
||||
fprintf (stderr, " #Sats: %d;", gps_sat_num);
|
||||
}
|
||||
else if (gps_quality == 3)
|
||||
fprintf (stderr, " GPS: Not Enabled / No Hardware Present;");
|
||||
|
||||
fprintf (stderr, " COG: %f%s;", course_deg, deg_glyph);
|
||||
fprintf (stderr, " SPD: %f k/h;", fkph);
|
||||
|
||||
if (sec_m != 0x1FFFF) // && sec_m != 0 (so says the doc, but midnight is a valid time)
|
||||
fprintf (stderr, " T: %02d:%02d:%02d;", thour, tmin, tsec);
|
||||
|
||||
if (status_emergency)
|
||||
fprintf (stderr, " Emergency;");
|
||||
fprintf (stderr, " SPD: %d knots; %f MPH;", nmea_speed, fmph);
|
||||
}
|
||||
else if (phase == 1)
|
||||
{
|
||||
if (src != 0)
|
||||
fprintf (stderr, " SRC: %08d;", src);
|
||||
else
|
||||
fprintf (stderr, " SRC: UNK;");
|
||||
fprintf (stderr, " GPS: %f%s, %f%s;", lat_DDMMmm, deg_glyph, lon_DDMMmm, deg_glyph);
|
||||
|
||||
//again, can't vouch 100%, so just going to dump the GPS Coordinates and zero out other values for LRRP file
|
||||
fkph = 0.0f;
|
||||
course_deg = 0.0f;
|
||||
}
|
||||
//Course Over Ground (COG)
|
||||
fprintf (stderr, " COG: %03d%s;", nmea_cog, deg_glyph); //%360
|
||||
|
||||
//debug speed
|
||||
// fprintf (stderr, " RSPD: %02X;", nmea_speed);
|
||||
|
||||
//debug cog
|
||||
// fprintf (stderr, " RCOG: %03X;", nmea_cog >> 0);
|
||||
|
||||
//UTC Time
|
||||
fprintf (stderr, " T: %02d:%02d:%02d UTC;", thour, tmin, tsec);
|
||||
|
||||
//Fix Indicator
|
||||
if (nmea_q) fprintf (stderr, " Current Fix;");
|
||||
else fprintf (stderr, " Last Fix;");
|
||||
|
||||
//save to ncurses string
|
||||
sprintf (state->dmr_embedded_gps[slot], "(%f%s, %f%s)", lat_DDMMmm, deg_glyph, lon_DDMMmm, deg_glyph);
|
||||
sprintf (state->dmr_embedded_gps[slot], "(%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
|
||||
|
||||
//save to event history string
|
||||
if (state->event_history_s[slot].Event_History_Items[0].source_id == src && src != 0)
|
||||
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]);
|
||||
|
||||
//save to LRRP report for mapping/logging
|
||||
if (opts->lrrp_file_output == 1 && src != 0 && gps_quality != 3)
|
||||
FILE * pFile; //file pointer
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
char * datestr = getDate();
|
||||
char * timestr = getTime();
|
||||
|
||||
//rounded interger formats for the log report
|
||||
int s = (int)fkph;
|
||||
int a = (int)course_deg;
|
||||
int a = nmea_cog;
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
FILE * pFile; //file pointer
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
fprintf (pFile, "%s\t", datestr );
|
||||
fprintf (pFile, "%s\t", timestr );
|
||||
fprintf (pFile, "%s\t", timestr ); //could switch to UTC time on PDU if desired
|
||||
fprintf (pFile, "%08d\t", src);
|
||||
fprintf (pFile, "%.6lf\t", lat_DDMMmm);
|
||||
fprintf (pFile, "%.6lf\t", lon_DDMMmm);
|
||||
fprintf (pFile, "%.6lf\t", latitude);
|
||||
fprintf (pFile, "%.6lf\t", longitude);
|
||||
fprintf (pFile, "%d\t ", s);
|
||||
fprintf (pFile, "%d\t ", a);
|
||||
fprintf (pFile, "\n");
|
||||
@@ -439,14 +424,147 @@ void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t
|
||||
|
||||
}
|
||||
|
||||
//debugging dump of this message
|
||||
// if (opts->payload == 1)
|
||||
// {
|
||||
// fprintf (stderr, "\n ");
|
||||
// for (int i = 0; i < 14; i++)
|
||||
// fprintf (stderr, "%02X ", (uint8_t)ConvertBitIntoBytes(input+(i*8), 8));
|
||||
// }
|
||||
//NOTE: There seems to be a few more octets left undecoded in this PDU
|
||||
//not including the CRC obviously, unsure of their meaning, could relate
|
||||
//to other GPS functions like precision, etc, unknown.
|
||||
|
||||
//NOTE2: All these values are just best effort based on observation and
|
||||
//making map points match using logic and distance over time for speed and course
|
||||
//without any documentation, any of these values may still be wrong
|
||||
|
||||
}
|
||||
|
||||
//fallback version (if desired/required)
|
||||
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input)
|
||||
{
|
||||
|
||||
uint8_t lat_sign, lat_deg, lat_min = 0;
|
||||
uint8_t lon_sign, lon_deg, lon_min = 0;
|
||||
uint16_t lat_mmin = 0;
|
||||
uint16_t lon_mmin = 0;
|
||||
|
||||
//potentially in this PDU, but unverifiable without documentation or reasonable
|
||||
//mathematical proof vs map points and direction of travel / distance over time (assuming the radio is facing that direction)
|
||||
uint16_t rspeed = (uint16_t)ConvertBitIntoBytes(&input[136], 8); //MSB //136
|
||||
rspeed = (rspeed << 8) + (uint16_t)ConvertBitIntoBytes(&input[128], 8); //LSB //128
|
||||
|
||||
//this works okay for example of driver driving due west at the speed limit of the road (fluke?)
|
||||
uint16_t rangle = (uint16_t)ConvertBitIntoBytes(&input[120], 8); //120,8
|
||||
float fspeed = (float)rspeed;
|
||||
float fangle = (float)rangle;
|
||||
|
||||
fspeed /= 255.0f; //unit value of 1 bit
|
||||
fspeed *= 1.56f; //this is based on an observation of a driver moving approx 210 meters in about 8 seconds and this makes it just under the speed limit on the road there
|
||||
// fangle *= 360.0f/255.0f; //unit value of 1 bit
|
||||
fangle *= 2.0f; //may exceed 360 degrees as is (use %)
|
||||
|
||||
int s, a = 0;
|
||||
s = (int)fspeed * 3.6;
|
||||
a = (int)fangle % 360;
|
||||
|
||||
//fix and quality indicators?
|
||||
uint8_t fix = input[147]; UNUSED(fix);
|
||||
uint8_t quality = (uint16_t)ConvertBitIntoBytes(&input[148], 3); UNUSED(quality);
|
||||
|
||||
//timestamp (16-bit value w/ appended 17th bit to MSB)
|
||||
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16); //seconds since midnight since last GPS fix (whatever the radio has as internal time)
|
||||
rtime |= input[144] << 16; //test appending this as bit 17 (observed this bit set after the afternoon 0xFFFF rollover)
|
||||
uint32_t thour = rtime / 3600;
|
||||
uint32_t tmin = (rtime % 3600) / 60;
|
||||
uint32_t tsec = (rtime % 3600) % 60;
|
||||
|
||||
float lat_dec = 0.0f;
|
||||
float lon_dec = 0.0f;
|
||||
|
||||
char deg_glyph[4];
|
||||
sprintf (deg_glyph, "%s", "°");
|
||||
|
||||
//This appears to be similar to the NMEA GPGGA format (DDmm.mm) but
|
||||
//octets are ordered in least significant to most significant value
|
||||
lat_mmin = (uint16_t)ConvertBitIntoBytes(&input[40], 16); //? bits required, but grabbing two octets
|
||||
lat_min = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //6 bits required to get 60
|
||||
lat_deg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //7 bits required to get 90
|
||||
lat_sign = input[72]; //64
|
||||
|
||||
lon_mmin = (uint16_t)ConvertBitIntoBytes(&input[72], 16); //? bits required, but grabbing two octets
|
||||
lon_min = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //6 bits required to get 60
|
||||
lon_deg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //8 bits required to get 180
|
||||
lon_sign = input[88]; //88, unsure of a correct location, but on the sample with 0 minutes, this lonely bit was flagged on
|
||||
|
||||
int src = 0;
|
||||
if (slot == 0) src = state->lastsrc;
|
||||
if (slot == 1) src = state->lastsrcR;
|
||||
|
||||
//calculate decimal representation (was a pain to figure out the sub minute values)
|
||||
lat_dec = ( (float)lat_deg + ((float)lat_min/60.0f) + ((float)lat_mmin/600000.0f) );
|
||||
lon_dec = ( (float)lon_deg + ((float)lon_min/60.0f) + ((float)lon_mmin/600000.0f) );
|
||||
|
||||
if (lat_sign)
|
||||
lat_dec *= -1.0f;
|
||||
|
||||
if (lon_sign)
|
||||
lon_dec *= -1.0f;
|
||||
|
||||
//line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " GPS: %f%s, %f%s;", lat_dec, deg_glyph, lon_dec, deg_glyph);
|
||||
|
||||
//gps fix time
|
||||
// fprintf (stderr, " RT: %05X;", rtime); //debug
|
||||
fprintf (stderr, " LTS: %02d:%02d:%02d UTC;", thour, tmin, tsec); //last time synced to GPS
|
||||
|
||||
//speed and direction
|
||||
// fprintf (stderr, " RSPD: %04X;", rspeed); //debug
|
||||
// fprintf (stderr, " RANG: %02X;", rangle); //debug
|
||||
// fprintf (stderr, " MPS: %06.02f;", fspeed);
|
||||
// fprintf (stderr, " KPH: %06.02f;", fspeed * 3.6f);
|
||||
// fprintf (stderr, " MPH: %06.02f;", fspeed * 2.2369f);
|
||||
fprintf (stderr, " DIR: %03d%s;", a, deg_glyph);
|
||||
|
||||
// fprintf (stderr, " FIX: %d", fix);
|
||||
// fprintf (stderr, " QLT: %02d", quality);
|
||||
|
||||
// fprintf (stderr, " SRC: %08d;", src);
|
||||
|
||||
//save to array for ncurses
|
||||
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", lat_dec, deg_glyph, lon_dec, deg_glyph);
|
||||
|
||||
//save to LRRP report for mapping/logging
|
||||
FILE * pFile; //file pointer
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
|
||||
char * datestr = getDate();
|
||||
char * timestr = getTime();
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
fprintf (pFile, "%s\t", datestr );
|
||||
fprintf (pFile, "%s\t", timestr );
|
||||
fprintf (pFile, "%08d\t", src);
|
||||
fprintf (pFile, "%.6lf\t", lat_dec);
|
||||
fprintf (pFile, "%.6lf\t", lon_dec);
|
||||
fprintf (pFile, "%d\t ", s);
|
||||
fprintf (pFile, "%d\t ", a);
|
||||
fprintf (pFile, "\n");
|
||||
fclose (pFile);
|
||||
|
||||
if (timestr != NULL)
|
||||
{
|
||||
free (timestr);
|
||||
timestr = NULL;
|
||||
}
|
||||
if (datestr != NULL)
|
||||
{
|
||||
free (datestr);
|
||||
datestr = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//NOTE: Thanks to DSheirer (SDRTrunk) for helping me work out a few of the things in here
|
||||
//not entirely convinced on some of these calcs (speed, angle, and TS) but sure these bits
|
||||
//are actual speed/direction/timestamps judging from what the coordinates show on a map
|
||||
}
|
||||
|
||||
//externalize embedded GPS - Confirmed working now on NE, NW, SE, and SW coordinates
|
||||
@@ -536,8 +654,8 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
char * datestr = getDate();
|
||||
char * timestr = getTime();
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
@@ -644,9 +762,8 @@ void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||
FILE * pFile; //file pointer
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
char * datestr = getDate();
|
||||
char * timestr = getTime();
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
@@ -710,7 +827,6 @@ void decode_cellocator(dsd_opts * opts, dsd_state * state, uint8_t * input, int
|
||||
//will need to establish a len value for data and contents
|
||||
|
||||
}
|
||||
|
||||
void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
|
||||
{
|
||||
//TODO: This
|
||||
@@ -719,240 +835,3 @@ void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
|
||||
UNUSED(input);
|
||||
UNUSED(len);
|
||||
}
|
||||
|
||||
//WIP: Based on Denny's always brilliant reverse-engineering of GPS reports
|
||||
void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src)
|
||||
{
|
||||
|
||||
//Elevation
|
||||
int16_t elevation = (int16_t)convert_bits_into_output(input+56, 16);
|
||||
|
||||
//Speed & Heading (in tenths)
|
||||
uint16_t speed_raw = (uint16_t)convert_bits_into_output(input+74, 14);
|
||||
uint16_t heading_raw = (uint16_t)convert_bits_into_output(input+92, 12);
|
||||
double speed = speed_raw / 10.0; //knots or k/h
|
||||
double heading = heading_raw / 10.0;
|
||||
|
||||
//Date
|
||||
uint16_t year = (uint16_t)(convert_bits_into_output(input+136,7) + 2000);
|
||||
uint8_t month = (uint8_t) convert_bits_into_output(input+143,4);
|
||||
uint8_t day = (uint8_t)(convert_bits_into_output(input+147,5) + 1);
|
||||
|
||||
//Longitude (DDMM.mmmm format)
|
||||
uint16_t lon_degmin = (uint16_t)convert_bits_into_output(input+152, 16);
|
||||
uint16_t lon_frac = (uint16_t)convert_bits_into_output(input+16, 15);
|
||||
uint8_t lon_hem = (uint8_t) convert_bits_into_output(input+183, 1);
|
||||
double lon_minutes = (lon_degmin % 100) + (lon_frac / 10000.0);
|
||||
double lon_decimal = (lon_degmin / 100) + (lon_minutes / 60.0);
|
||||
double longitude = (lon_hem == 0) ? lon_decimal : -lon_decimal;
|
||||
|
||||
//Latitude (DDMM.mmmm format)
|
||||
uint16_t lat_degmin = (uint16_t)convert_bits_into_output(input+184,16);
|
||||
uint16_t lat_frac = (uint16_t)convert_bits_into_output(input+200,15);
|
||||
uint8_t lat_hem = (uint8_t) convert_bits_into_output(input+215,1);
|
||||
double lat_minutes = (lat_degmin % 100) + (lat_frac / 10000.0);
|
||||
double lat_decimal = (lat_degmin / 100) + (lat_minutes / 60.0);
|
||||
double latitude = (lat_hem == 0) ? lat_decimal : -lat_decimal;
|
||||
|
||||
//Time
|
||||
uint8_t hour = (uint8_t) convert_bits_into_output(input+247,5);
|
||||
uint8_t minute = (uint8_t) convert_bits_into_output(input+252,6);
|
||||
|
||||
char deg_glyph[4];
|
||||
sprintf (deg_glyph, "%s", "°");
|
||||
|
||||
//Report
|
||||
fprintf (stderr, "\n GPS: (%f%s, %f%s) ", latitude, deg_glyph, longitude, deg_glyph);
|
||||
fprintf (stderr, "Speed: %lf k/h; ", speed);
|
||||
fprintf (stderr, "COG: %lf; ", heading);
|
||||
fprintf (stderr, "Elevation: %i; ", elevation);
|
||||
fprintf (stderr, "Date: %04d/%02d/%02d ", year, month, day);
|
||||
fprintf (stderr, "Time: %02d:%02d ", hour, minute);
|
||||
|
||||
//dump to event history
|
||||
sprintf (state->event_history_s[0].Event_History_Items[0].gps_s, "(%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
|
||||
uint32_t source = state->dmr_lrrp_source[0];
|
||||
uint32_t target = state->dmr_lrrp_target[0];
|
||||
char comp_string[500]; memset (comp_string, 0, sizeof(comp_string));
|
||||
sprintf (comp_string, "GPS SRC: %d; TGT: %d;", source, target);
|
||||
watchdog_event_datacall (opts, state, source, target, comp_string, 0);
|
||||
|
||||
//save to LRRP report for mapping/logging
|
||||
if (opts->lrrp_file_output == 1 && src != 0)
|
||||
{
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
|
||||
//rounded interger formats for the log report
|
||||
int s = (int)speed;
|
||||
int a = (int)heading;
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
FILE * pFile; //file pointer
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
fprintf (pFile, "%s\t", datestr );
|
||||
fprintf (pFile, "%s\t", timestr );
|
||||
fprintf (pFile, "%08d\t", src);
|
||||
fprintf (pFile, "%.6lf\t", latitude);
|
||||
fprintf (pFile, "%.6lf\t", longitude);
|
||||
fprintf (pFile, "%d\t ", s);
|
||||
fprintf (pFile, "%d\t ", a);
|
||||
fprintf (pFile, "\n");
|
||||
fclose (pFile);
|
||||
|
||||
if (timestr != NULL)
|
||||
{
|
||||
free (timestr);
|
||||
timestr = NULL;
|
||||
}
|
||||
if (datestr != NULL)
|
||||
{
|
||||
free (datestr);
|
||||
datestr = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//reset source and target from PDU storage
|
||||
state->dmr_lrrp_source[0] = 0;
|
||||
state->dmr_lrrp_target[0] = 0;
|
||||
}
|
||||
|
||||
uint8_t nmea_sentence_checker(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t slot, int len)
|
||||
{
|
||||
|
||||
//Check Start and End, and Validity w/ Checksum
|
||||
int i;
|
||||
uint8_t start_value = (uint8_t)ConvertBitIntoBytes(input, 8);
|
||||
uint8_t end_value = 0;
|
||||
uint8_t checksum_calc = 0;
|
||||
uint8_t checksum_ext = 0;
|
||||
char checksum_str[3];
|
||||
memset(checksum_str, 0, sizeof(checksum_str));
|
||||
uint8_t valid = 0;
|
||||
|
||||
if (start_value == '$' || start_value == '!')
|
||||
{
|
||||
//debug
|
||||
// fprintf (stderr, "Start Okay! ");
|
||||
|
||||
for (i = 1; i < len; i++)
|
||||
{
|
||||
uint8_t value = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8);
|
||||
if (value == '*')
|
||||
{
|
||||
end_value = value;
|
||||
checksum_str[0] = (uint8_t)ConvertBitIntoBytes(input+(i*8)+8, 8);
|
||||
checksum_str[1] = (uint8_t)ConvertBitIntoBytes(input+(i*8)+16, 8);
|
||||
checksum_str[2] = 0;
|
||||
sscanf (checksum_str, "%hhX", &checksum_ext);
|
||||
break;
|
||||
}
|
||||
else if (value >= 0x20 && value < 0x7F)
|
||||
{
|
||||
checksum_calc ^= value;
|
||||
continue;
|
||||
}
|
||||
else break; //anything else that isn't ascii range values
|
||||
}
|
||||
|
||||
//debug
|
||||
// if (end_value == '*')
|
||||
// fprintf (stderr, "Checksum Present! ");
|
||||
|
||||
if (checksum_ext == checksum_calc)
|
||||
{
|
||||
// fprintf (stderr, "Checksum Okay! (%02X / %02X)", checksum_calc, checksum_ext);
|
||||
valid = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// fprintf (stderr, "Checksum Err! (%02X / %02X) ", checksum_calc, checksum_ext);
|
||||
valid = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//dump, if valid
|
||||
memset(state->event_history_s[slot].Event_History_Items[0].text_message, 0, sizeof(state->event_history_s[slot].Event_History_Items[0].text_message));
|
||||
if (valid)
|
||||
{
|
||||
uint8_t ascii = 0;
|
||||
uint16_t crlf = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8);
|
||||
crlf = (uint16_t)ConvertBitIntoBytes(input+(i*8), 16);
|
||||
if (ascii >= 0x20 && ascii < 0x7F)
|
||||
{
|
||||
fprintf (stderr, "%c", ascii);
|
||||
state->event_history_s[slot].Event_History_Items[0].text_message[i] = ascii;
|
||||
}
|
||||
else if (crlf == 0x0D0A) //NMEA Sentences end with 0x0D 0x0A (CR/LF)
|
||||
{
|
||||
fprintf (stderr, "%s", " ");
|
||||
state->event_history_s[slot].Event_History_Items[0].text_message[i+0] = 0x20; //Space
|
||||
state->event_history_s[slot].Event_History_Items[0].text_message[i+1] = 0x20; //Space
|
||||
i+=2;
|
||||
break;
|
||||
}
|
||||
else break; //anything else that isn't ascii range values
|
||||
}
|
||||
|
||||
//check for supplimentary string i.e., $PP25
|
||||
ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8);
|
||||
if (crlf == 0x0D0A && ascii == '$')
|
||||
{
|
||||
for (i = i; i < len; i++)
|
||||
{
|
||||
ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8);
|
||||
crlf = (uint16_t)ConvertBitIntoBytes(input+(i*8), 16);
|
||||
|
||||
if (ascii >= 0x20 && ascii < 0x7F)
|
||||
{
|
||||
fprintf (stderr, "%c", ascii);
|
||||
state->event_history_s[slot].Event_History_Items[0].text_message[i] = ascii;
|
||||
}
|
||||
else if (crlf == 0x0D0A) //Supplimentary Sentences end with 0x0D 0x0A (CR/LF)
|
||||
{
|
||||
state->event_history_s[slot].Event_History_Items[0].text_message[i] = 0x20;
|
||||
break;
|
||||
}
|
||||
else break; //anything else that isn't ascii range values
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf(stderr, "\n %s", state->event_history_s[slot].Event_History_Items[0].text_message);
|
||||
|
||||
//dump to event history
|
||||
state->event_history_s[slot].Event_History_Items[0].text_message[i] = '\0';
|
||||
uint32_t source = state->dmr_lrrp_source[slot];
|
||||
uint32_t target = state->dmr_lrrp_target[slot];
|
||||
char comp_string[500]; memset (comp_string, 0, sizeof(comp_string));
|
||||
sprintf (comp_string, "NMEA SRC: %d; TGT: %d;", source, target);
|
||||
watchdog_event_datacall (opts, state, source, target, comp_string, slot);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (start_value != '$' && start_value != '!')
|
||||
fprintf (stderr, " Not an NMEA Sentence Structure;");
|
||||
else if ( (start_value == '$' || start_value == '!') && end_value != '*')
|
||||
fprintf (stderr, " Possible NMEA Sentence, Missing Ending *;");
|
||||
else if ( start_value != '$' && start_value != '!' && end_value == '*')
|
||||
fprintf (stderr, " Possible NMEA Sentence, Missing Starting $ or !;");
|
||||
else if (checksum_calc != checksum_ext)
|
||||
fprintf (stderr, " NMEA Checksum Error (%02X / %02X);", checksum_calc, checksum_ext);
|
||||
}
|
||||
|
||||
//TODO: Convert to LRRP data and report / GPS on Event History
|
||||
|
||||
state->dmr_lrrp_source[slot] = 0;
|
||||
state->dmr_lrrp_target[slot] = 0;
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
@@ -7,31 +7,26 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
char filename[1024] = "filename.csv";
|
||||
sprintf (filename, "%s", opts->group_in_file);
|
||||
|
||||
//filename[1023] = '\0'; //necessary?
|
||||
char buffer[BSIZE];
|
||||
FILE * fp;
|
||||
fp = fopen(filename, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
if (fp == NULL) {
|
||||
printf("Unable to open group file '%s'\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int row_count = 0;
|
||||
int field_count = 0;
|
||||
|
||||
unsigned int i = 0;
|
||||
while (fgets(buffer, BSIZE, fp))
|
||||
{
|
||||
|
||||
long int group_number = 0; //local group number for array index value
|
||||
UNUSED(group_number);
|
||||
int i = 0;
|
||||
while (fgets(buffer, BSIZE, fp)) {
|
||||
field_count = 0;
|
||||
row_count++;
|
||||
if (row_count == 1)
|
||||
continue; //don't want labels
|
||||
char * field = strtok(buffer, ","); //seperate by comma
|
||||
while (field)
|
||||
{
|
||||
while (field) {
|
||||
|
||||
if (field_count == 0)
|
||||
{
|
||||
@@ -39,38 +34,23 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
|
||||
state->group_array[i].groupNumber = atol(field);
|
||||
fprintf (stderr, "%ld, ", state->group_array[i].groupNumber);
|
||||
}
|
||||
else if (field_count == 1)
|
||||
if (field_count == 1)
|
||||
{
|
||||
// strcpy(state->group_array[i].groupMode, field);
|
||||
strncpy(state->group_array[i].groupMode, field, 2); //only copy two letters max A, B, D, DE
|
||||
strcpy(state->group_array[i].groupMode, field);
|
||||
fprintf (stderr, "%s, ", state->group_array[i].groupMode);
|
||||
}
|
||||
else if (field_count == 2)
|
||||
if (field_count == 2)
|
||||
{
|
||||
// strcpy(state->group_array[i].groupName, field);
|
||||
strncpy(state->group_array[i].groupName, field, 98); //this should also fix potential issues observed in past when there is no comma after the name
|
||||
|
||||
uint16_t len = strlen((const char*)state->group_array[i].groupName);
|
||||
|
||||
//remove any potential linebreaks at this point
|
||||
if (state->group_array[i].groupName[len-1] == '\n')
|
||||
{
|
||||
// fprintf(stderr, " ** LEN: %d; ** ", len); //debug
|
||||
state->group_array[i].groupName[len-1] = '\0';
|
||||
}
|
||||
|
||||
strcpy(state->group_array[i].groupName, field);
|
||||
fprintf (stderr, "%s ", state->group_array[i].groupName);
|
||||
}
|
||||
|
||||
field = strtok(NULL, ",");
|
||||
field_count++;
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
i++;
|
||||
state->group_tally++;
|
||||
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
|
||||
459
src/dsd_main.c
459
src/dsd_main.c
@@ -234,7 +234,7 @@
|
||||
state->dibit_buf_p = state->dibit_buf + 200;
|
||||
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||
//dmr buffer
|
||||
state->dmr_payload_p = state->dmr_payload_buf + 200;
|
||||
state->dmr_payload_p = state->dibit_buf + 200;
|
||||
memset (state->dmr_payload_buf, 0, sizeof (int) * 200);
|
||||
memset (state->dmr_stereo_payload, 1, sizeof(int) * 144);
|
||||
//dmr buffer end
|
||||
@@ -417,7 +417,7 @@
|
||||
// state->p25_vc_freq[1] = 0;
|
||||
|
||||
//new nxdn stuff
|
||||
state->nxdn_part_of_frame = 3;
|
||||
state->nxdn_part_of_frame = 0;
|
||||
state->nxdn_ran = 0;
|
||||
state->nxdn_sf = 0;
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
|
||||
@@ -538,6 +538,7 @@
|
||||
//M17 Storage
|
||||
memset (state->m17_lsf, 0, sizeof(state->m17_lsf));
|
||||
memset (state->m17_pkt, 0, sizeof(state->m17_pkt));
|
||||
state->m17_pbc_ct = 0;
|
||||
state->m17_str_dt = 9;
|
||||
|
||||
state->m17_dst = 0;
|
||||
@@ -551,18 +552,13 @@
|
||||
state->m17_enc = 0;
|
||||
state->m17_enc_st = 0;
|
||||
memset(state->m17_meta, 0, sizeof(state->m17_meta));
|
||||
memset(state->m17_aes_iv, 0, sizeof(state->m17_aes_iv));
|
||||
|
||||
memset(state->m17_text_string, 0, sizeof(state->m17_text_string));
|
||||
memset(state->m17_gnss_string, 0, sizeof(state->m17_gnss_string));
|
||||
memset(state->m17_data_string, 0, sizeof(state->m17_data_string));
|
||||
memset(state->m17_meta_string, 0, sizeof(state->m17_meta_string));
|
||||
sprintf (state->m17_text_string, "%s", "");
|
||||
sprintf (state->m17_gnss_string, "%s", "");
|
||||
sprintf (state->m17_data_string, "%s", "");
|
||||
sprintf (state->m17_meta_string, "%s", "");
|
||||
|
||||
state->m17_viterbi_err = 0.0f;
|
||||
|
||||
//misc str storage
|
||||
// sprintf (state->str50a, "%s", "");
|
||||
// memset (state->str50b, 0, 50*sizeof(char));
|
||||
// memset (state->str50c, 0, 50*sizeof(char));
|
||||
// memset (state->m17sms, 0, 800*sizeof(char));
|
||||
// sprintf (state->m17dat, "%s", "");
|
||||
|
||||
//set float temp buffer to baseline
|
||||
memset (state->audio_out_temp_buf, 0.0f, sizeof(state->audio_out_temp_buf));
|
||||
@@ -636,7 +632,6 @@
|
||||
opts->wav_out_file[0] = 0;
|
||||
opts->wav_out_fileR[0] = 0;
|
||||
opts->wav_out_file_raw[0] = 0;
|
||||
opts->wav_custom_tag[0] = 0;
|
||||
opts->symbol_out_file[0] = 0;
|
||||
opts->lrrp_out_file[0] = 0;
|
||||
opts->event_out_file[0] = 0;
|
||||
@@ -679,26 +674,29 @@
|
||||
opts->uvquality = 3;
|
||||
opts->inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
|
||||
opts->inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
|
||||
opts->inverted_nxdn = 0; //only seek pos by default to reduce false sync pattern detections
|
||||
opts->inverted_m17 = 0; //samples from M17_Education seem to all be positive polarity (same from m17-tools programs)
|
||||
opts->mod_threshold = 26;
|
||||
opts->ssize = 128; //36 default, max is 128, much cleaner data decodes on Phase 2 cqpsk at max
|
||||
opts->msize = 1024; //15 default, max is 1024, much cleaner data decodes on Phase 2 cqpsk at max
|
||||
opts->playfiles = 0;
|
||||
opts->m17encoder = 0;
|
||||
opts->m17encoderbrt = 0;
|
||||
opts->m17encoderpkt = 0;
|
||||
opts->m17decoderip = 0;
|
||||
opts->delay = 0;
|
||||
opts->use_cosine_filter = 1;
|
||||
opts->unmute_encrypted_p25 = 0;
|
||||
//all RTL user options -- enabled AGC by default due to weak signal related issues
|
||||
opts->rtl_dev_index = 0; //choose which device we want by index number
|
||||
opts->rtl_gain_value = 0; //mid value, 0 - AGC - 0 to 49 acceptable values
|
||||
opts->rtl_gain_actual = -100; //mirror dongle.gain actual values -100 is AUTO_GAIN, else nearest_gain(dongle.dev, dongle.gain);
|
||||
opts->rtl_squelch_level = 100; //100 by default, but only affects NXDN and dPMR during framesync test, compared to RMS value
|
||||
opts->rtl_squelch_level = 10000; //default scaled for rtl_pwr (RMS^2 proxy); ~100 RMS equivalent
|
||||
opts->rtl_volume_multiplier = 2; //sample multiplier; This multiplies the sample value to produce a higher 'inlvl' for the demodulator
|
||||
opts->rtl_udp_port = 0; //set UDP port for RTL remote -- 0 by default, will be making this optional for some external/legacy use cases (edacs-fm, etc)
|
||||
opts->rtl_bandwidth = 12; //default is 12, reverted back to normal on this (no inherent benefit)
|
||||
opts->rtlsdr_ppm_error = 0; //initialize ppm with 0 value;
|
||||
opts->rtlsdr_center_freq = 850000000; //set to an initial value (if user is using a channel map, then they won't need to specify anything other than -i rtl if desired)
|
||||
opts->rtl_started = 0;
|
||||
opts->rtl_rms = 0; //root means square power level on rtl input signal
|
||||
opts->rtl_pwr = 0; // mean power approximation level on rtl input signal
|
||||
//end RTL user options
|
||||
opts->pulse_raw_rate_in = 48000;
|
||||
opts->pulse_raw_rate_out = 48000;//
|
||||
@@ -764,6 +762,12 @@
|
||||
opts->udp_portno = 23456; //default port, same os OP25's sockaudio.py
|
||||
sprintf (opts->udp_hostname, "%s", "127.0.0.1");
|
||||
|
||||
//M17 UDP Port and hostname
|
||||
opts->m17_use_ip = 0; //if enabled, open UDP and broadcast IP frame
|
||||
opts->m17_portno = 17000; //default is 17000
|
||||
opts->m17_udp_sock = 0; //actual UDP socket for M17 to send to
|
||||
sprintf (opts->m17_hostname, "%s", "127.0.0.1");
|
||||
|
||||
//tcp input options
|
||||
opts->tcp_sockfd = 0;
|
||||
opts->tcp_portno = 7355; //default favored by SDR++
|
||||
@@ -1037,7 +1041,7 @@
|
||||
state->K2 = 0;
|
||||
state->K3 = 0;
|
||||
state->K4 = 0;
|
||||
state->forced_alg_id = 0; //force key priority over settings from fid/so
|
||||
state->M = 0; //force key priority over settings from fid/so
|
||||
|
||||
state->dmr_stereo = 0; //1, or 0?
|
||||
state->dmrburstL = 17; //initialize at higher value than possible
|
||||
@@ -1060,9 +1064,6 @@
|
||||
state->tyt_ap = 0;
|
||||
state->tyt_bp = 0;
|
||||
state->tyt_ep = 0;
|
||||
state->retevis_ap = 0;
|
||||
state->baofeng_ap = 0;
|
||||
state->csi_ee = 0;
|
||||
|
||||
state->ken_sc = 0;
|
||||
state->any_bp = 0;
|
||||
@@ -1188,9 +1189,7 @@
|
||||
//trunking
|
||||
memset (state->trunk_lcn_freq, 0, sizeof(state->trunk_lcn_freq));
|
||||
memset (state->trunk_chan_map, 0, sizeof(state->trunk_chan_map));
|
||||
//allocate max 24-bit address w/ memory with zero fill (termination) for end of strings, import will only copy 98, leaving the terminator intact
|
||||
state->group_array = calloc(16778000, sizeof(groupinfo));
|
||||
state->group_tally = 0; //is an unsigned int
|
||||
state->group_tally = 0;
|
||||
state->lcn_freq_count = 0; //number of frequncies imported as an enumerated lcn list
|
||||
state->lcn_freq_roll = 0; //needs reset if sync is found?
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
@@ -1206,7 +1205,7 @@
|
||||
state->tg_hold = 0;
|
||||
|
||||
//new nxdn stuff
|
||||
state->nxdn_part_of_frame = 3;
|
||||
state->nxdn_part_of_frame = 0;
|
||||
state->nxdn_ran = 0;
|
||||
state->nxdn_sf = 0;
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
|
||||
@@ -1214,7 +1213,6 @@
|
||||
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||
state->nxdn_alias_block_number = 0;
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
state->nxdn_pn95_seed = 228; //default value
|
||||
|
||||
//site/srv/cch info
|
||||
state->nxdn_location_site_code = 0;
|
||||
@@ -1313,16 +1311,22 @@
|
||||
//M17 Storage
|
||||
memset (state->m17_lsf, 0, sizeof(state->m17_lsf));
|
||||
memset (state->m17_pkt, 0, sizeof(state->m17_pkt));
|
||||
state->m17_pbc_ct = 0;
|
||||
state->m17_str_dt = 9;
|
||||
|
||||
//misc str storage
|
||||
// sprintf (state->str50a, "%s", "");
|
||||
memset (state->str50b, 0, 50*sizeof(char));
|
||||
memset (state->str50c, 0, 50*sizeof(char));
|
||||
memset (state->m17sms, 0, 800*sizeof(char));
|
||||
sprintf (state->m17dat, "%s", "");
|
||||
|
||||
state->m17_dst = 0;
|
||||
state->m17_src = 0;
|
||||
state->m17_can = 0; //can value that was decoded from signal
|
||||
state->m17_can_en = -1; //can value supplied to the encoding side
|
||||
state->m17_rate = 48000; //sampling rate for audio input
|
||||
state->m17_vox = 0; //vox mode enabled on M17 encoder
|
||||
memset(state->m17_dst_csd, 0, sizeof(state->m17_dst_csd));
|
||||
memset(state->m17_src_csd, 0, sizeof(state->m17_src_csd));
|
||||
sprintf (state->m17_dst_str, "%s", "");
|
||||
@@ -1330,19 +1334,10 @@
|
||||
|
||||
state->m17_enc = 0;
|
||||
state->m17_enc_st = 0;
|
||||
state->m17encoder_tx = 0;
|
||||
state->m17encoder_eot = 0;
|
||||
memset(state->m17_meta, 0, sizeof(state->m17_meta));
|
||||
memset(state->m17_aes_iv, 0, sizeof(state->m17_aes_iv));
|
||||
|
||||
memset(state->m17_text_string, 0, sizeof(state->m17_text_string));
|
||||
memset(state->m17_gnss_string, 0, sizeof(state->m17_gnss_string));
|
||||
memset(state->m17_data_string, 0, sizeof(state->m17_data_string));
|
||||
memset(state->m17_meta_string, 0, sizeof(state->m17_meta_string));
|
||||
sprintf (state->m17_text_string, "%s", "");
|
||||
sprintf (state->m17_gnss_string, "%s", "");
|
||||
sprintf (state->m17_data_string, "%s", "");
|
||||
sprintf (state->m17_meta_string, "%s", "");
|
||||
|
||||
state->m17_viterbi_err = 0.0f;
|
||||
|
||||
|
||||
#ifdef USE_CODEC2
|
||||
state->codec2_3200 = codec2_create(CODEC2_MODE_3200);
|
||||
@@ -1400,6 +1395,8 @@
|
||||
printf (" rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)\n");
|
||||
printf (" tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)\n");
|
||||
printf (" tcp:192.168.7.5:7355 for custom address and port \n");
|
||||
printf (" m17udp for M17 UDP/IP socket bind input (default host 127.0.0.1; default port 17000)\n");
|
||||
printf (" m17udp:192.168.7.8:17001 for M17 UDP/IP bind input (Binding Address and Port\n");
|
||||
printf (" filename.bin for OP25/FME capture bin files\n");
|
||||
printf (" filename.wav for 48K/1 wav files (SDR++, GQRX)\n");
|
||||
printf (" filename.wav -s 96000 for 96K/1 wav files (DSDPlus)\n");
|
||||
@@ -1422,6 +1419,8 @@
|
||||
printf (" null for no audio output\n");
|
||||
printf (" udp for UDP socket blaster output (default host 127.0.0.1; default port 23456)\n");
|
||||
printf (" udp:192.168.7.8:23470 for UDP socket blaster output (Target Address and Port\n");
|
||||
printf (" m17udp for M17 UDP/IP socket blaster output (default host 127.0.0.1; default port 17000)\n");
|
||||
printf (" m17udp:192.168.7.8:17001 for M17 UDP/IP blaster output (Target Address and Port\n");
|
||||
printf (" -d <dir> Create mbe data files, use this directory (TDMA version is experimental)\n");
|
||||
printf (" -r <files> Read/Play saved mbe data from file(s)\n");
|
||||
printf (" -g <float> Audio Digital Output Gain (Default: 0 = Auto; )\n");
|
||||
@@ -1437,8 +1436,6 @@
|
||||
printf (" -w <file> Output synthesized speech to a single static .wav file. (Do not use with -P Per Call Switch)\n");
|
||||
printf (" -P Enable Per Call WAV file saving. (Do not use with -w filename.wav single wav file switch)\n");
|
||||
printf (" (Per Call works with everything now and doesn't require ncurses terminal!)\n");
|
||||
printf (" -j <string> Custom Tag(s), colon delimited, to append to Per Call decoded .wav files.\n");
|
||||
printf (" -j TGT_LAPD:TAG_FIRE:FREQ_851987500 (Total 3 Tags, Maximum Letter Length = 100)\n");
|
||||
printf (" -a Enable Call Alert Beep\n");
|
||||
printf (" (Warning! Might be annoying.)\n");
|
||||
printf (" -J <file> Specify Filename for Event Log Output.\n");
|
||||
@@ -1464,12 +1461,34 @@
|
||||
printf (" gain <num> RTL-SDR Device Gain (0-49)(default = 0; Hardware AGC recommended)\n");
|
||||
printf (" ppm <num> RTL-SDR PPM Error (default = 0)\n");
|
||||
printf (" bw <num> RTL-SDR Bandwidth kHz (default = 12)(4, 6, 8, 12, 16, 24) \n");
|
||||
printf (" sq <num> RTL-SDR Squelch Level vs RMS Value (Optional)\n");
|
||||
printf (" sq <num> RTL-SDR Squelch Level vs PWR Value (Optional)\n");
|
||||
// printf (" udp <num> RTL-SDR Legacy UDP Remote Port (Optional -- External Use Only)\n"); //NOTE: This is still available as an option in the ncurses menu
|
||||
printf (" vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)\n");
|
||||
printf (" Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T\n");
|
||||
printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2\n");
|
||||
printf ("\n");
|
||||
printf ("Encoder options:\n");
|
||||
printf (" -fZ M17 Stream Voice Encoder\n");
|
||||
printf (" Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i pulse -6 m17signal.wav -8 -N 2> m17encoderlog.txt\n");
|
||||
printf (" Run M17 Encoding, listening to pulse audio server, with internal decode/playback and output to 48k/1 wav file\n");
|
||||
printf ("\n");
|
||||
printf (" Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i tcp -o pulse -8 -N 2> m17encoderlog.txt\n");
|
||||
printf (" Run M17 Encoding, listening to default tcp input, without internal decode/playback and output to 48k/1 analog output device\n");
|
||||
printf ("\n");
|
||||
printf (" -fP M17 Packet Encoder\n");
|
||||
printf (" Example: dsd-fme -fP -M M17:9:DSD-FME:LWVMOBILE -6 m17pkt.wav -8 -S 'Hello World'\n");
|
||||
printf ("\n");
|
||||
printf (" -fB M17 BERT Encoder\n");
|
||||
printf (" Example: dsd-fme -fB -M M17:9:DSD-FME:LWVMOBILE -6 m17bert.wav -8\n");
|
||||
printf ("\n");
|
||||
printf (" -M M17 Encoding User Configuration String: M17:CAN:SRC:DST:INPUT_RATE:VOX (see examples above).\n");
|
||||
printf (" CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.\n");
|
||||
printf (" BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'\n");
|
||||
printf (" Input Rate Default is 48000; Use Multiples of 8000 up to 48000.\n");
|
||||
printf (" VOX Enabled on 1; (Default = 0)\n");
|
||||
printf (" Values not entered into the M17: string are set to default values.\n");
|
||||
// printf (" -S M17 Packet Encoder SMS String: No more than 772 chars, use single quotations (see example above).\n");
|
||||
// printf (" -S M17 Stream Encoder SMS String: No more than 48 chars, activates 1600 voice (best if broken into six 8 char chunks).\n");
|
||||
printf ("Decoder options:\n");
|
||||
printf (" -fa Auto Detection\n");
|
||||
printf (" -fA Passive Analog Audio Monitor\n");
|
||||
@@ -1481,6 +1500,7 @@
|
||||
printf (" -fx Decode only X2-TDMA\n");
|
||||
printf (" -fy Decode only YSF\n");
|
||||
printf (" -fz Decode only M17*\n");
|
||||
printf (" -fU Decode only M17 UDP/IP Frame***\n");
|
||||
printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n");
|
||||
printf (" -fn Decode only NXDN96* (12.5 kHz)\n");
|
||||
printf (" -fp Decode only ProVoice*\n");
|
||||
@@ -1496,20 +1516,15 @@
|
||||
printf (" -xx Expect non-inverted X2-TDMA signal\n");
|
||||
printf (" -xr Expect inverted DMR signal\n");
|
||||
printf (" -xd Expect inverted ICOM dPMR signal\n");
|
||||
#ifdef NXDN_OLD_SYNC
|
||||
//
|
||||
#else
|
||||
printf (" -xn Expect inverted NXDN/DCR/IDAS signal\n");
|
||||
#endif
|
||||
printf (" -xz Expect inverted M17 signal\n");
|
||||
printf ("\n");
|
||||
printf (" * denotes frame types that cannot be auto-detected.\n");
|
||||
printf (" ** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.\n");
|
||||
printf (" *** configure UDP Input with -i m17:127.0.0.1:17000 \n");
|
||||
printf ("\n");
|
||||
printf ("Advanced Decoder options:\n");
|
||||
printf (" -X <hex> Manually Set P2 Parameters (WACN, SYSID, CC/NAC)\n");
|
||||
printf (" (-X BEE00ABC123)\n");
|
||||
printf (" -_ <dec> Manually Set NXDN PN95 Dibit Scrambler Seed (1-511)\n");
|
||||
printf (" (-* 261)\n");
|
||||
printf (" -D <dec> Manually Set TIII DMR Location Area n bit len (0-10)(10 max)\n");
|
||||
printf (" (Value defaults to max n bit value for site model size)\n");
|
||||
printf (" (Setting 0 will show full Site ID, no area/subarea)\n");
|
||||
@@ -1524,17 +1539,18 @@
|
||||
printf (" Use this feature to allow MAC_SIGNAL even if CRC errors.\n");
|
||||
printf (" -F Relax DMR RAS/CRC CSBK/DATA Pass/Fail\n");
|
||||
printf (" Enabling on some systems could lead to bad channel assignments/site data decoding if bad or marginal signal\n");
|
||||
printf (" -F Relax M17 LSF/PKT CRC Error Checking\n");
|
||||
printf ("\n");
|
||||
printf (" -b <dec> Manually Enter Basic Privacy Key (Decimal Value of Key Number)\n");
|
||||
printf (" (NOTE: This used to be the 'K' option!) \n");
|
||||
printf (" (NOTE: This used to be the 'K' option! \n");
|
||||
printf ("\n");
|
||||
printf (" -H <hex> Manually Enter Hytera 10/32/64 Char Basic Privacy Hex Key (see example below)\n");
|
||||
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
|
||||
printf (" -H 0B57935150 \n");
|
||||
printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n");
|
||||
printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n");
|
||||
printf ("\n");
|
||||
printf (" -H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)\n");
|
||||
printf ("\n"); //may move to using the rkey_array with an offset for additional key values
|
||||
printf (" -H <hex> Manually Enter AES-128 or AES-256 Hex Key (see example below)\n");
|
||||
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
|
||||
printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n");
|
||||
printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n");
|
||||
@@ -1545,28 +1561,14 @@
|
||||
printf (" \n");
|
||||
printf (" -2 <hex> Manually Enter and Enforce TYT 16-bit BP Key Value (DMR) (Hex Value) \n");
|
||||
printf (" \n");
|
||||
printf (" -! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) 128 or 256 bit AP Hex Key (see example below)\n");
|
||||
printf (" -! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) AP Hex Key (see example below)\n");
|
||||
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
|
||||
printf (" -! '736B9A9C5645288B 243AD5CB8701EF8A' \n");
|
||||
printf (" -! '1111111111111111 1111111111111111 1111111111111111 1111111111111111' \n");
|
||||
printf (" \n");
|
||||
printf (" -@ <hex> Manually Enter and Enforce Retevis Advanced Privacy (RC2) 128 or 256 bit AP Hex Key (see example below)\n");
|
||||
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
|
||||
printf (" -@ '736B9A9C5645288B 243AD5CB8701EF8A' \n");
|
||||
printf (" -@ '1122334455667788 99AABBCCDDEEFF11 1122334455667788 99AABBCCDDEEFF11' \n");
|
||||
printf (" \n");
|
||||
printf (" -5 <hex> Manually Enter and Enforce TYT Enhanced Privacy (AES-128) EP Hex Key (see example below)\n");
|
||||
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
|
||||
printf (" -5 '736B9A9C5645288B 243AD5CB8701EF8A' \n");
|
||||
printf (" \n");
|
||||
printf (" -+ <hex> Manually Enter and Enforce Baofeng Advanced Privacy (PC5-128 or PC5-256) AP Hex Key (see example below)\n");
|
||||
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
|
||||
printf (" -+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC' \n");
|
||||
printf (" -+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC EF01EF01EF01EF01 EF01EF01EF01EF01' \n");
|
||||
printf (" \n");
|
||||
printf (" -^ <hex> Manually Enter and Enforce Connect Systems 72-bit (9-byte) Extended Encryption Hex Key (see example below)\n");
|
||||
printf (" -^ 3BBE782C0430008271\n");
|
||||
printf (" \n");
|
||||
printf (" -9 <dec> Manually Enter and Enforce Kenwood 15-bit Scrambler Key Value (DMR) (Dec Value) \n");
|
||||
printf (" \n");
|
||||
printf (" -A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) \n");
|
||||
@@ -1578,8 +1580,6 @@
|
||||
printf (" -S 49:123456789ABC80\n");
|
||||
printf (" For Example, enter 49-bit Keystream (MBP 70) as:\n");
|
||||
printf (" -S 49:ED0AED4AED4AED4A\n");
|
||||
printf (" For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:\n");
|
||||
printf (" -S 49:11AA22BB33CC44\n");
|
||||
printf (" \n");
|
||||
printf (" -k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.\n");
|
||||
printf (" Only supports NXDN, DMR Basic Privacy (decimal value). \n");
|
||||
@@ -1592,8 +1592,6 @@
|
||||
printf (" \n");
|
||||
printf (" -0 Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR) \n");
|
||||
printf (" \n");
|
||||
printf (" -M <hex> Force Entered Alg ID Value over Missing PI header/LE Encryption Identifiers (DMR) \n");
|
||||
printf (" \n");
|
||||
printf (" -3 Disable DMR Late Entry Encryption Identifiers (VC6 Single Burst) \n");
|
||||
printf (" Note: Disable this if false positives on Voice ENC occur. \n");
|
||||
printf ("\n");
|
||||
@@ -1646,8 +1644,8 @@
|
||||
open_rtlsdr_stream(opts);
|
||||
opts->rtl_started = 1; //set here so ncurses terminal doesn't attempt to open it again
|
||||
// #ifdef __arm__
|
||||
// fprintf (stderr, "WARNING: RMS Function is Disabled on ARM Devices (Raspberry Pi) due to High CPU use. \n");
|
||||
// fprintf (stderr, "RMS/Squelch Functionality for NXDN, dPMR, EDACS Analog, M17 and Raw Audio Monitor are unavailable and these modes will not function properly. \n");
|
||||
// fprintf (stderr, "WARNING: PWR Function is Disabled on ARM Devices (Raspberry Pi) due to High CPU use. \n");
|
||||
// fprintf (stderr, "PWR/Squelch Functionality for NXDN, dPMR, EDACS Analog, M17 and Raw Audio Monitor are unavailable and these modes will not function properly. \n");
|
||||
// if (opts->monitor_input_audio == 1) opts->monitor_input_audio = 0;
|
||||
// opts->rtl_squelch_level = 0;
|
||||
// #endif
|
||||
@@ -1779,10 +1777,10 @@
|
||||
if (opts->static_wav_file == 0)
|
||||
{
|
||||
if (opts->wav_out_f != NULL)
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
|
||||
if (opts->wav_out_fR != NULL)
|
||||
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
|
||||
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
|
||||
}
|
||||
|
||||
else if (opts->static_wav_file == 1)
|
||||
@@ -1821,6 +1819,9 @@
|
||||
if (opts->udp_sockfdA)
|
||||
close (opts->udp_sockfdA);
|
||||
|
||||
if (opts->m17_udp_sock)
|
||||
close (opts->m17_udp_sock);
|
||||
|
||||
//close MBE out files
|
||||
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||
@@ -1894,11 +1895,11 @@
|
||||
init_audio_filters(&state); //audio filters
|
||||
init_rrc_filter_memory(); //initialize input filtering
|
||||
InitAllFecFunction();
|
||||
|
||||
CNXDNConvolution_init();
|
||||
|
||||
exitflag = 0;
|
||||
|
||||
while ((c = getopt (argc, argv, "~yhaepPqs:t:v:z:i:o:d:c:g:n:w:B:C:R:f:m:u:x:A:S:G:D:L:V:U:YK:b:H:X:M:NQ:WrlZTF@:!:01:2:345:6:^:7:8_:9:Ek:I:J:O+:j:")) != -1)
|
||||
while ((c = getopt (argc, argv, "~yhaepPqs:t:v:z:i:o:d:c:g:n:w:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:YK:b:H:X:NQ:WrlZTF!:01:2:345:6:7:89:Ek:I:J:O")) != -1)
|
||||
{
|
||||
|
||||
switch (c)
|
||||
@@ -1912,21 +1913,14 @@
|
||||
opts.call_alert = 1;
|
||||
break;
|
||||
|
||||
|
||||
//NOTE: Don't use -*, found it exhibits unusual behavior, was setting
|
||||
//itself to -3 for some reason, may be reading contents of directory
|
||||
//Free'd up switches include: j, O,
|
||||
//
|
||||
|
||||
//make sure to put a colon : after each if they need an argument
|
||||
//or remove colon if no argument required
|
||||
|
||||
//NOTE: The 'K' option for single BP key has been swapped to 'b'
|
||||
//'K' is now used for hexidecimal key.csv imports
|
||||
|
||||
case 'j': //custom wav tags appended to per-call wav files
|
||||
strncpy(opts.wav_custom_tag, optarg, 99);
|
||||
opts.wav_custom_tag[99] = '\0';
|
||||
fprintf(stderr, "Appending Tags %s to per-call wav files. \n", opts.wav_custom_tag);
|
||||
break;
|
||||
|
||||
//this is a debug option hidden from users, but use it to replay .bin files on loop
|
||||
case '~':
|
||||
@@ -1940,28 +1934,37 @@
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
//Specify M17 encoder User Data (CAN, DST, SRC values)
|
||||
//NOTE: Disabled QPSK settings by borrowing these switches (nobody probalby used them anyways)
|
||||
case 'M':
|
||||
strncpy(state.m17dat, optarg, 49);
|
||||
state.m17dat[49] = '\0';
|
||||
break;
|
||||
|
||||
// //Specify M17 encoder SMS Message (truncates at 772)
|
||||
// case 'S':
|
||||
// strncpy(state.m17sms, optarg, 772);
|
||||
// state.m17sms[772] = '\0';
|
||||
// state.m17_str_dt = 3; //flip this so that STR encoder knows to use 1600 voice + data
|
||||
// break;
|
||||
|
||||
//specify TG Hold value
|
||||
case 'I':
|
||||
sscanf (optarg, "%d", &state.tg_hold);
|
||||
fprintf (stderr, "TG Hold set to %d \n", state.tg_hold);
|
||||
break;
|
||||
|
||||
//raw audio monitoring
|
||||
//experimental audio monitoring
|
||||
case '8':
|
||||
opts.monitor_input_audio = 1;
|
||||
fprintf (stderr,"Raw Analog Source Monitoring Enabled.\n");
|
||||
fprintf (stderr,"Experimental Raw Analog Source Monitoring Enabled (Pulse Audio Only!)\n");
|
||||
break;
|
||||
|
||||
//rc4 enforcement on DMR (due to missing the PI header)
|
||||
case '0':
|
||||
state.forced_alg_id = 0x21;
|
||||
state.M = 0x21;
|
||||
fprintf (stderr,"Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR)\n");
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
sscanf (optarg, "%hhX", &state.forced_alg_id);
|
||||
fprintf (stderr,"Force DMR ALG ID 0x%02X over Missing PI header/LE Encryption Identifiers (DMR)\n", state.forced_alg_id);
|
||||
break;
|
||||
|
||||
//load single rc4/des key
|
||||
case '1':
|
||||
@@ -1984,24 +1987,12 @@
|
||||
case '!':
|
||||
tyt_ap_pc4_keystream_creation(&state, optarg);
|
||||
break;
|
||||
|
||||
//get user Retevis AP Key and Force Its application
|
||||
case '@':
|
||||
retevis_rc2_keystream_creation(&state, optarg);
|
||||
break;
|
||||
|
||||
//get user TYT EP Key and Force Its application
|
||||
case '5':
|
||||
tyt_ep_aes_keystream_creation(&state, optarg);
|
||||
break;
|
||||
|
||||
//get user CSI EE Key and Force Its application
|
||||
case '^':
|
||||
parse_raw_user_string(optarg, state.static_ks_bits[0]);
|
||||
fprintf (stderr,"DMR CS Extended 72-bit Key with Forced Application\n");
|
||||
state.csi_ee = 1;
|
||||
break;
|
||||
|
||||
//get user Kenwood DMR Scrambler Key and Force Its application
|
||||
case '9':
|
||||
ken_dmr_scrambler_keystream_creation(&state, optarg);
|
||||
@@ -2012,11 +2003,6 @@
|
||||
anytone_bp_keystream_creation(&state, optarg);
|
||||
break;
|
||||
|
||||
//get user Baofeng AP Key and Force Its application
|
||||
case '+':
|
||||
baofeng_ap_pc5_keystream_creation(&state, optarg);
|
||||
break;
|
||||
|
||||
//Straight KS Generation
|
||||
case 'S':
|
||||
straight_mod_xor_keystream_creation(&state, optarg);
|
||||
@@ -2219,15 +2205,7 @@
|
||||
if (state.R > 0x7FFF) state.R = 0x7FFF;
|
||||
//disable keyloader in case user tries to use this and it at the same time
|
||||
state.keyloader = 0;
|
||||
fprintf (stderr, "NXDN Scrambler Key set to: %05lld; \n", state.R);
|
||||
break;
|
||||
|
||||
case '_': //was *
|
||||
sscanf (optarg, "%hu", &state.nxdn_pn95_seed);
|
||||
if (state.nxdn_pn95_seed > 0x1FF) state.nxdn_pn95_seed = 0x1FF;
|
||||
else if (state.nxdn_pn95_seed == 0) state.nxdn_pn95_seed = 228;
|
||||
fprintf (stderr, "NXDN PN95 Seed Value set to: %03d; \n", state.nxdn_pn95_seed);
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
//new handling for 10/32/64 Char Key
|
||||
@@ -2259,7 +2237,7 @@
|
||||
break;
|
||||
|
||||
case '4':
|
||||
state.forced_alg_id = 1;
|
||||
state.M = 1;
|
||||
fprintf (stderr,"Force Privacy Key over Encryption Identifiers (DMR BP and NXDN Scrambler) \n");
|
||||
break;
|
||||
|
||||
@@ -2775,11 +2753,6 @@
|
||||
sprintf (opts.output_name, "NXDN48");
|
||||
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
|
||||
fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n");
|
||||
#ifdef NXDN_OLD_SYNC
|
||||
//
|
||||
#else
|
||||
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
|
||||
#endif
|
||||
}
|
||||
else if (optarg[0] == 'y')
|
||||
{
|
||||
@@ -2915,7 +2888,6 @@
|
||||
// opts.setmod_bw = 12000; //causing issues
|
||||
sprintf (opts.output_name, "NXDN96");
|
||||
fprintf (stderr,"Decoding only NXDN 9600 baud frames.\n");
|
||||
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
|
||||
}
|
||||
else if (optarg[0] == 'r')
|
||||
{
|
||||
@@ -3002,6 +2974,40 @@
|
||||
//disable RRC filter for now
|
||||
opts.use_cosine_filter = 0;
|
||||
}
|
||||
else if (optarg[0] == 'Z') //Captial Z to Run the M17 STR encoder
|
||||
{
|
||||
opts.m17encoder = 1;
|
||||
opts.pulse_digi_rate_out = 48000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
//filters disabled by default, use ncurses VBN switches
|
||||
opts.use_lpf = 0;
|
||||
opts.use_hpf = 0;
|
||||
opts.use_pbf = 0;
|
||||
opts.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "M17 Encoder");
|
||||
}
|
||||
else if (optarg[0] == 'B') //Captial B to Run the M17 BRT encoder
|
||||
{
|
||||
opts.m17encoderbrt = 1;
|
||||
opts.pulse_digi_rate_out = 48000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
sprintf (opts.output_name, "M17 BERT");
|
||||
}
|
||||
else if (optarg[0] == 'P') //Captial P to Run the M17 PKT encoder
|
||||
{
|
||||
opts.m17encoderpkt = 1;
|
||||
opts.pulse_digi_rate_out = 48000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
sprintf (opts.output_name, "M17 Packet");
|
||||
}
|
||||
else if (optarg[0] == 'U') //Captial U to Run the M17 UDP IPF decoder
|
||||
{
|
||||
opts.m17decoderip = 1;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
sprintf (opts.output_name, "M17 IP Frame");
|
||||
fprintf (stderr, "Decoding M17 UDP/IP Frames.\n");
|
||||
}
|
||||
break;
|
||||
//don't mess with the modulations unless you really need to
|
||||
case 'm':
|
||||
@@ -3101,14 +3107,10 @@
|
||||
opts.inverted_dpmr = 1;
|
||||
fprintf (stderr, "Expecting inverted ICOM dPMR signals.\n");
|
||||
}
|
||||
else if (optarg[0] == 'n')
|
||||
else if (optarg[0] == 'z')
|
||||
{
|
||||
opts.inverted_nxdn = 1;
|
||||
#ifdef NXDN_OLD_SYNC
|
||||
//
|
||||
#else
|
||||
fprintf (stderr, "Expecting inverted NXDN/DCR/IDAS signals.\n");
|
||||
#endif
|
||||
opts.inverted_m17 = 1;
|
||||
fprintf (stderr, "Expecting inverted M17 signals.\n");
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3121,6 +3123,7 @@
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "MBE Playback");
|
||||
state.optind = optind;
|
||||
break;
|
||||
case 'l':
|
||||
opts.use_cosine_filter = 0;
|
||||
@@ -3130,16 +3133,54 @@
|
||||
exit (0);
|
||||
}
|
||||
}
|
||||
|
||||
// Set optind after getopt completes so -r works regardless of argument order
|
||||
if (opts.playfiles == 1)
|
||||
state.optind = optind;
|
||||
|
||||
if (opts.resume > 0)
|
||||
{
|
||||
openSerial (&opts, &state);
|
||||
}
|
||||
|
||||
if((strncmp(opts.audio_in_dev, "m17udp", 6) == 0)) //M17 UDP Socket Input
|
||||
{
|
||||
fprintf (stderr, "M17 UDP IP Frame Input: ");
|
||||
char * curr;
|
||||
|
||||
curr = strtok(opts.audio_in_dev, ":"); //should be 'm17'
|
||||
if (curr != NULL) ; //continue
|
||||
else goto M17ENDIN; //end early with preset values
|
||||
|
||||
curr = strtok(NULL, ":"); //host address
|
||||
if (curr != NULL) strncpy (opts.m17_hostname, curr, 1023);
|
||||
|
||||
curr = strtok(NULL, ":"); //host port
|
||||
if (curr != NULL) opts.m17_portno = atoi (curr);
|
||||
|
||||
M17ENDIN:
|
||||
fprintf (stderr, "%s:", opts.m17_hostname);
|
||||
fprintf (stderr, "%d \n", opts.m17_portno);
|
||||
}
|
||||
|
||||
if((strncmp(opts.audio_out_dev, "m17udp", 6) == 0)) //M17 UDP Socket Output
|
||||
{
|
||||
fprintf (stderr, "M17 UDP IP Frame Output: ");
|
||||
char * curr;
|
||||
|
||||
curr = strtok(opts.audio_out_dev, ":"); //should be 'm17'
|
||||
if (curr != NULL) ; //continue
|
||||
else goto M17ENDOUT; //end early with preset values
|
||||
|
||||
curr = strtok(NULL, ":"); //host address
|
||||
if (curr != NULL) strncpy (opts.m17_hostname, curr, 1023);
|
||||
|
||||
curr = strtok(NULL, ":"); //host port
|
||||
if (curr != NULL) opts.m17_portno = atoi (curr);
|
||||
|
||||
M17ENDOUT:
|
||||
fprintf (stderr, "%s:", opts.m17_hostname);
|
||||
fprintf (stderr, "%d \n", opts.m17_portno);
|
||||
opts.m17_use_ip = 1; //tell the encoder to open the socket
|
||||
opts.audio_out_type = 9; //set to null device
|
||||
}
|
||||
|
||||
if((strncmp(opts.audio_in_dev, "tcp", 3) == 0)) //tcp socket input from SDR++ and others
|
||||
{
|
||||
fprintf (stderr, "TCP Direct Link: ");
|
||||
@@ -3428,23 +3469,18 @@
|
||||
//to a variable config, it cannot be the input as well
|
||||
|
||||
|
||||
#ifndef __APPLE__
|
||||
if((strncmp(opts.audio_in_dev, "/dev/audio", 10) == 0))
|
||||
{
|
||||
sprintf (opts.audio_in_dev, "%s", "/dev/dsp");
|
||||
fprintf (stderr, "Switching to /dev/dsp.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
|
||||
if((strncmp(opts.audio_in_dev, "pa", 2) == 0))
|
||||
{
|
||||
sprintf (opts.audio_in_dev, "%s", "/dev/dsp");
|
||||
fprintf (stderr, "Switching to /dev/dsp.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
|
||||
speed = 48000; //hardset to 48000
|
||||
if((strncmp(opts.audio_in_dev, "/dev/dsp", 8) == 0))
|
||||
{
|
||||
@@ -3478,31 +3514,24 @@
|
||||
|
||||
opts.audio_in_type = 5; //5 will become OSS input type
|
||||
}
|
||||
#endif
|
||||
|
||||
//check for OSS output
|
||||
#ifndef __APPLE__
|
||||
if((strncmp(opts.audio_out_dev, "/dev/audio", 10) == 0))
|
||||
{
|
||||
sprintf (opts.audio_out_dev, "%s", "/dev/dsp");
|
||||
fprintf (stderr, "Switching to /dev/dsp.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
if((strncmp(opts.audio_out_dev, "pa", 2) == 0))
|
||||
{
|
||||
sprintf (opts.audio_out_dev, "%s", "/dev/dsp");
|
||||
fprintf (stderr, "Switching to /dev/dsp.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
//this will only open OSS output if its listed as a type
|
||||
//changed to this so I could call it freely inside of ncurses terminal
|
||||
#ifndef __APPLE__
|
||||
if (opts.playfiles == 0)
|
||||
openOSSOutput(&opts);
|
||||
#endif
|
||||
|
||||
if (opts.playfiles == 1)
|
||||
{
|
||||
@@ -3514,10 +3543,8 @@
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
if (opts.audio_out_type == 0)
|
||||
openPulseOutput(&opts);
|
||||
#ifndef __APPLE__
|
||||
else if((strncmp(opts.audio_out_dev, "/dev/dsp", 8) == 0))
|
||||
openOSSOutput(&opts); //open after split == 1 so it will open and playback at the proper speed
|
||||
#endif
|
||||
}
|
||||
|
||||
//this particular if-elseif-else could be rewritten to be a lot neater and simpler
|
||||
@@ -3551,12 +3578,134 @@
|
||||
signal(SIGINT, handler);
|
||||
signal(SIGTERM, handler);
|
||||
|
||||
//read in any user supplied M17 CAN and/or CSD data
|
||||
if((strncmp(state.m17dat, "M17", 3) == 0))
|
||||
{
|
||||
//read in values
|
||||
//string in format of M17:can:src_csd:dst_csd:input_rate
|
||||
|
||||
//check and capatalize any letters in the CSD
|
||||
for (int i = 0; state.m17dat[i]!='\0'; i++)
|
||||
{
|
||||
if(state.m17dat[i] >= 'a' && state.m17dat[i] <= 'z')
|
||||
state.m17dat[i] = state.m17dat[i] -32;
|
||||
}
|
||||
|
||||
fprintf (stderr, "M17 User Data: ");
|
||||
char * curr;
|
||||
|
||||
// if((strncmp(state.m17dat, "M17", 3) == 0))
|
||||
// goto M17END;
|
||||
|
||||
curr = strtok(state.m17dat, ":"); //should be 'M17'
|
||||
if (curr != NULL) ; //continue
|
||||
else goto M17END; //end early with preset values
|
||||
|
||||
curr = strtok(NULL, ":"); //m17 channel access number
|
||||
if (curr != NULL)
|
||||
state.m17_can_en = atoi(curr);
|
||||
|
||||
curr = strtok(NULL, ":"); //m17 src address
|
||||
if (curr != NULL)
|
||||
{
|
||||
strncpy (state.str50c, curr, 9); //only read first 9
|
||||
state.str50c[9] = '\0';
|
||||
}
|
||||
|
||||
curr = strtok(NULL, ":"); //m17 dst address
|
||||
if (curr != NULL)
|
||||
{
|
||||
strncpy (state.str50b, curr, 9); //only read first 9
|
||||
state.str50b[9] = '\0';
|
||||
}
|
||||
|
||||
curr = strtok(NULL, ":"); //m17 input audio rate
|
||||
if (curr != NULL)
|
||||
state.m17_rate = atoi(curr);
|
||||
|
||||
curr = strtok(NULL, ":"); //m17 vox enable
|
||||
if (curr != NULL)
|
||||
state.m17_vox = atoi(curr);
|
||||
|
||||
// curr = strtok(NULL, ":"); //moved to in and out methods
|
||||
// if (curr != NULL)
|
||||
// opts.m17_use_ip = atoi(curr);
|
||||
|
||||
M17END: ; //do nothing
|
||||
|
||||
//check to make sure can value is no greater than 15 (4 bit value)
|
||||
if (state.m17_can_en > 15)
|
||||
state.m17_can_en = 15;
|
||||
|
||||
//if vox is greater than 1, assume user meant 'yes' and set to one
|
||||
if (state.m17_vox > 1)
|
||||
state.m17_vox = 1;
|
||||
|
||||
//debug print m17dat string
|
||||
// fprintf (stderr, " %s;", state.m17dat);
|
||||
|
||||
fprintf (stderr, " M17:%d:%s:%s:%d;", state.m17_can_en, state.str50c, state.str50b, state.m17_rate);
|
||||
if (state.m17_vox == 1) fprintf (stderr, "VOX;");
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
if (opts.playfiles == 1)
|
||||
{
|
||||
|
||||
playMbeFiles (&opts, &state, argc, argv);
|
||||
}
|
||||
|
||||
else if (opts.m17encoder == 1)
|
||||
{
|
||||
//disable RRC filter for now
|
||||
opts.use_cosine_filter = 0;
|
||||
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
|
||||
//open any inputs, if not alread opened, OSS input and output already handled
|
||||
if (opts.audio_in_type == 0) openPulseInput(&opts);
|
||||
|
||||
#ifdef USE_RTLSDR
|
||||
else if(opts.audio_in_type == 3)
|
||||
{
|
||||
open_rtlsdr_stream(&opts);
|
||||
opts.rtl_started = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
//open any outputs, if not already opened
|
||||
if (opts.audio_out_type == 0) openPulseOutput(&opts);
|
||||
//All input and output now opened and handled correctly, so let's not break things by tweaking
|
||||
encodeM17STR(&opts, &state);
|
||||
}
|
||||
|
||||
else if (opts.m17encoderbrt == 1)
|
||||
{
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
//open any outputs, if not already opened
|
||||
if (opts.audio_out_type == 0) openPulseOutput(&opts);
|
||||
encodeM17BRT(&opts, &state);
|
||||
}
|
||||
|
||||
else if (opts.m17encoderpkt == 1)
|
||||
{
|
||||
//disable RRC filter for now
|
||||
opts.use_cosine_filter = 0;
|
||||
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
//open any outputs, if not already opened
|
||||
if (opts.audio_out_type == 0) openPulseOutput(&opts);
|
||||
encodeM17PKT(&opts, &state);
|
||||
}
|
||||
|
||||
else if (opts.m17decoderip == 1)
|
||||
{
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
//open any outputs, if not already opened
|
||||
if (opts.audio_out_type == 0) openPulseOutput(&opts);
|
||||
processM17IPF(&opts, &state);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
@@ -3568,5 +3717,5 @@
|
||||
return (0);
|
||||
}
|
||||
|
||||
//Tag: 2026
|
||||
//Tag: 2025
|
||||
|
||||
|
||||
380
src/dsd_mbe.c
380
src/dsd_mbe.c
@@ -18,8 +18,6 @@
|
||||
#include "dsd.h"
|
||||
#include "bp.h"
|
||||
#include "pc4.h"
|
||||
#include "rc2.h"
|
||||
#include "pc5.h"
|
||||
|
||||
//NOTE: This set of functions will be reorganized and simplified (hopefully) or at least
|
||||
//a more logical flow will be established to jive with the new audio handling
|
||||
@@ -93,7 +91,7 @@ void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
|
||||
//static wav file only, handled by playSynthesizedVoiceMS
|
||||
//NOTE: if using -o null, playSynthesizedVoiceMS will not write to static wav file
|
||||
//Per call will work, but will end up with a single file with no meta info
|
||||
if (opts->wav_out_f != NULL && (opts->dmr_stereo_wav == 1 || opts->static_wav_file == 1))
|
||||
if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1)
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
@@ -141,7 +139,7 @@ void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
|
||||
//static wav file only, handled by playSynthesizedVoiceMS
|
||||
//NOTE: if using -o null, playSynthesizedVoiceMS will not write to static wav file
|
||||
//Per call will work, but will end up with a single file with no meta info
|
||||
if (opts->wav_out_f != NULL && (opts->dmr_stereo_wav == 1 || opts->static_wav_file == 1))
|
||||
if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1)
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
@@ -175,30 +173,6 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
unsigned long long int k;
|
||||
int x;
|
||||
|
||||
//keystream and silence conditional items
|
||||
uint64_t silence = 0xF801A99F8CE080; //AMBE+2 default silence vector expressed as a 56-bit hex value.
|
||||
char ambe_silence[49];
|
||||
for (i = 0; i < 49; i++)
|
||||
ambe_silence[i] = (silence >> (55-i)) & 1;
|
||||
//zeroed ambe_d can look like 00000000000580(w/ DMRA IV), 000D2C00000000,
|
||||
//or 00000000000000 look at +24 position for 20 bits (fits all these scenarios)
|
||||
char zeroes[49]; memset(zeroes, 0, sizeof(zeroes));
|
||||
size_t zeroes_threshold = 20;
|
||||
|
||||
if (state->forced_alg_id > 1 && state->forced_alg_id != 0x16) //1 and 0x16 is saved for BP stuff, so anything higher than that (Kirisun, requires svc opts set as well)
|
||||
{
|
||||
if (state->currentslot == 0 && state->dmr_so & 0x40)
|
||||
{
|
||||
state->payload_algid = state->forced_alg_id;
|
||||
state->payload_keyid = 0xFF;
|
||||
}
|
||||
if (state->currentslot == 1 && state->dmr_soR & 0x40)
|
||||
{
|
||||
state->payload_algidR = state->forced_alg_id;
|
||||
state->payload_keyidR = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
//these conditions should ensure no clashing with the BP/HBP/Scrambler key loading machanisms already coded in
|
||||
if (state->currentslot == 0 && state->payload_algid != 0 && state->payload_algid != 0x80 && state->keyloader == 1)
|
||||
keyring (opts, state);
|
||||
@@ -474,7 +448,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
state->errs2 += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
if ( (state->nxdn_cipher_type == 0x01 && state->R != 0) ||
|
||||
(state->forced_alg_id == 1 && state->R > 0) )
|
||||
(state->M == 1 && state->R > 0) )
|
||||
{
|
||||
|
||||
if (state->payload_miN == 0)
|
||||
@@ -535,7 +509,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
state->bit_counterL = 0;
|
||||
|
||||
//unpack the octets into a bit-wise keystream
|
||||
unpack_byte_array_into_bit_array(state->ks_octetL+16, state->ks_bitstreamL, 14*16);
|
||||
unpack_byte_array_into_bit_array(state->ks_octetL+8, state->ks_bitstreamL, 15*8);
|
||||
|
||||
//reset flag to 0
|
||||
state->nxdn_new_iv = 0;
|
||||
@@ -546,7 +520,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
if (state->bit_counterL > (1568-49))
|
||||
state->bit_counterL = (1568-49);
|
||||
|
||||
//Keystream Application (no +7 discard like DMRA)
|
||||
//Keystream creation is currently inside of the NXDN_decode_VCALL_IV function
|
||||
for (i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= state->ks_bitstreamL[state->bit_counterL++];
|
||||
|
||||
@@ -582,7 +556,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
//EXPERIMENTAL!!
|
||||
//load basic privacy key number from array by the tg value (if not forced)
|
||||
//currently only Moto BP and Hytera 10 Char BP
|
||||
if (state->forced_alg_id == 0 && state->payload_algid == 0)
|
||||
if (state->M == 0 && state->payload_algid == 0)
|
||||
{
|
||||
//see if we need to hash a value larger than 16-bits
|
||||
hash = state->lasttg & 0xFFFFFF;
|
||||
@@ -608,7 +582,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||
(state->K > 0 && state->forced_alg_id == 1) )
|
||||
(state->K > 0 && state->M == 1) )
|
||||
{
|
||||
k = BPK[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
@@ -620,7 +594,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||
(state->K1 > 0 && state->forced_alg_id == 1) )
|
||||
(state->K1 > 0 && state->M == 1) )
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
@@ -674,15 +648,10 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
pos = state->DMRvcL * 49;
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
pos += 49;
|
||||
else
|
||||
for(i = 0; i < 49; i++)
|
||||
{
|
||||
for(i = 0; i < 49; i++)
|
||||
{
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
state->DMRvcL++;
|
||||
}
|
||||
@@ -743,8 +712,6 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
(state->currentslot == 0 && state->payload_algid == 0x25 && state->aes_key_loaded[0] == 1 ) || //DMR AES256
|
||||
(state->currentslot == 0 && state->payload_algid == 0x89 && state->aes_key_loaded[0] == 1 ) || //P25 AES128
|
||||
(state->currentslot == 0 && state->payload_algid == 0x84 && state->aes_key_loaded[0] == 1 ) || //P25 AES256
|
||||
(state->currentslot == 0 && state->payload_algid == 0x36 && state->aes_key_loaded[0] == 1 ) || //KIRI ADV
|
||||
(state->currentslot == 0 && state->payload_algid == 0x37 && state->aes_key_loaded[0] == 1 ) || //KIRI UNI
|
||||
(state->currentslot == 0 && state->payload_algid == 0x02 && state->R != 0 ) ) //HYT ENHANCED
|
||||
{
|
||||
|
||||
@@ -779,16 +746,6 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
n = 0;
|
||||
hytera_enhanced_rc4_setup(opts, state, state->R, state->payload_mi);
|
||||
}
|
||||
if (state->payload_algid == 0x36)
|
||||
{
|
||||
n = 0;
|
||||
kirisun_adv_keystream_creation(state);
|
||||
}
|
||||
if (state->payload_algid == 0x37)
|
||||
{
|
||||
n = 0;
|
||||
kirisun_uni_keystream_creation(state);
|
||||
}
|
||||
|
||||
//Load Keystream Octet Bytes directly into keystream array //TODO: Convert to unpack function
|
||||
for (i = 0; i < 9 * 16; i++) //9 rounds at 16 octets
|
||||
@@ -802,17 +759,18 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
}
|
||||
|
||||
//skip keystream if silence or zeroes (some CCR), else apply keystream directly and increment counter
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->bit_counterL += 49;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->bit_counterL += 49;
|
||||
else
|
||||
//now we do the bit by bit xor depending on the frame and position of the state bit counter
|
||||
//run 6 instead of 7 so we can just do bit 49 outside of loop to keep extra bits overloading the array
|
||||
z = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
for (i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= state->ks_bitstreamL[state->bit_counterL++];
|
||||
for (j = 0; j < 8; j++)
|
||||
ambe_d[z++] ^= state->ks_bitstreamL[state->bit_counterL++];
|
||||
}
|
||||
|
||||
//last bit
|
||||
ambe_d[48] ^= state->ks_bitstreamL[state->bit_counterL++];
|
||||
|
||||
//skip the next 7 bits of the array (if not Hytera Enhanced)
|
||||
if(state->payload_algid != 0x02)
|
||||
state->bit_counterL += 7;
|
||||
@@ -850,24 +808,15 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
//that may occur on some systems that preempt VC6 voice for a RC opportuninity (TXI)
|
||||
//this occurs because we are supposed to either have a a 'repeat' frame, or 'silent' frame play
|
||||
//due to the error, but the keystream application makes it random 'pfft pop' sound instead
|
||||
if (state->errs < 3)
|
||||
rc4_voice_decrypt(state->dropL, 9, 7, rckey, cipher, plain);
|
||||
else memcpy (plain, cipher, sizeof(plain));
|
||||
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->dropL += 7;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->dropL += 7;
|
||||
else
|
||||
{
|
||||
if (state->errs < 3)
|
||||
rc4_voice_decrypt(state->dropL, 9, 7, rckey, cipher, plain);
|
||||
else memcpy (plain, cipher, sizeof(plain));
|
||||
state->dropL += 7;
|
||||
|
||||
state->dropL += 7;
|
||||
|
||||
//unpack deciphered plain array back into ambe_d bit array
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
unpack_ambe(plain, ambe_d);
|
||||
|
||||
}
|
||||
//unpack deciphered plain array back into ambe_d bit array
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
unpack_ambe(plain, ambe_d);
|
||||
|
||||
}
|
||||
|
||||
@@ -908,115 +857,49 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
unpack_ambe(plain, ambe_d);
|
||||
|
||||
}
|
||||
|
||||
//DMR Retevis AP, Either Slot (static single key'd enforced KS)
|
||||
if (state->retevis_ap == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
|
||||
else
|
||||
{
|
||||
uint8_t frame1_cipher[49];
|
||||
|
||||
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
|
||||
|
||||
decrypt_rc2((CryptoContext *)state->rc2_context, frame1_cipher);
|
||||
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
for (int i = 0; i < 49; i++) ambe_d[i] = frame1_cipher[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//DMR TYT AP, Either Slot (static single key'd enforced KS)
|
||||
if (state->tyt_ap == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
|
||||
else
|
||||
{
|
||||
short frame1_cipher[49];
|
||||
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
|
||||
decrypt_frame_49(frame1_cipher);
|
||||
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
for (int i = 0; i < 49; i++) ambe_d[i] = ctx.bits[i];
|
||||
}
|
||||
|
||||
short frame1_cipher[49];
|
||||
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
|
||||
decrypt_frame_49(frame1_cipher);
|
||||
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
for (int i = 0; i < 49; i++) ambe_d[i] = ctx.bits[i];
|
||||
|
||||
}
|
||||
|
||||
//DMR BAOFENG AP, Either Slot (static single key'd enforced KS)
|
||||
if (state->baofeng_ap == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
|
||||
else
|
||||
{
|
||||
short frame1_cipher[49];
|
||||
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
|
||||
|
||||
decrypt_frame_49_pc5(frame1_cipher);
|
||||
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
for (int i = 0; i < 49; i++) ambe_d[i] = ctxpc5.bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
//DMR TYT EP, Either Slot (static single key'd enforced KS)
|
||||
if (state->tyt_ep == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 1);
|
||||
}
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 1);
|
||||
}
|
||||
|
||||
//DMR Kenwood Scrambler, Either Slot (static single key'd enforced KS)
|
||||
//DMR Kenwood Scrambler, Either Slot (static single key'd enforced KS) //should probably break this up, but this is a test for now
|
||||
if (state->ken_sc == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%882] & 1); //Yikes!
|
||||
}
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%882] & 1); //Yikes!
|
||||
}
|
||||
|
||||
//DMR Anytone BP, Either Slot (static single key'd enforced KS)
|
||||
if (state->any_bp == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%16] & 1); //Yikes!
|
||||
}
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%16] & 1); //Yikes!
|
||||
}
|
||||
|
||||
//Generic Straight Static Keystream
|
||||
if (state->straight_ks == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else
|
||||
{
|
||||
//disable enc identifiers, if present
|
||||
state->dmr_so = 0;
|
||||
state->payload_algid = 0;
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
|
||||
}
|
||||
//disable enc identifiers, if present
|
||||
state->dmr_so = 0;
|
||||
state->payload_algid = 0;
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
|
||||
}
|
||||
|
||||
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
||||
@@ -1049,7 +932,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
//EXPERIMENTAL!!
|
||||
//load basic privacy key number from array by the tg value (if not forced)
|
||||
//currently only Moto BP and Hytera 10 Char BP
|
||||
if (state->forced_alg_id == 0 && state->payload_algidR == 0)
|
||||
if (state->M == 0 && state->payload_algidR == 0)
|
||||
{
|
||||
//see if we need to hash a value larger than 16-bits
|
||||
hash = state->lasttgR & 0xFFFFFF;
|
||||
@@ -1075,7 +958,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
||||
(state->K > 0 && state->forced_alg_id == 1) )
|
||||
(state->K > 0 && state->M == 1) )
|
||||
{
|
||||
k = BPK[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
@@ -1087,7 +970,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
|
||||
(state->K1 > 0 && state->forced_alg_id == 1))
|
||||
(state->K1 > 0 && state->M == 1))
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
@@ -1141,15 +1024,10 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
pos = state->DMRvcR * 49;
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
pos += 49;
|
||||
else
|
||||
for(i = 0; i < 49; i++)
|
||||
{
|
||||
for(i = 0; i < 49; i++)
|
||||
{
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
state->DMRvcR++;
|
||||
}
|
||||
@@ -1210,8 +1088,6 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
(state->currentslot == 1 && state->payload_algidR == 0x25 && state->aes_key_loaded[1] == 1 ) || //DMR AES256
|
||||
(state->currentslot == 1 && state->payload_algidR == 0x89 && state->aes_key_loaded[1] == 1 ) || //P25 AES128
|
||||
(state->currentslot == 1 && state->payload_algidR == 0x84 && state->aes_key_loaded[1] == 1 ) || //P25 AES256
|
||||
(state->currentslot == 1 && state->payload_algidR == 0x36 && state->aes_key_loaded[1] == 1 ) || //KIRI ADV
|
||||
(state->currentslot == 1 && state->payload_algidR == 0x37 && state->aes_key_loaded[1] == 1 ) || //KIRI UNI
|
||||
(state->currentslot == 1 && state->payload_algidR == 0x02 && state->RR != 0 ) ) //HYT ENHANCED
|
||||
{
|
||||
|
||||
@@ -1246,16 +1122,6 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
n = 0;
|
||||
hytera_enhanced_rc4_setup(opts, state, state->RR, state->payload_miR);
|
||||
}
|
||||
if (state->payload_algidR == 0x36)
|
||||
{
|
||||
n = 0;
|
||||
kirisun_adv_keystream_creation(state);
|
||||
}
|
||||
if (state->payload_algidR == 0x37)
|
||||
{
|
||||
n = 0;
|
||||
kirisun_uni_keystream_creation(state);
|
||||
}
|
||||
|
||||
//Load Keystream Octet Bytes directly into keystream array
|
||||
for (i = 0; i < 9 * 16; i++) //9 rounds at 16 octets //TODO: Convert to unpack function
|
||||
@@ -1269,17 +1135,18 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
}
|
||||
|
||||
//skip keystream if silence or zeroes (some CCR), else apply keystream directly and increment counter
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->bit_counterR += 49;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->bit_counterR += 49;
|
||||
else
|
||||
//now we do the bit by bit xor depending on the frame and position of the state bit counter
|
||||
//run 6 instead of 7 so we can just do bit 49 outside of loop to keep extra bits overloading the array
|
||||
z = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
for (i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= state->ks_bitstreamR[state->bit_counterR++];
|
||||
for (j = 0; j < 8; j++)
|
||||
ambe_d[z++] ^= state->ks_bitstreamR[state->bit_counterR++];
|
||||
}
|
||||
|
||||
//last bit
|
||||
ambe_d[48] ^= state->ks_bitstreamR[state->bit_counterR++];
|
||||
|
||||
//skip the next 7 bits of the array (if not Hytera Enhanced)
|
||||
if(state->payload_algidR != 0x02)
|
||||
state->bit_counterR += 7;
|
||||
@@ -1317,22 +1184,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
//that may occur on some systems that preempt VC6 voice for a RC opportuninity (TXI)
|
||||
//this occurs because we are supposed to either have a a 'repeat' frame, or 'silent' frame play
|
||||
//due to the error, but the keystream application makes it random 'pfft pop' sound instead
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->dropR += 7;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->dropR += 7;
|
||||
else
|
||||
{
|
||||
if (state->errsR < 3)
|
||||
rc4_voice_decrypt(state->dropR, 9, 7, rckey, cipher, plain);
|
||||
else memcpy (plain, cipher, sizeof(plain));
|
||||
state->dropR += 7;
|
||||
if (state->errsR < 3)
|
||||
rc4_voice_decrypt(state->dropR, 9, 7, rckey, cipher, plain);
|
||||
else memcpy (plain, cipher, sizeof(plain));
|
||||
state->dropR += 7;
|
||||
|
||||
//unpack deciphered plain array back into ambe_d bit array
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
unpack_ambe(plain, ambe_d);
|
||||
|
||||
}
|
||||
//unpack deciphered plain array back into ambe_d bit array
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
unpack_ambe(plain, ambe_d);
|
||||
|
||||
}
|
||||
|
||||
@@ -1374,115 +1233,48 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
|
||||
}
|
||||
|
||||
//DMR Retevis AP, Either Slot (static single key'd enforced KS)
|
||||
if (state->retevis_ap == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
|
||||
else
|
||||
{
|
||||
uint8_t frame1_cipher[49];
|
||||
|
||||
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
|
||||
|
||||
decrypt_rc2((CryptoContext *)state->rc2_context, frame1_cipher);
|
||||
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
for (int i = 0; i < 49; i++) ambe_d[i] = frame1_cipher[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//DMR TYT AP, Either Slot (static single key'd enforced KS)
|
||||
if (state->tyt_ap == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
|
||||
else
|
||||
{
|
||||
short frame1_cipher[49];
|
||||
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
|
||||
decrypt_frame_49(frame1_cipher);
|
||||
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
for (int i = 0; i < 49; i++) ambe_d[i] = ctx.bits[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//DMR BAOFENG AP, Either Slot (static single key'd enforced KS)
|
||||
if (state->baofeng_ap == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
|
||||
else
|
||||
{
|
||||
short frame1_cipher[49];
|
||||
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
|
||||
|
||||
decrypt_frame_49_pc5(frame1_cipher);
|
||||
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
for (int i = 0; i < 49; i++) ambe_d[i] = ctxpc5.bits[i];
|
||||
}
|
||||
|
||||
short frame1_cipher[49];
|
||||
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
|
||||
decrypt_frame_49(frame1_cipher);
|
||||
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
for (int i = 0; i < 49; i++) ambe_d[i] = ctx.bits[i];
|
||||
|
||||
}
|
||||
|
||||
//DMR TYT EP, Either Slot (static single key'd enforced KS)
|
||||
if (state->tyt_ep == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 1);
|
||||
}
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 1);
|
||||
}
|
||||
|
||||
//DMR Kenwood Scrambler, Either Slot (static single key'd enforced KS) //should probably break this up, but this is a test for now
|
||||
if (state->ken_sc == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%882] & 1); //Yikes!
|
||||
}
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%882] & 1); //Yikes!
|
||||
}
|
||||
|
||||
//DMR Anytone BP, Either Slot (static single key'd enforced KS)
|
||||
if (state->any_bp == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%16] & 1); //Yikes!
|
||||
}
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%16] & 1); //Yikes!
|
||||
}
|
||||
|
||||
//Generic Straight Static Keystream
|
||||
if (state->straight_ks == 1)
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 49;
|
||||
else
|
||||
{
|
||||
//disable enc identifiers, if present
|
||||
state->dmr_soR = 0;
|
||||
state->payload_algidR = 0;
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
|
||||
}
|
||||
//disable enc identifiers, if present
|
||||
state->dmr_soR = 0;
|
||||
state->payload_algidR = 0;
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
|
||||
}
|
||||
|
||||
mbe_processAmbe2450Dataf (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR,
|
||||
|
||||
343
src/dsd_misc.c
343
src/dsd_misc.c
@@ -8,6 +8,322 @@
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
static const int PARITY[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};
|
||||
|
||||
// trellis_1_2 encode: source is in bits, result in bits
|
||||
void trellis_encode(uint8_t result[], const uint8_t source[], int result_len, int reg)
|
||||
{
|
||||
for (int i=0; i<result_len; i+=2) {
|
||||
reg = (reg << 1) | source[i>>1];
|
||||
result[i] = PARITY[reg & 0x19];
|
||||
result[i+1] = PARITY[reg & 0x17];
|
||||
}
|
||||
}
|
||||
|
||||
// simplified trellis 2:1 decode; source and result in bits
|
||||
// assumes that encoding was done with NTEST trailing zero bits
|
||||
// result_len should be set to the actual number of data bits
|
||||
// in the original unencoded message (excl. these trailing bits)
|
||||
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len)
|
||||
{
|
||||
int reg = 0;
|
||||
int min_d;
|
||||
int min_bt;
|
||||
static const int NTEST = 4;
|
||||
static const int NTESTC = 1 << NTEST;
|
||||
uint8_t bt[NTEST];
|
||||
uint8_t tt[NTEST*2];
|
||||
int dstats[4];
|
||||
int sum;
|
||||
for (int p=0; p < 4; p++)
|
||||
dstats[p] = 0;
|
||||
for (int p=0; p < result_len; p++) {
|
||||
for (int i=0; i<NTESTC; i++) {
|
||||
bt[0] = (i&8)>>3;
|
||||
bt[1] = (i&4)>>2;
|
||||
bt[2] = (i&2)>>1;
|
||||
bt[3] = (i&1);
|
||||
trellis_encode(tt, bt, NTEST*2, reg);
|
||||
sum=0;
|
||||
for (int j=0; j<NTEST*2; j++) {
|
||||
sum += tt[j] ^ source[p*2+j];
|
||||
}
|
||||
if (i == 0 || sum < min_d) {
|
||||
min_d = sum;
|
||||
min_bt = bt[0];
|
||||
}
|
||||
}
|
||||
result[p] = min_bt;
|
||||
reg = (reg << 1) | min_bt;
|
||||
dstats[(min_d > 3) ? 3 : min_d] += 1;
|
||||
}
|
||||
|
||||
//debug output
|
||||
// fprintf (stderr, "\n stats\t%d %d %d %d\n", dstats[0], dstats[1], dstats[2], dstats[3]);
|
||||
}
|
||||
|
||||
//Original Copyright/License
|
||||
|
||||
/* -*- c++ -*- */
|
||||
/*
|
||||
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||
*
|
||||
* This file is part of OP25
|
||||
*
|
||||
* This 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 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This software 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 software; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
//Ripped from libM17
|
||||
#define K 5 //constraint length
|
||||
#define NUM_STATES (1 << (K - 1)) //number of states
|
||||
|
||||
static uint32_t prevMetrics[NUM_STATES];
|
||||
static uint32_t currMetrics[NUM_STATES];
|
||||
static uint32_t prevMetricsData[NUM_STATES];
|
||||
static uint32_t currMetricsData[NUM_STATES];
|
||||
static uint16_t viterbi_history[244];
|
||||
|
||||
/**
|
||||
* @brief Decode unpunctured convolutionally encoded data.
|
||||
*
|
||||
* @param out Destination array where decoded data is written.
|
||||
* @param in Input data.
|
||||
* @param len Input length in bits.
|
||||
* @return Number of bit errors corrected.
|
||||
*/
|
||||
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
|
||||
{
|
||||
if(len > 244*2)
|
||||
fprintf(stderr, "Input size exceeds max history\n");
|
||||
|
||||
viterbi_reset();
|
||||
|
||||
size_t pos = 0;
|
||||
for(size_t i = 0; i < len; i += 2)
|
||||
{
|
||||
uint16_t s0 = in[i];
|
||||
uint16_t s1 = in[i + 1];
|
||||
|
||||
viterbi_decode_bit(s0, s1, pos);
|
||||
pos++;
|
||||
}
|
||||
uint32_t err = viterbi_chainback(out, pos, len/2);
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "\n vcb: \n");
|
||||
// for (size_t i = 0; i < len; i++)
|
||||
// fprintf (stderr, "%02X", out[i]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode punctured convolutionally encoded data.
|
||||
*
|
||||
* @param out Destination array where decoded data is written.
|
||||
* @param in Input data.
|
||||
* @param punct Puncturing matrix.
|
||||
* @param in_len Input data length.
|
||||
* @param p_len Puncturing matrix length (entries).
|
||||
* @return Number of bit errors corrected.
|
||||
*/
|
||||
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
|
||||
{
|
||||
if(in_len > 244*2)
|
||||
fprintf(stderr, "Input size exceeds max history\n");
|
||||
|
||||
uint16_t umsg[244*2]; //unpunctured message
|
||||
uint8_t p=0; //puncturer matrix entry
|
||||
uint16_t u=0; //bits count - unpunctured message
|
||||
uint16_t i=0; //bits read from the input message
|
||||
|
||||
while(i<in_len)
|
||||
{
|
||||
if(punct[p])
|
||||
{
|
||||
umsg[u]=in[i];
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
umsg[u]=0x7FFF;
|
||||
}
|
||||
|
||||
u++;
|
||||
p++;
|
||||
p%=p_len;
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " p: %d, u: %d; p_len: %d; len: %d;", p, u, p_len, (u-in_len)*0x7FFF);
|
||||
|
||||
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode one bit and update trellis.
|
||||
*
|
||||
* @param s0 Cost of the first symbol.
|
||||
* @param s1 Cost of the second symbol.
|
||||
* @param pos Bit position in history.
|
||||
*/
|
||||
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos)
|
||||
{
|
||||
static const uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
|
||||
static const uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0, 0, 0xFFFF, 0xFFFF, 0};
|
||||
|
||||
for(uint8_t i = 0; i < NUM_STATES/2; i++)
|
||||
{
|
||||
uint32_t metric = q_abs_diff(COST_TABLE_0[i], s0)
|
||||
+ q_abs_diff(COST_TABLE_1[i], s1);
|
||||
|
||||
|
||||
uint32_t m0 = prevMetrics[i] + metric;
|
||||
uint32_t m1 = prevMetrics[i + NUM_STATES/2] + (0x1FFFE - metric);
|
||||
|
||||
uint32_t m2 = prevMetrics[i] + (0x1FFFE - metric);
|
||||
uint32_t m3 = prevMetrics[i + NUM_STATES/2] + metric;
|
||||
|
||||
uint8_t i0 = 2 * i;
|
||||
uint8_t i1 = i0 + 1;
|
||||
|
||||
if(m0 >= m1)
|
||||
{
|
||||
viterbi_history[pos]|=(1<<i0);
|
||||
currMetrics[i0] = m1;
|
||||
}
|
||||
else
|
||||
{
|
||||
viterbi_history[pos]&=~(1<<i0);
|
||||
currMetrics[i0] = m0;
|
||||
}
|
||||
|
||||
if(m2 >= m3)
|
||||
{
|
||||
viterbi_history[pos]|=(1<<i1);
|
||||
currMetrics[i1] = m3;
|
||||
}
|
||||
else
|
||||
{
|
||||
viterbi_history[pos]&=~(1<<i1);
|
||||
currMetrics[i1] = m2;
|
||||
}
|
||||
}
|
||||
|
||||
//swap
|
||||
uint32_t tmp[NUM_STATES];
|
||||
for(uint8_t i=0; i<NUM_STATES; i++)
|
||||
{
|
||||
tmp[i]=currMetrics[i];
|
||||
}
|
||||
for(uint8_t i=0; i<NUM_STATES; i++)
|
||||
{
|
||||
currMetrics[i]=prevMetrics[i];
|
||||
prevMetrics[i]=tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief History chainback to obtain final byte array.
|
||||
*
|
||||
* @param out Destination byte array for decoded data.
|
||||
* @param pos Starting position for the chainback.
|
||||
* @param len Length of the output in bits.
|
||||
* @return Minimum Viterbi cost at the end of the decode sequence.
|
||||
*/
|
||||
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
|
||||
{
|
||||
uint8_t state = 0;
|
||||
size_t bitPos = len+4;
|
||||
|
||||
memset(out, 0, (len-1)/8+1);
|
||||
|
||||
while(pos > 0)
|
||||
{
|
||||
bitPos--;
|
||||
pos--;
|
||||
uint16_t bit = viterbi_history[pos]&((1<<(state>>4)));
|
||||
state >>= 1;
|
||||
if(bit)
|
||||
{
|
||||
state |= 0x80;
|
||||
out[bitPos/8]|=1<<(7-(bitPos%8));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cost = prevMetrics[0];
|
||||
|
||||
for(size_t i = 0; i < NUM_STATES; i++)
|
||||
{
|
||||
uint32_t m = prevMetrics[i];
|
||||
if(m < cost) cost = m;
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "\n cb: \n");
|
||||
// for (size_t i = 0; i < len; i++)
|
||||
// fprintf (stderr, "%02X", out[i]);
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the decoder state. No args.
|
||||
*
|
||||
*/
|
||||
void viterbi_reset(void)
|
||||
{
|
||||
memset((uint8_t*)viterbi_history, 0, 2*244);
|
||||
memset((uint8_t*)currMetrics, 0, 4*NUM_STATES);
|
||||
memset((uint8_t*)prevMetrics, 0, 4*NUM_STATES);
|
||||
memset((uint8_t*)currMetricsData, 0, 4*NUM_STATES);
|
||||
memset((uint8_t*)prevMetricsData, 0, 4*NUM_STATES);
|
||||
}
|
||||
|
||||
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
|
||||
{
|
||||
if(v2 > v1) return v2 - v1;
|
||||
return v1 - v2;
|
||||
}
|
||||
|
||||
//original sources
|
||||
//--------------------------------------------------------------------
|
||||
// M17 C library - decode/viterbi.c
|
||||
//
|
||||
// This file contains:
|
||||
// - the Viterbi decoder
|
||||
//
|
||||
// Wojciech Kaczmarski, SP5WWP
|
||||
// M17 Project, 29 December 2023
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// M17 C library - math/math.c
|
||||
//
|
||||
// This file contains:
|
||||
// - absolute difference value
|
||||
// - Euclidean norm (L2) calculation for n-dimensional vectors (float)
|
||||
// - soft-valued arrays to integer conversion (and vice-versa)
|
||||
// - fixed-valued multiplication and division
|
||||
//
|
||||
// Wojciech Kaczmarski, SP5WWP
|
||||
// M17 Project, 29 December 2023
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
|
||||
//audio filter stuff sourced from: https://github.com/NedSimao/FilteringLibrary
|
||||
//no license / information provided in source code
|
||||
#define PI 3.141592653
|
||||
@@ -21,6 +337,7 @@
|
||||
// #define HFC 50
|
||||
// #define LFC 100
|
||||
|
||||
|
||||
void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
|
||||
{
|
||||
|
||||
@@ -34,6 +351,7 @@ void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
|
||||
|
||||
}
|
||||
|
||||
|
||||
float LPFilter_Update(LPFilter *filter, float v_in)
|
||||
{
|
||||
|
||||
@@ -64,6 +382,7 @@ void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS)
|
||||
|
||||
}
|
||||
|
||||
|
||||
float HPFilter_Update(HPFilter *filter, float v_in)
|
||||
{
|
||||
|
||||
@@ -273,3 +592,27 @@ long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as
|
||||
if (rms < 0) rms = 150;
|
||||
return rms;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mean power (RMS^2 proxy) without sqrt, modeled after mean_power() in rtl_sdr_fm.cpp.
|
||||
* Computes a DC-corrected average of squares to avoid costly sqrt operations.
|
||||
*/
|
||||
long int raw_pwr(int16_t *samples, int len, int step)
|
||||
{
|
||||
int64_t p = 0;
|
||||
int64_t t = 0;
|
||||
for (int i = 0; i < len; i += step) {
|
||||
int64_t s = (int64_t)samples[i];
|
||||
t += s;
|
||||
p += s * s;
|
||||
}
|
||||
/* DC-corrected energy ≈ p - (t^2)/len with rounded division */
|
||||
int64_t dc_corr = 0;
|
||||
if (len > 0) {
|
||||
int64_t tt = t * t;
|
||||
dc_corr = (tt + (len / 2)) / len;
|
||||
}
|
||||
int64_t energy = p - dc_corr;
|
||||
if (energy < 0) energy = 0;
|
||||
return (long int)(energy / (len > 0 ? len : 1));
|
||||
}
|
||||
@@ -34,7 +34,10 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
|
||||
|
||||
//keyboard shortcuts - codes same as ascii codes
|
||||
if (c == 10) //Return / Enter key, open menu
|
||||
ncursesMenu (opts, state);
|
||||
{
|
||||
if (opts->m17encoder == 0) //don't allow menu if using M17 encoder
|
||||
ncursesMenu (opts, state);
|
||||
}
|
||||
|
||||
//use k and l keys to test tg hold toggles on slots 1 and slots 2
|
||||
if (c == 107) //'k' key, hold tg on slot 1 for trunking purposes, or toggle clear
|
||||
@@ -211,14 +214,14 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
|
||||
|
||||
if (c == 52) // '4' key, toggle force privacy key over fid and svc (dmr)
|
||||
{
|
||||
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0;
|
||||
else state->forced_alg_id = 1;
|
||||
if (state->M == 1 || state->M == 0x21) state->M = 0;
|
||||
else state->M = 1;
|
||||
}
|
||||
|
||||
if (c == 54) // '6' key, toggle force rc4 key over missing pi header/late entry
|
||||
{
|
||||
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0;
|
||||
else state->forced_alg_id = 0x21;
|
||||
if (state->M == 1 || state->M == 0x21) state->M = 0;
|
||||
else state->M = 0x21;
|
||||
}
|
||||
|
||||
if (c == 105) //'i' key, toggle signal inversion on inverted types
|
||||
@@ -230,7 +233,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
|
||||
opts->inverted_dpmr = 1;
|
||||
opts->inverted_x2tdma = 1;
|
||||
opts->inverted_ysf = 1;
|
||||
opts->inverted_nxdn = 1;
|
||||
opts->inverted_m17 = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -238,7 +241,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
|
||||
opts->inverted_dpmr = 0;
|
||||
opts->inverted_x2tdma = 0;
|
||||
opts->inverted_ysf = 0;
|
||||
opts->inverted_nxdn = 0;
|
||||
opts->inverted_m17 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,8 +382,8 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
|
||||
if (c == 112) //'p' key - stop all per call wav files //TODO: Fix
|
||||
{
|
||||
//TODO: Add Closing of RAW files as well?
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
|
||||
opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling
|
||||
opts->wav_out_fileR[0] = 0;
|
||||
opts->dmr_stereo_wav = 0;
|
||||
@@ -594,7 +597,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
|
||||
else opts->trunk_tune_enc_calls = 1;
|
||||
}
|
||||
|
||||
if (opts->p25_trunk == 1 && c == 84) //'T' key, toggle tune group calls
|
||||
if (opts->p25_trunk == 1 && c == 103) //'g' key, toggle tune group calls
|
||||
{
|
||||
if (opts->trunk_tune_group_calls == 1) opts->trunk_tune_group_calls = 0;
|
||||
else opts->trunk_tune_group_calls = 1;
|
||||
@@ -885,6 +888,16 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
|
||||
else opts->use_hpf_d = 0;
|
||||
}
|
||||
|
||||
if (opts->m17encoder == 1 && c == 92) //'\' key - toggle M17 encoder Encode + TX
|
||||
{
|
||||
if (state->m17encoder_tx == 0) state->m17encoder_tx = 1;
|
||||
else state->m17encoder_tx = 0;
|
||||
|
||||
//flag on the EOT marker to send last frame after toggling encoder to zero
|
||||
if (state->m17encoder_tx == 0) state->m17encoder_eot = 1;
|
||||
|
||||
}
|
||||
|
||||
if(opts->frame_provoice == 1 && c == 65) //'A' Key, toggle ESK mask 0xA0
|
||||
{
|
||||
if (state->esk_mask == 0) state->esk_mask = 0xA0;
|
||||
@@ -918,26 +931,6 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
|
||||
if (c == 123)
|
||||
opts->rtlsdr_ppm_error--;
|
||||
|
||||
//RTL Gain Manual Adjustment
|
||||
if (c == 103)
|
||||
{
|
||||
if (opts->rtl_gain_value > 0)
|
||||
{
|
||||
opts->rtl_gain_value--;
|
||||
opts->rtl_gain_actual = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == 71)
|
||||
{
|
||||
if (opts->rtl_gain_value < 49)
|
||||
{
|
||||
opts->rtl_gain_value++;
|
||||
opts->rtl_gain_actual = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//anything with an entry box will need the inputs and outputs stopped first
|
||||
//so probably just write a function to handle c input, and when c = certain values
|
||||
//needing an entry box, then stop all of those
|
||||
|
||||
@@ -130,13 +130,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
closePulseOutput (opts);
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
//close OSS output
|
||||
if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
|
||||
{
|
||||
close (opts->audio_out_fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (opts->audio_in_type == 0) //close pulse input if it is the specified input method
|
||||
{
|
||||
@@ -419,7 +417,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
entry_win = newwin(8, WIDTH+22, starty+10, startx+10);
|
||||
box (entry_win, 0, 0);
|
||||
mvwprintw(entry_win, 2, 2, " RTL RMS Squelch Level (NXDN/dPMR/Analog/Raw only): ");
|
||||
mvwprintw(entry_win, 2, 2, " RTL Power Squelch Level (NXDN/dPMR/Analog/Raw only): ");
|
||||
mvwprintw(entry_win, 3, 3, " ");
|
||||
echo();
|
||||
refresh();
|
||||
@@ -465,7 +463,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
mvwprintw(entry_win, 8, 2, " RTL Volume Multiplier: %d", opts->rtl_volume_multiplier);
|
||||
mvwprintw(entry_win, 9, 2, " RTL Device UDP Port: %d", opts->rtl_udp_port);
|
||||
mvwprintw(entry_win, 10, 2, " RTL Device PPM: %d", opts->rtlsdr_ppm_error);
|
||||
mvwprintw(entry_win, 11, 2, " RTL RMS Squelch: %d", opts->rtl_squelch_level);
|
||||
mvwprintw(entry_win, 11, 2, " RTL PWR Squelch: %d", opts->rtl_squelch_level);
|
||||
mvwprintw(entry_win, 13, 2, " Are You Sure?");
|
||||
mvwprintw(entry_win, 14, 2, " 1 = Yes, 2 = No ");
|
||||
mvwprintw(entry_win, 15, 3, " ");
|
||||
@@ -694,8 +692,8 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
if (choicec == 12)
|
||||
{
|
||||
//TODO: Add Closing of RAW files as well?
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
|
||||
opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling
|
||||
opts->wav_out_fileR[0] = 0;
|
||||
opts->dmr_stereo_wav = 0;
|
||||
@@ -923,11 +921,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
//toggle enforcement of basic privacy key over enc bit set on traffic
|
||||
if (option == 4)
|
||||
{
|
||||
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21)
|
||||
if (state->M == 1 || state->M == 0x21)
|
||||
{
|
||||
state->forced_alg_id = 0;
|
||||
state->M = 0;
|
||||
}
|
||||
else state->forced_alg_id = 1;
|
||||
else state->M = 1;
|
||||
}
|
||||
|
||||
if (option == 5)
|
||||
@@ -953,11 +951,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
//toggle enforcement of rc4 key over missing pi header/le on DMR
|
||||
// if (option == 6)
|
||||
// {
|
||||
// if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21)
|
||||
// if (state->M == 1 || state->M == 0x21)
|
||||
// {
|
||||
// state->forced_alg_id = 0;
|
||||
// state->M = 0;
|
||||
// }
|
||||
// else state->forced_alg_id = 0x21;
|
||||
// else state->M = 0x21;
|
||||
// }
|
||||
|
||||
//load AES keys
|
||||
@@ -1417,6 +1415,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
opts->inverted_dpmr = 1;
|
||||
opts->inverted_x2tdma = 1;
|
||||
opts->inverted_ysf = 1;
|
||||
opts->inverted_m17 = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1424,6 +1423,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
opts->inverted_dpmr = 0;
|
||||
opts->inverted_x2tdma = 0;
|
||||
opts->inverted_ysf = 0;
|
||||
opts->inverted_m17 = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1587,12 +1587,10 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
openPulseOutput (opts);
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
|
||||
{
|
||||
openOSSOutput (opts);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (opts->audio_in_type == 0) //reopen pulse input if it is the specified input method
|
||||
|
||||
@@ -223,6 +223,9 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw ("| Symbol Float Input: %s \n", opts->audio_in_dev);
|
||||
}
|
||||
|
||||
if (opts->m17decoderip == 1 && opts->udp_sockfd)
|
||||
printw ("| M17 UDP IP Frame Input: %s:%d \n", opts->m17_hostname, opts->m17_portno);
|
||||
|
||||
if (opts->audio_in_type == 8)
|
||||
{
|
||||
printw ("| TCP Signal Input: %s:%d; %d kHz; 1 Ch; ", opts->tcp_hostname, opts->tcp_portno, opts->wav_sample_rate/1000);
|
||||
@@ -245,14 +248,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->audio_in_type == 3)
|
||||
{
|
||||
printw ("| RTL: %d;", opts->rtl_dev_index);
|
||||
if (opts->rtl_gain_actual == -100)
|
||||
if (opts->rtl_gain_value == 0)
|
||||
printw (" G: AGC;");
|
||||
else
|
||||
printw (" G: %i/%i;", opts->rtl_gain_value, opts->rtl_gain_actual);
|
||||
printw (" G: %idB;", opts->rtl_gain_value);
|
||||
printw (" V: %iX;", opts->rtl_volume_multiplier);
|
||||
printw (" PPM: %i;", opts->rtlsdr_ppm_error); //Adjust manually now with { and }
|
||||
printw (" SQ: %i;", opts->rtl_squelch_level);
|
||||
printw (" RMS: %04li;", opts->rtl_rms);
|
||||
printw (" PWR: %04li;", opts->rtl_pwr);
|
||||
printw (" BW: %i;", opts->rtl_bandwidth);
|
||||
printw (" FRQ: %i;", opts->rtlsdr_center_freq);
|
||||
if (opts->rtl_udp_port != 0) printw ("\n| External RTL Tuning on UDP Port: %i", opts->rtl_udp_port);
|
||||
@@ -279,7 +282,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw ("| Pulse Analog Output: %i kHz; %i Ch; G: %02.0f%% (/|*) ", opts->pulse_raw_rate_out/1000, opts->pulse_raw_out_channels, opts->audio_gainA);
|
||||
if (opts->audio_gainA == 0.0f) printw ("Auto ");
|
||||
else printw ("Manual ");
|
||||
if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms);
|
||||
if (opts->audio_in_type != 3) printw ("PWR: %04ld; ", opts->rtl_pwr);
|
||||
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
|
||||
if (opts->use_hpf == 1) printw ("HP|"); else printw (" |");
|
||||
if (opts->use_pbf == 1) printw ("PB|"); else printw (" |");
|
||||
@@ -299,7 +302,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) )
|
||||
{
|
||||
printw ("\n| Analog Monitor RMS: %04ld; G: %02.0f%% (/|*) ", opts->rtl_rms, opts->audio_gainA);
|
||||
printw ("\n| Analog Monitor PWR: %04ld; G: %02.0f%% (/|*) ", opts->rtl_pwr, opts->audio_gainA);
|
||||
if (opts->audio_gainA == 0.0f) printw ("Auto ");
|
||||
else printw ("Manual ");
|
||||
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
|
||||
@@ -318,14 +321,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->use_hpf_d == 1) printw (" HPF");
|
||||
if (opts->call_alert == 1) printw (" *CA!"); //Call Alert
|
||||
if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) )
|
||||
printw (" - Monitor RMS: %04ld ", opts->rtl_rms);
|
||||
printw (" - Monitor PWR: %04ld ", opts->rtl_pwr);
|
||||
printw (" \n");
|
||||
if (opts->udp_sockfdA != 0) //Analog Output on udp port +2
|
||||
{
|
||||
printw ("| UDP Analog Output: %s:%d; 48 kHz 1 Ch; G: %02.0f%% (/|*) ", opts->udp_hostname, opts->udp_portno+2, opts->audio_gainA);
|
||||
if (opts->audio_gainA == 0.0f) printw ("A ");
|
||||
else printw ("M ");
|
||||
if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms);
|
||||
if (opts->audio_in_type != 3) printw ("PWR: %04ld; ", opts->rtl_pwr);
|
||||
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
|
||||
if (opts->use_hpf == 1) printw ("HP|"); else printw (" |");
|
||||
if (opts->use_pbf == 1) printw ("PB|"); else printw (" |");
|
||||
@@ -333,6 +336,25 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->m17encoder == 1)
|
||||
{
|
||||
printw ("| M17 Encoder:");
|
||||
if (state->m17encoder_tx == 1 && state->m17_vox == 0) printw (" Toggle TX (\\) ON ;");
|
||||
if (state->m17encoder_tx == 0 && state->m17_vox == 0) printw (" Toggle TX (\\) OFF;");
|
||||
if (state->m17_vox == 1) printw (" Vox Mode;");
|
||||
printw (" Input Gain (/|*): %02.0f%% ", opts->audio_gainA);
|
||||
|
||||
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
|
||||
if (opts->use_hpf == 1) printw ("HP|"); else printw (" |");
|
||||
if (opts->use_pbf == 1) printw ("PB|"); else printw (" |");
|
||||
if (opts->audio_in_type != 3 && state->m17_vox == 1) printw ( " SQL: %04ld : %04d;", opts->rtl_pwr, opts->rtl_squelch_level);
|
||||
printw ("\n");
|
||||
|
||||
}
|
||||
|
||||
if (opts->m17_use_ip == 1)
|
||||
printw ("| M17 UDP IP Frame Output: %s:%d \n", opts->m17_hostname, opts->m17_portno);
|
||||
|
||||
if (opts->mbe_out_dir[0] != 0 && opts->dmr_stereo == 0)
|
||||
{
|
||||
printw ("| Writing MBE data files to directory %s\n", opts->mbe_out_dir);
|
||||
@@ -363,7 +385,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
printw ("| Trunking -");
|
||||
if (opts->trunk_tune_group_calls == 0) attron(COLOR_PAIR(2));
|
||||
printw (" Group(T)");
|
||||
printw (" Group(g)");
|
||||
attron(COLOR_PAIR(4));
|
||||
if (opts->trunk_tune_private_calls == 0) attron(COLOR_PAIR(2));
|
||||
printw (" Private(u)");
|
||||
@@ -382,8 +404,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->p25_trunk == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3) )
|
||||
{
|
||||
printw ("| Trunking -");
|
||||
if (opts->trunk_tune_group_calls == 0) printw (" group(T)");
|
||||
else printw (" GROUP(T)");
|
||||
if (opts->trunk_tune_group_calls == 0) printw (" group(g)");
|
||||
else printw (" GROUP(g)");
|
||||
if (opts->trunk_tune_private_calls == 0) printw (" private(u)");
|
||||
else printw (" PRIVATE(u)");
|
||||
if (opts->trunk_tune_data_calls == 0) printw (" data(d)");
|
||||
@@ -495,18 +517,18 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
#endif
|
||||
// if (opts->aggressive_framesync == 0) printw ("| Selective CRC ERR Bypass Enabled (RAS) \n");
|
||||
if (state->forced_alg_id == 1)
|
||||
if (state->M == 1)
|
||||
{
|
||||
if (state->R != 0) printw ("| Forcing Key Priority -- NXDN Sc Key: %05lld \n", state->R);
|
||||
if (state->K != 0) printw ("| Forcing Key Priority -- Moto BP Key: %03lld \n", state->K);
|
||||
if (state->K1 != 0) printw ("| Forcing Key Priority -- Hytera BP Key: %016llX \n", state->K1);
|
||||
if (state->K != 0 && state->K1 != 0) printw ("| Warning! Multiple DMR Key Types Loaded! \n"); //warning may not be required
|
||||
}
|
||||
if (state->forced_alg_id == 0x21)
|
||||
if (state->M == 0x21)
|
||||
{
|
||||
if (state->R != 0) printw ("| Forcing Key Priority -- RC4 Key: %010llX \n", state->R);
|
||||
}
|
||||
if (state->forced_alg_id == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H);
|
||||
if (state->M == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H);
|
||||
|
||||
if (opts->scanner_mode == 1)
|
||||
{
|
||||
@@ -539,6 +561,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->mod_c4fm == 1) printw ("[C4FM]");
|
||||
if (opts->mod_gfsk == 1) printw ("[GFSK]");
|
||||
printw ( "[%d] \n", (48000*opts->wav_interpolator)/state->samplesPerSymbol);
|
||||
if (opts->m17encoder == 1) printw ("| Encoding: [%s] \n", opts->output_name);
|
||||
printw ("| Decoding: [%s] ", opts->output_name);
|
||||
if (opts->aggressive_framesync == 0) printw ("CRC/(RAS) ");
|
||||
//debug -- troubleshoot voice tuning after grant on DMR CC, subsequent grant may not tune because tuner isn't available
|
||||
@@ -631,22 +654,20 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw ("\n");
|
||||
printw ("| ");
|
||||
|
||||
|
||||
//fill in any extra info, like Meta (IV, etc)
|
||||
if (state->m17_enc == 1)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw ("Scrambler - Type: %d", state->m17_enc_st);
|
||||
attron(COLOR_PAIR(3));
|
||||
printw (" Scrambler - Type: %d", state->m17_enc_st);
|
||||
}
|
||||
|
||||
if (state->m17_enc == 2)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw ("AES-CTR IV: ");
|
||||
//display AES IV
|
||||
printw ("AES-CTR - IV: ");
|
||||
//display packed meta as IV
|
||||
for (i = 0; i < 16; i++)
|
||||
printw ("%02X", state->m17_aes_iv[i]);
|
||||
attron(COLOR_PAIR(3));
|
||||
printw ("%02X", state->m17_meta[i]);
|
||||
}
|
||||
|
||||
if (state->m17_enc == 3)
|
||||
@@ -655,30 +676,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
printw ("\n");
|
||||
printw ("| ");
|
||||
|
||||
//decoded elements from stream
|
||||
char shortm17[72];
|
||||
memset(shortm17, 0, sizeof(shortm17));
|
||||
strncpy (shortm17, state->m17_text_string, 70);
|
||||
shortm17[71] = 0;
|
||||
printw ("META TEXT: %s", shortm17);
|
||||
|
||||
printw ("\n");
|
||||
printw ("| ");
|
||||
memset(shortm17, 0, sizeof(shortm17));
|
||||
strncpy (shortm17, state->m17_gnss_string, 70);
|
||||
shortm17[71] = 0;
|
||||
printw ("META GNSS: %s", shortm17);
|
||||
|
||||
printw ("\n");
|
||||
printw ("| ");
|
||||
memset(shortm17, 0, sizeof(shortm17));
|
||||
strncpy (shortm17, state->m17_data_string, 70);
|
||||
shortm17[71] = 0;
|
||||
printw ("MISC DATA: %s", shortm17);
|
||||
|
||||
printw ("\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -1112,26 +1109,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw(" Key: %010llX", state->R);
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algid == 0x36)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Kirisun Advanced ");
|
||||
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algid == 0x37)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Kirisun Universal ");
|
||||
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algid == 0x35)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Kirisun Encryption "); //0x35 is a placeholder if gained through late-entry method and alg is not known
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
|
||||
printw ("\n");
|
||||
|
||||
@@ -1307,26 +1284,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw(" Key: %010llX", state->RR);
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algidR == 0x36)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Kirisun Advanced ");
|
||||
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algidR == 0x37)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Kirisun Universal ");
|
||||
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algidR == 0x35)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Kirisun Encryption "); //0x35 is a placeholder if gained through late-entry method and alg is not known
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
|
||||
printw ("\n");
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ void error(char *msg) {
|
||||
|
||||
struct sockaddr_in address;
|
||||
struct sockaddr_in addressA;
|
||||
struct sockaddr_in addressM17;
|
||||
|
||||
//
|
||||
// Connect
|
||||
@@ -323,6 +324,18 @@ void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void *
|
||||
if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible
|
||||
}
|
||||
|
||||
int m17_socket_receiver(dsd_opts * opts, void * data)
|
||||
{
|
||||
size_t err = 0;
|
||||
struct sockaddr_in cliaddr;
|
||||
socklen_t len = sizeof(cliaddr);
|
||||
|
||||
//receive data from socket
|
||||
err = recvfrom(opts->udp_sockfd, data, 1000, 0, (struct sockaddr * ) & address, &len); //was MSG_WAITALL, but that seems to be = 256
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
//Analog UDP port on +2 of normal open socket
|
||||
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
|
||||
{
|
||||
@@ -340,6 +353,20 @@ void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void *
|
||||
if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible
|
||||
}
|
||||
|
||||
int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
|
||||
{
|
||||
UNUSED(state);
|
||||
unsigned long long int err = 0;
|
||||
|
||||
//See notes in m17.c on line ~3395 regarding usage
|
||||
|
||||
//send audio or data to socket
|
||||
err = sendto(opts->m17_udp_sock, data, nsam, 0, (const struct sockaddr * ) & addressM17, sizeof(struct sockaddr_in));
|
||||
//RETURN Value should be ACKN or NACK, or PING, or PONG
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
int udp_socket_connect(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
UNUSED(state);
|
||||
@@ -420,6 +447,46 @@ int udp_socket_connectA(dsd_opts * opts, dsd_state * state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udp_socket_connectM17(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
UNUSED(state);
|
||||
|
||||
long int err = 0;
|
||||
err = opts->m17_udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (err < 0)
|
||||
{
|
||||
fprintf (stderr, " UDP Socket Error %ld\n", err);
|
||||
return (err);
|
||||
}
|
||||
|
||||
// Don't think this is needed, but doesn't seem to hurt to keep it here either
|
||||
int broadcastEnable = 1;
|
||||
err = setsockopt(opts->m17_udp_sock, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
|
||||
if (err < 0)
|
||||
{
|
||||
fprintf (stderr, " UDP Broadcast Set Error %ld\n", err);
|
||||
return (err);
|
||||
}
|
||||
|
||||
memset((char * ) & addressM17, 0, sizeof(addressM17));
|
||||
addressM17.sin_family = AF_INET;
|
||||
err = addressM17.sin_addr.s_addr = inet_addr(opts->m17_hostname);
|
||||
if (err < 0) //error in this context reports back 32-bit inet_addr reversed order byte pairs
|
||||
{
|
||||
fprintf (stderr, " UDP inet_addr Error %ld\n", err);
|
||||
return (err);
|
||||
}
|
||||
|
||||
addressM17.sin_port = htons(opts->m17_portno);
|
||||
if (err < 0)
|
||||
{
|
||||
fprintf (stderr, " UDP htons Error %ld\n", err);
|
||||
return (err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void return_to_cc (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
//extra safeguards due to sync issues with NXDN
|
||||
|
||||
@@ -132,7 +132,7 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
||||
if (get_rtlsdr_sample(&sample, opts, state) < 0)
|
||||
cleanupAndExit(opts, state);
|
||||
//update root means square power level
|
||||
opts->rtl_rms = rtl_return_rms();
|
||||
opts->rtl_pwr = rtl_return_pwr();
|
||||
sample *= opts->rtl_volume_multiplier;
|
||||
|
||||
#endif
|
||||
@@ -268,9 +268,9 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
||||
|
||||
if (state->analog_sample_counter == 960)
|
||||
{
|
||||
//get an rms value if not using the rtl built in version
|
||||
//get an pwr value if not using the rtl built in version
|
||||
if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1)
|
||||
opts->rtl_rms = raw_rms(state->analog_out, 960, 1);
|
||||
opts->rtl_pwr = raw_pwr(state->analog_out, 960, 1);
|
||||
|
||||
//raw wav file saving -- only write when not NXDN, dPMR, or M17 due to noise that can cause tons of false positives when no sync
|
||||
if (opts->wav_out_raw != NULL && opts->frame_nxdn48 == 0 && opts->frame_nxdn96 == 0 && opts->frame_dpmr == 0 && opts->frame_m17 == 0)
|
||||
@@ -299,13 +299,13 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
||||
else
|
||||
agsm(opts, state, state->analog_out, 960);
|
||||
|
||||
//Running RMS after filtering does remove the analog spike from the RMS value
|
||||
//Running PWR after filtering does remove the analog spike from the PWR value
|
||||
//but noise floor noise will still produce higher values
|
||||
// if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1)
|
||||
// opts->rtl_rms = raw_rms(state->analog_out, 960, 1);
|
||||
// opts->rtl_pwr = raw_pwr(state->analog_out, 960, 1);
|
||||
|
||||
//seems to be working now, but RMS values are lower on actual analog signal than on no signal but noise
|
||||
if ( (opts->rtl_rms > opts->rtl_squelch_level) && opts->monitor_input_audio == 1 && state->carrier == 0 ) //added carrier check here in lieu of disabling it above
|
||||
//seems to be working now, but PWR values are lower on actual analog signal than on no signal but noise
|
||||
if ( (opts->rtl_pwr > opts->rtl_squelch_level) && opts->monitor_input_audio == 1 && state->carrier == 0 ) //added carrier check here in lieu of disabling it above
|
||||
{
|
||||
if (opts->audio_out_type == 0)
|
||||
pa_simple_write(opts->pulse_raw_dev_out, state->analog_out, 960*2, NULL);
|
||||
|
||||
@@ -179,7 +179,7 @@ unsigned long long int edacsVoteFr(unsigned long long int fr_1_4, unsigned long
|
||||
void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn)
|
||||
{
|
||||
int i, result;
|
||||
int count = 5; //RMS has a 5 count (5 * 180ms) now before cutting off;
|
||||
int count = 5; //PWR has a 5 count (5 * 180ms) now before cutting off;
|
||||
short analog1[960];
|
||||
short analog2[960];
|
||||
short analog3[960];
|
||||
@@ -202,7 +202,7 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
|
||||
memset (d2, 0, sizeof(d2));
|
||||
memset (d3, 0, sizeof(d3));
|
||||
|
||||
long int rms = opts->rtl_squelch_level + 1; //one more for the initial loop phase
|
||||
long int pwr = opts->rtl_squelch_level + 1; //one more for the initial loop phase
|
||||
long int sql = opts->rtl_squelch_level;
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
@@ -229,8 +229,8 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
|
||||
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||
analog3[i] = sample;
|
||||
}
|
||||
//this rms will only work properly (for now) with squelch enabled in SDR++ or other
|
||||
rms = raw_rms(analog3, 960, 1);
|
||||
//this pwr will only work properly (for now) with squelch enabled in SDR++ or other
|
||||
pwr = raw_pwr(analog3, 960, 1);
|
||||
}
|
||||
|
||||
//NOTE: The core dumps observed previously were due to SDR++ Remote Server connection dropping due to Internet/Other issues
|
||||
@@ -287,8 +287,8 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
|
||||
analog3[i] = sample;
|
||||
}
|
||||
|
||||
//this rms will only work properly (for now) with squelch enabled in SDR++
|
||||
rms = raw_rms(analog3, 960, 1);
|
||||
//this pwr will only work properly (for now) with squelch enabled in SDR++
|
||||
pwr = raw_pwr(analog3, 960, 1);
|
||||
}
|
||||
|
||||
//RTL Input
|
||||
@@ -315,8 +315,8 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
|
||||
sample *= opts->rtl_volume_multiplier;
|
||||
analog3[i] = sample;
|
||||
}
|
||||
//the rtl rms value works properly without needing a 'hard' squelch value
|
||||
rms = rtl_return_rms();
|
||||
//the rtl pwr value works properly without needing a 'hard' squelch value
|
||||
pwr = rtl_return_pwr();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -387,12 +387,12 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
|
||||
agsm (opts, state, analog3, 960);
|
||||
}
|
||||
|
||||
//NOTE: Ideally, we would run raw_rms for TCP/VS here, but the analog spike on EDACS (STM)
|
||||
//NOTE: Ideally, we would run raw_pwr for TCP/VS here, but the analog spike on EDACS (STM)
|
||||
//system gets filtered out, and when they hold the radio open and don't talk,
|
||||
//it counts against the squelch hit as no audio, so we will just have to use
|
||||
//the squelch checkbox in SDR++ and similar when using those input methods
|
||||
// if (opts->audio_in_type != 3)
|
||||
// rms = raw_rms(analog3, 960, 1);
|
||||
// pwr = raw_pwr(analog3, 960, 1);
|
||||
|
||||
//reconfigured to use seperate audio out stream that is always 48k short
|
||||
if (opts->audio_out_type == 0 && opts->slot1_on == 1)
|
||||
@@ -425,18 +425,18 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
|
||||
write (opts->audio_out_fd, analog3, 960*2);
|
||||
}
|
||||
|
||||
opts->rtl_rms = rms;
|
||||
opts->rtl_pwr = pwr;
|
||||
|
||||
|
||||
printFrameSync (opts, state, " EDACS", 0, "A");
|
||||
|
||||
if (rms < sql) count--;
|
||||
if (pwr < sql) count--;
|
||||
else count = 5;
|
||||
|
||||
if (rms > sql) fprintf(stderr, "%s", KGRN);
|
||||
if (pwr > sql) fprintf(stderr, "%s", KGRN);
|
||||
else fprintf(stderr, "%s", KRED);
|
||||
|
||||
fprintf (stderr, " Analog RMS: %04ld SQL: %ld", rms, sql);
|
||||
fprintf (stderr, " Analog PWR: %04ld SQL: %ld", pwr, sql);
|
||||
if (state->ea_mode == 0)
|
||||
{
|
||||
int a = (afs >> state->edacs_a_shift) & state->edacs_a_mask;
|
||||
@@ -1098,7 +1098,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
if (is_digital == 1) {} //just write to already open temp file to be renamed later
|
||||
else //close the temp 8k wav file and open as 48k
|
||||
{
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
|
||||
}
|
||||
}
|
||||
@@ -1214,7 +1214,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
if (is_digital == 1) {} //just write to already open temp file to be renamed later
|
||||
else //close the temp 8k wav file and open as 48k
|
||||
{
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
|
||||
}
|
||||
}
|
||||
@@ -1321,7 +1321,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
if (is_digital == 1) {} //just write to already open temp file to be renamed later
|
||||
else //close the temp 8k wav file and open as 48k
|
||||
{
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
|
||||
}
|
||||
}
|
||||
@@ -1488,7 +1488,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//NOTE: Restructured below so that analog and digital are handled the same, just that when
|
||||
//its analog, it will now start edacs_analog which will while loop analog samples until
|
||||
//signal level drops (RMS, or a dotting sequence is detected)
|
||||
//signal level drops (PWR, or a dotting sequence is detected)
|
||||
|
||||
//this is working now with the new import setup
|
||||
if (opts->trunk_tune_group_calls == 1 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
||||
@@ -1501,7 +1501,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
if (is_digital == 1) {} //just write to already open temp file to be renamed later
|
||||
else //close the temp 8k wav file and open as 48k
|
||||
{
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
|
||||
}
|
||||
}
|
||||
@@ -1743,7 +1743,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//NOTE: Restructured below so that analog and digital are handled the same, just that when
|
||||
//its analog, it will now start edacs_analog which will while loop analog samples until
|
||||
//signal level drops (RMS, or a dotting sequence is detected)
|
||||
//signal level drops (PWR, or a dotting sequence is detected)
|
||||
|
||||
//this is working now with the new import setup
|
||||
if (((is_individual == 0 && opts->trunk_tune_group_calls == 1) || (is_individual == 1 && opts->trunk_tune_private_calls == 1)) &&
|
||||
@@ -1757,7 +1757,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
if (is_digital == 1) {} //just write to already open temp file to be renamed later
|
||||
else //close the temp 8k wav file and open as 48k
|
||||
{
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
|
||||
}
|
||||
}
|
||||
@@ -1857,7 +1857,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//NOTE: Restructured below so that analog and digital are handled the same, just that when
|
||||
//its analog, it will now start edacs_analog which will while loop analog samples until
|
||||
//signal level drops (RMS, or a dotting sequence is detected)
|
||||
//signal level drops (PWR, or a dotting sequence is detected)
|
||||
|
||||
//this is working now with the new import setup
|
||||
if ((opts->trunk_tune_private_calls == 1) && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
||||
@@ -1870,7 +1870,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
if (is_digital == 1) {} //just write to already open temp file to be renamed later
|
||||
else //close the temp 8k wav file and open as 48k
|
||||
{
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
|
||||
}
|
||||
}
|
||||
@@ -2153,7 +2153,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//NOTE: Restructured below so that analog and digital are handled the same, just that when
|
||||
//its analog, it will now start edacs_analog which will while loop analog samples until
|
||||
//signal level drops (RMS, or a dotting sequence is detected)
|
||||
//signal level drops (PWR, or a dotting sequence is detected)
|
||||
|
||||
//this is working now with the new import setup
|
||||
if ((opts->trunk_tune_group_calls == 1) && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
||||
@@ -2166,7 +2166,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
if (is_digital == 1) {} //just write to already open temp file to be renamed later
|
||||
else //close the temp 8k wav file and open as 48k
|
||||
{
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
|
||||
}
|
||||
}
|
||||
@@ -2299,10 +2299,6 @@ void eot_cc(dsd_opts * opts, dsd_state * state)
|
||||
|
||||
fprintf (stderr, "EOT; \n");
|
||||
|
||||
//skip an arbitrary number of dibits so that the CC can cancel the grant after a dotting sequence
|
||||
//prior to returning to the control channel and getting sent in a loopback to the call channel
|
||||
skipDibit(opts, state, 240*8);
|
||||
|
||||
//watchdog event at this point
|
||||
state->lastsynctype = 38;
|
||||
watchdog_event_history(opts, state, 0);
|
||||
@@ -2312,7 +2308,7 @@ void eot_cc(dsd_opts * opts, dsd_state * state)
|
||||
if (opts->dmr_stereo_wav == 1)
|
||||
{
|
||||
if (opts->wav_out_f != NULL)
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1479,7 +1479,7 @@ bool QR_16_7_6_decode(unsigned char *rxBits)
|
||||
unsigned int syndromeI = 0; // syndrome index
|
||||
int is = 0;
|
||||
int i = 0;
|
||||
int corrections = 0; UNUSED(corrections);
|
||||
int corrections = 0;
|
||||
for (is = 0; is < 9; is++)
|
||||
{
|
||||
syndromeI += (((rxBits[0] * QR_16_7_6_m_H[16*is + 0])
|
||||
|
||||
173
src/nxdn_convolution.c
Normal file
173
src/nxdn_convolution.c
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016,2018 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* Copyright (C) 2018 by Edouard Griffiths F4EXB:
|
||||
* - Cosmetic changes to integrate with DSDcc
|
||||
*
|
||||
* Copyright (C) 2018 by Louis HERVE F4HUZ:
|
||||
* - Transform C++ lib into C lib to integrate with DSD
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
/* Include ------------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* Define -------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/* Global variables ---------------------------------------------------------*/
|
||||
static const uint8_t CNXDNConvolution_BIT_MASK_TABLE[8] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | CNXDNConvolution_BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
static const uint8_t CNXDNConvolution_BRANCH_TABLE1[8] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U};
|
||||
static const uint8_t CNXDNConvolution_BRANCH_TABLE2[8] = {0U, 2U, 2U, 0U, 0U, 2U, 2U, 0U};
|
||||
|
||||
static const unsigned int CNXDNConvolution_NUM_OF_STATES_D2 = 8U;
|
||||
static const uint32_t CNXDNConvolution_M = 4U;
|
||||
static const unsigned int CNXDNConvolution_K = 5U;
|
||||
|
||||
//NOTE:
|
||||
uint16_t m_metrics1[16];
|
||||
uint16_t m_metrics2[16];
|
||||
uint64_t m_decisions[8*300];
|
||||
uint16_t * m_oldMetrics = NULL;
|
||||
uint16_t * m_newMetrics = NULL;
|
||||
uint64_t * m_dp = NULL;
|
||||
|
||||
/* Functions ----------------------------------------------------------------*/
|
||||
|
||||
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
uint8_t j = 0;
|
||||
uint8_t decision0 = 0;
|
||||
uint8_t decision1 = 0;
|
||||
uint16_t metric = 0;
|
||||
uint16_t m0 = 0;
|
||||
uint16_t m1 = 0;
|
||||
uint16_t * tmp = NULL;
|
||||
|
||||
*m_dp = 0U;
|
||||
|
||||
for (i = 0U; i < CNXDNConvolution_NUM_OF_STATES_D2; i++)
|
||||
{
|
||||
j = i * 2U;
|
||||
|
||||
metric = abs(CNXDNConvolution_BRANCH_TABLE1[i] - s0) + abs(CNXDNConvolution_BRANCH_TABLE2[i] - s1);
|
||||
|
||||
m0 = m_oldMetrics[i] + metric;
|
||||
m1 = m_oldMetrics[i + CNXDNConvolution_NUM_OF_STATES_D2] + (CNXDNConvolution_M - metric);
|
||||
decision0 = (m0 >= m1) ? 1U : 0U;
|
||||
m_newMetrics[j + 0U] = decision0 != 0U ? m1 : m0;
|
||||
|
||||
m0 = m_oldMetrics[i] + (CNXDNConvolution_M - metric);
|
||||
m1 = m_oldMetrics[i + CNXDNConvolution_NUM_OF_STATES_D2] + metric;
|
||||
decision1 = (m0 >= m1) ? 1U : 0U;
|
||||
m_newMetrics[j + 1U] = decision1 != 0U ? m1 : m0;
|
||||
|
||||
*m_dp |= ((uint64_t)(decision1) << (j + 1U)) | ((uint64_t)(decision0) << (j + 0U));
|
||||
}
|
||||
|
||||
++m_dp;
|
||||
|
||||
//assert((m_dp - m_decisions) <= 300);
|
||||
|
||||
tmp = m_oldMetrics;
|
||||
m_oldMetrics = m_newMetrics;
|
||||
m_newMetrics = tmp;
|
||||
}
|
||||
|
||||
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits)
|
||||
{
|
||||
//assert(out != 0);
|
||||
|
||||
uint32_t state = 0U;
|
||||
uint32_t i = 0;
|
||||
uint8_t bit = 0;
|
||||
|
||||
while (nBits-- > 0)
|
||||
{
|
||||
--m_dp;
|
||||
|
||||
i = state >> (9 - CNXDNConvolution_K);
|
||||
bit = (uint8_t)(*m_dp >> i) & 1;
|
||||
state = (bit << 7) | (state >> 1);
|
||||
|
||||
WRITE_BIT1(out, nBits, bit != 0U);
|
||||
}
|
||||
}
|
||||
|
||||
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits)
|
||||
{
|
||||
//assert(in != 0);
|
||||
//assert(out != 0);
|
||||
//assert(nBits > 0U);
|
||||
|
||||
uint8_t g1 = 0U;
|
||||
uint8_t g2 = 0U;
|
||||
uint8_t d = 0U;
|
||||
uint8_t d1 = 0U;
|
||||
uint8_t d2 = 0U;
|
||||
uint8_t d3 = 0U;
|
||||
uint8_t d4 = 0U;
|
||||
uint32_t k = 0U;
|
||||
unsigned int i = 0U;
|
||||
|
||||
for (i = 0U; i < nBits; i++)
|
||||
{
|
||||
d = READ_BIT1(in, i) ? 1U : 0U;
|
||||
|
||||
g1 = (d + d3 + d4) & 1;
|
||||
g2 = (d + d1 + d2 + d4) & 1;
|
||||
|
||||
d4 = d3;
|
||||
d3 = d2;
|
||||
d2 = d1;
|
||||
d1 = d;
|
||||
|
||||
WRITE_BIT1(out, k, g1 != 0U);
|
||||
k++;
|
||||
|
||||
WRITE_BIT1(out, k, g2 != 0U);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
void CNXDNConvolution_start(void)
|
||||
{
|
||||
|
||||
m_oldMetrics = m_metrics1;
|
||||
m_newMetrics = m_metrics2;
|
||||
m_dp = m_decisions;
|
||||
|
||||
}
|
||||
|
||||
void CNXDNConvolution_init()
|
||||
{
|
||||
memset(m_metrics1, 0x0, 16*sizeof(uint16_t));
|
||||
memset(m_metrics2, 0x0, 16*sizeof(uint16_t));
|
||||
memset(m_decisions,0x0, 8*300*sizeof(uint64_t));
|
||||
}
|
||||
|
||||
2790
src/nxdn_deperm.c
2790
src/nxdn_deperm.c
File diff suppressed because it is too large
Load Diff
1519
src/nxdn_element.c
1519
src/nxdn_element.c
File diff suppressed because it is too large
Load Diff
444
src/nxdn_frame.c
444
src/nxdn_frame.c
@@ -1,32 +1,40 @@
|
||||
//NXDN frame handler
|
||||
//Reworked portions from Osmocom OP25 rx_sync.cc
|
||||
|
||||
/*-------------------------------------------------------------------------------
|
||||
* nxdn_frame.c
|
||||
* NXDN Frame Handling
|
||||
*
|
||||
* Reworked portions from Osmocom OP25 rx_sync.cc
|
||||
/* -*- c++ -*- */
|
||||
/*
|
||||
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2026-03 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
*
|
||||
* This 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 3, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This software 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 software; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "nxdn_const.h"
|
||||
|
||||
#define LICH_ERR_THRESHOLD 7 //Threshold for lich dibit "dividing" errors (8 is perfect, 7 is 1 bit error)
|
||||
// #define NXDN_DEBUG_LICH //print LICH debug info on err on payload == 1
|
||||
// #define NXDN_LICH_OFFBITS //use the offbits to help determine sync status (disable if bad signal / bad sample)
|
||||
|
||||
//NXDN Type Enablement (handled by cmake -DTYPEC=OFF , -DTYPED=OFF, -DDCR=OFF)
|
||||
// #define JPNDCR_DECODE //Include Japanese DCR Decoding
|
||||
// #define TYPE_D_DECODE //Include Type-D Decoding
|
||||
// #define TYPE_C_DECODE //Include Type-C and Conventional Decoding (Basically the "normal" NXDN stuff)
|
||||
// #define NXDN_DEBUG_LICH //print LICH debug info on err on payload == 1
|
||||
#define NXDN_LICH_OFFBITS //use the offbits to help determine sync status (disable if bad signal / bad sample)
|
||||
|
||||
void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// length is implicitly 192, with frame sync in first 10 dibits
|
||||
uint8_t dbuf[182];
|
||||
uint8_t lich = 0;
|
||||
uint8_t lich;
|
||||
uint8_t answer[32];
|
||||
uint8_t sacch_answer[32];
|
||||
int lich_parity_received;
|
||||
int lich_parity_computed;
|
||||
int voice = 0;
|
||||
@@ -63,21 +71,12 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//nxdn bit buffer, for easy assignment handling
|
||||
int nxdn_bit_buffer[364];
|
||||
|
||||
#ifdef NXDN_DEBUG_LICH
|
||||
/*
|
||||
//this is how many bad syncs with polarity flipping occurred when allowed both pos and inv polarity on sync test
|
||||
Sync: no sync
|
||||
[+] [+] [-] [+] [-] [+] [-] [-] [-] [+] [+] [-] [+] (+) 17:36:02 Sync: NXDN48 RDCH Voice RAN 01 PF X/4
|
||||
*/
|
||||
//debug polarity (purely illustative, shows how frequently false sync can be attributed to wrong polarity)
|
||||
if (state->lastsynctype == 28)
|
||||
fprintf (stderr, "[+] ");
|
||||
else fprintf (stderr, "[-] ");
|
||||
#endif
|
||||
int nxdn_dibit_buffer[182];
|
||||
|
||||
//init all arrays
|
||||
memset (dbuf, 0, sizeof(dbuf));
|
||||
memset (answer, 0, sizeof(answer));
|
||||
memset (sacch_answer, 0, sizeof(sacch_answer));
|
||||
memset (lich_dibits, 0, sizeof(lich_dibits));
|
||||
memset (sacch_bits, 0, sizeof(sacch_bits));
|
||||
memset (facch_bits_b, 0, sizeof(facch_bits_b));
|
||||
@@ -86,18 +85,15 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
memset (facch2_bits, 0, sizeof(facch2_bits));
|
||||
|
||||
memset (nxdn_bit_buffer, 0, sizeof(nxdn_bit_buffer));
|
||||
memset (nxdn_dibit_buffer, 0, sizeof(nxdn_dibit_buffer));
|
||||
|
||||
//collect lich bits first, if they are good, then we can collect the rest of them
|
||||
for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
||||
|
||||
nxdn_pn95_dibit_scrambler (state, lich_dibits, 8);
|
||||
nxdn_descramble (lich_dibits, 8);
|
||||
|
||||
//divide lich_dibits to get the lich code w/ parity bit on LSB
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
lich <<= 1;
|
||||
lich |= (lich_dibits[i] >> 1) & 1;
|
||||
}
|
||||
lich = 0;
|
||||
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
|
||||
|
||||
//debug lich as a 16-bit value (with encoding "dividing")
|
||||
uint8_t lich_bits[16]; memset(lich_bits, 0, sizeof(lich_bits));
|
||||
@@ -111,17 +107,18 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//debug look at the "off bits" of the encoded lich, should be all 1's (8)
|
||||
//disble this code if sync issues arise, this may not be ideal of marginal signal
|
||||
uint8_t lich_off_hex = 0; UNUSED(lich_off_hex);
|
||||
uint8_t lich_off_hex = 0;
|
||||
for (int i=0; i<8; i++)
|
||||
lich_off_hex += lich_bits[(i*2)+1];
|
||||
#ifdef NXDN_LICH_OFFBITS
|
||||
if (lich_off_hex < LICH_ERR_THRESHOLD)
|
||||
if (lich_off_hex < 7) //allow up to 1 bit error
|
||||
{
|
||||
#ifdef NXDN_DEBUG_LICH
|
||||
if (opts->payload == 1)
|
||||
fprintf(stderr, " Lich Off Bit Fill Error: %d / 8; \n", lich_off_hex);
|
||||
#endif
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
goto END;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -130,11 +127,9 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
lich_parity_computed = ((lich_full >> 7) + (lich_full >> 6) + (lich_full >> 5) + (lich_full >> 4)) & 1;
|
||||
lich = lich_full >> 1;
|
||||
|
||||
#ifdef JPNDCR_DECODE
|
||||
//special cases on DCR where parity is computed over 7 bits, and not 4 bits
|
||||
if (lich == 0x08 || lich == 0x4A || lich == 0x48 || lich == 0x46)
|
||||
if (lich == 0x4A || lich == 0x48 || lich == 0x46)
|
||||
lich_parity_computed = ((lich_full >> 7) + (lich_full >> 6) + (lich_full >> 5) + (lich_full >> 4) + (lich_full >> 3) + (lich_full >> 2) + (lich_full >> 1)) & 1;
|
||||
#endif
|
||||
|
||||
if (lich_parity_received != lich_parity_computed)
|
||||
{
|
||||
@@ -142,7 +137,8 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->payload == 1)
|
||||
fprintf(stderr, " Lich Parity Error %02X / %04X\n", lich_full, lich_bits_hex);
|
||||
#endif
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
state->lastsynctype = -1;
|
||||
goto END;
|
||||
}
|
||||
|
||||
voice = 0;
|
||||
@@ -160,179 +156,150 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
fprintf(stderr, " Simplex/Inbound NXDN lich on trunking system - type 0x%02X\n", lich);
|
||||
#endif
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
goto END;
|
||||
}
|
||||
|
||||
switch(lich)
|
||||
{
|
||||
switch(lich) { //cases without breaks continue to flow downwards until they hit the break
|
||||
case 0x01: // CAC types
|
||||
case 0x05:
|
||||
cac = 1;
|
||||
break;
|
||||
case 0x28: //facch2 types
|
||||
case 0x29:
|
||||
// case 0x48: //removing from here, moving to DCR as pich_tch
|
||||
case 0x49:
|
||||
facch2 = 1;
|
||||
break;
|
||||
case 0x2e: //udch types
|
||||
case 0x2f:
|
||||
case 0x4e:
|
||||
case 0x4f:
|
||||
udch = 1;
|
||||
break;
|
||||
case 0x32: //facch in 1, vch in 2
|
||||
case 0x33:
|
||||
case 0x52:
|
||||
case 0x53:
|
||||
voice = 2;
|
||||
facch = 1;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x34: //vch in 1, facch in 2
|
||||
case 0x35:
|
||||
case 0x54:
|
||||
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
|
||||
voice = 1;
|
||||
facch = 2;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x36: //vch in both
|
||||
case 0x37:
|
||||
case 0x56:
|
||||
case 0x57:
|
||||
voice = 3;
|
||||
facch = 0;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x20: //facch in both
|
||||
case 0x21:
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
case 0x40:
|
||||
case 0x41:
|
||||
case 0x50:
|
||||
case 0x51:
|
||||
voice = 0;
|
||||
facch = 3;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x38: //sacch only (NULL?)
|
||||
case 0x39:
|
||||
sacch = 1;
|
||||
break;
|
||||
|
||||
#ifdef TYPE_C_DECODE
|
||||
case 0x01: // CAC types
|
||||
case 0x05:
|
||||
cac = 1;
|
||||
break;
|
||||
case 0x28: //facch2 types
|
||||
case 0x29:
|
||||
// case 0x48: //removing from here, moving to DCR as pich_tch
|
||||
case 0x49:
|
||||
facch2 = 1;
|
||||
break;
|
||||
case 0x2e: //udch types
|
||||
case 0x2f:
|
||||
case 0x4e:
|
||||
case 0x4f:
|
||||
udch = 1;
|
||||
break;
|
||||
case 0x32: //facch in 1, vch in 2
|
||||
case 0x33:
|
||||
case 0x52:
|
||||
case 0x53:
|
||||
voice = 2;
|
||||
facch = 1;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x34: //vch in 1, facch in 2
|
||||
case 0x35:
|
||||
case 0x54:
|
||||
case 0x55:
|
||||
voice = 1;
|
||||
facch = 2;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x36: //vch in both
|
||||
case 0x37:
|
||||
case 0x56:
|
||||
case 0x57:
|
||||
voice = 3;
|
||||
facch = 0;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x20: //facch in both
|
||||
case 0x21:
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
case 0x40:
|
||||
case 0x41:
|
||||
case 0x50:
|
||||
case 0x51:
|
||||
voice = 0;
|
||||
facch = 3;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x38: //sacch only (NULL?)
|
||||
case 0x39:
|
||||
sacch = 1;
|
||||
break;
|
||||
#endif
|
||||
//DCR Voice
|
||||
case 0x46:
|
||||
voice = 3;
|
||||
sacch2 = 1;
|
||||
break;
|
||||
|
||||
#ifdef JPNDCR_DECODE
|
||||
//DCR Voice
|
||||
case 0x46:
|
||||
voice = 3;
|
||||
sacch2 = 1;
|
||||
break;
|
||||
//DCR Data or End Frame
|
||||
case 0x48:
|
||||
pich_tch = 3;
|
||||
case 0x4A:
|
||||
sacch2 = 1;
|
||||
break;
|
||||
|
||||
//DCR SB0, Data or End Frame
|
||||
case 0x08: //SB0 w/ CSM 9 digit BCD found here
|
||||
sacch2 = 1;
|
||||
pich_tch = 1; //observed 2nd PICH is zero fill
|
||||
break;
|
||||
case 0x48:
|
||||
pich_tch = 3; //may be 1, or 2 TCH (observed possibly both)
|
||||
case 0x4A:
|
||||
sacch2 = 1;
|
||||
break;
|
||||
#endif
|
||||
//NXDN "Type-D" or "IDAS" Specific Lich Codes
|
||||
case 0x76: //normal vch voice (in one and two)
|
||||
case 0x77:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 3;
|
||||
break;
|
||||
// case 0x74: //False Positive on DCR, keep disabled, or revert this line
|
||||
case 0x75: //vch in 1, facch1 in 2 (facch 2 steal)
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 1;
|
||||
facch = 2;
|
||||
break;
|
||||
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
|
||||
case 0x73:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 2;
|
||||
facch = 1;
|
||||
break;
|
||||
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
|
||||
case 0x71:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 3;
|
||||
break;
|
||||
case 0x6E: //udch2
|
||||
case 0x6F:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
udch2 = 1;
|
||||
break;
|
||||
case 0x68:
|
||||
case 0x69: //facch3
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch3 = 1;
|
||||
break;
|
||||
case 0x62:
|
||||
case 0x63: //facch1 in 1, null data and post field in 2
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 1;
|
||||
break;
|
||||
case 0x60:
|
||||
case 0x61: //facch1 in both (non vch)
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 3;
|
||||
break;
|
||||
|
||||
#ifdef TYPE_D_DECODE
|
||||
//NXDN "Type-D" or "IDAS" Specific Lich Codes
|
||||
case 0x76: //normal vch voice (in one and two)
|
||||
case 0x77:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 3;
|
||||
break;
|
||||
// case 0x74: //False Positive on DCR, keep disabled, or revert this line
|
||||
case 0x75: //vch in 1, facch1 in 2 (facch 2 steal)
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 1;
|
||||
facch = 2;
|
||||
break;
|
||||
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
|
||||
case 0x73:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 2;
|
||||
facch = 1;
|
||||
break;
|
||||
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
|
||||
case 0x71:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 3;
|
||||
break;
|
||||
case 0x6E: //udch2
|
||||
case 0x6F:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
udch2 = 1;
|
||||
break;
|
||||
case 0x68:
|
||||
case 0x69: //facch3
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch3 = 1;
|
||||
break;
|
||||
case 0x62:
|
||||
case 0x63: //facch1 in 1, null data and post field in 2
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 1;
|
||||
break;
|
||||
case 0x60:
|
||||
case 0x61: //facch1 in both (non vch)
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 3;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
#ifdef NXDN_DEBUG_LICH
|
||||
if (opts->payload == 1)
|
||||
fprintf(stderr, " false sync or unsupported NXDN lich type L: %02X / LH: %04X\n", lich, lich_bits_hex);
|
||||
#endif
|
||||
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
|
||||
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
voice = 0;
|
||||
break;
|
||||
} // end of switch(lich)
|
||||
|
||||
// //collect remaining dibits at this point (before bad frame skip)
|
||||
// for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
|
||||
// dbuf[i+8] = getDibit(opts, state);
|
||||
|
||||
//go to end if bad returns from earlier
|
||||
if (state->lastsynctype == -1)
|
||||
default:
|
||||
#ifdef NXDN_DEBUG_LICH
|
||||
if (opts->payload == 1)
|
||||
fprintf(stderr, " false sync or unsupported NXDN lich type L: %02X / LH: %04X\n", lich, lich_bits_hex);
|
||||
#endif
|
||||
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
|
||||
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
voice = 0;
|
||||
goto END;
|
||||
|
||||
//collect remaining dibits at this point (after bad frame skip)
|
||||
for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
|
||||
dbuf[i+8] = getDibit(opts, state);
|
||||
break;
|
||||
} // end of switch(lich)
|
||||
|
||||
//enable these after good lich parity and known lich value
|
||||
state->carrier = 1;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
|
||||
//debug polarity (purely illustative, shows how frequently false sync can be attributed to wrong polarity)
|
||||
#ifdef NXDN_DEBUG_LICH
|
||||
if (state->lastsynctype == 28)
|
||||
fprintf (stderr, "(+) ");
|
||||
else fprintf (stderr, "(-) ");
|
||||
#endif
|
||||
|
||||
//printframesync after determining we have a good lich and it has something in it
|
||||
if (idas)
|
||||
{
|
||||
@@ -369,7 +336,14 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
#endif
|
||||
}
|
||||
|
||||
nxdn_pn95_dibit_scrambler (state, dbuf, 182);
|
||||
//now that we have a good LICH, we can collect all of our dibits
|
||||
//and push them to the proper places for decoding (if LICH calls for that type)
|
||||
for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
|
||||
{
|
||||
dbuf[i+8] = getDibit(opts, state);
|
||||
}
|
||||
|
||||
nxdn_descramble (dbuf, 182); //sizeof(dbuf)
|
||||
|
||||
//seperate our dbuf (dibit_buffer) into individual bit array
|
||||
for (int i = 0; i < 182; i++)
|
||||
@@ -380,7 +354,9 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//sacch or scch bits
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
||||
}
|
||||
|
||||
//facch
|
||||
for (int i = 0; i < 144; i++)
|
||||
@@ -391,15 +367,21 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//cac
|
||||
for (int i = 0; i < 300; i++)
|
||||
{
|
||||
cac_bits[i] = nxdn_bit_buffer[i+16];
|
||||
}
|
||||
|
||||
//udch or facch2
|
||||
for (int i = 0; i < 348; i++)
|
||||
{
|
||||
facch2_bits[i] = nxdn_bit_buffer[i+16];
|
||||
}
|
||||
|
||||
//udch2 or facch3
|
||||
for (int i = 0; i < 288; i++)
|
||||
{
|
||||
facch3_bits[i] = nxdn_bit_buffer[i+16+60];
|
||||
}
|
||||
|
||||
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
|
||||
//sacch needs extra handling depending on superframe or non-superframe variety
|
||||
@@ -483,7 +465,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
if (state->rkey_array[limazulu] != 0)
|
||||
state->R = state->rkey_array[limazulu];
|
||||
|
||||
if (state->R != 0 && state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1;
|
||||
if (state->R != 0 && state->M == 1) state->nxdn_cipher_type = 0x1;
|
||||
|
||||
//add additional time to last_sync_time for LimaZulu to hold on current frequency
|
||||
//a little longer without affecting normal scan time on trunk_hangtime variable
|
||||
@@ -539,7 +521,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
if (voice && facch == 1) //facch steal 1 -- before voice
|
||||
{
|
||||
//force scrambler here, but with unspecified key (just use what's loaded)
|
||||
if (state->forced_alg_id == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
//roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal
|
||||
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||
{
|
||||
@@ -565,38 +547,32 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
else state->nxdn_sacch_non_superframe = FALSE;
|
||||
|
||||
//TODO Later: Add Direction and/or LICH to all decoding functions
|
||||
if (scch) nxdn_deperm_scch (opts, state, sacch_bits, direction);
|
||||
|
||||
//Type-D "IDAS"
|
||||
if (scch) idas_scch(opts, state, sacch_bits, direction);
|
||||
if (udch2) idas_facch3_udch2(opts, state, facch3_bits, 0);
|
||||
if (facch3) idas_facch3_udch2(opts, state, facch3_bits, 1);
|
||||
if (udch2) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 0);
|
||||
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
|
||||
|
||||
//NXDN Conventional and Type-C
|
||||
if (sacch) nxdn_sacch(opts, state, sacch_bits);
|
||||
if (cac) nxdn_cac(opts, state, cac_bits);
|
||||
|
||||
//Duplicate facch check inside decoder. store and compare for facch message, depending on passed frame value
|
||||
if (facch == 3)
|
||||
{
|
||||
if (facch & 1) nxdn_facch1(opts, state, facch_bits_a, 1);
|
||||
if (facch & 2) nxdn_facch1(opts, state, facch_bits_b, 2);
|
||||
}
|
||||
else //facch1 steals, or single facch only messages
|
||||
{
|
||||
if (facch & 1) nxdn_facch1(opts, state, facch_bits_a, 0);
|
||||
if (facch & 2) nxdn_facch1(opts, state, facch_bits_b, 0);
|
||||
}
|
||||
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
|
||||
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
||||
|
||||
//Seperated UDCH user data from facch2 data
|
||||
if (udch) nxdn_facch2_udch(opts, state, facch2_bits, 0);
|
||||
if (facch2) nxdn_facch2_udch(opts, state, facch2_bits, 1);
|
||||
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
|
||||
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
|
||||
|
||||
//Japanese DCR
|
||||
if (sacch2) dcr_sacch(opts, state, sacch_bits);
|
||||
if (pich_tch & 1) dcr_pich_tch(opts, state, facch_bits_a, lich);
|
||||
if (pich_tch & 2) dcr_pich_tch(opts, state, facch_bits_b, lich);
|
||||
//DCR
|
||||
if (sacch2) nxdn_deperm_sacch2(opts, state, sacch_bits);
|
||||
if (pich_tch & 1) nxdn_deperm_pich_tch(opts, state, facch_bits_a);
|
||||
if (pich_tch & 2) nxdn_deperm_pich_tch(opts, state, facch_bits_b);
|
||||
|
||||
//only run facch in second slot if its not equal to the first one
|
||||
//ideally, this would work better AFTER decoding/FEC
|
||||
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
||||
if (facch & 2)
|
||||
{
|
||||
if (memcmp (facch_bits_a, facch_bits_b, 144) != 0)
|
||||
nxdn_deperm_facch(opts, state, facch_bits_b);
|
||||
}
|
||||
|
||||
//EHR only (TODO: Make an EFR mode? never observed)
|
||||
if (voice)
|
||||
{
|
||||
//restore MBE file open here
|
||||
@@ -604,7 +580,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
//update last voice sync time
|
||||
state->last_vc_sync_time = time(NULL);
|
||||
//turn on scrambler if forced by user option
|
||||
if (state->forced_alg_id == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
//process voice frame
|
||||
nxdn_voice (opts, state, voice, dbuf);
|
||||
}
|
||||
@@ -652,7 +628,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
//if rejected sync, reset carrier and synctype as well
|
||||
if (state->lastsynctype == -1)
|
||||
{
|
||||
state->carrier = 0; //this is why the "Sync: no sync" doesn't show in terminal if we have a bad sync (not an issue, just not consistent)
|
||||
state->carrier = 0;
|
||||
state->synctype = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
||||
13, 2, 12, 1, 11, 0
|
||||
};
|
||||
|
||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf)
|
||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182])
|
||||
{
|
||||
int i;
|
||||
int start, stop;
|
||||
|
||||
176
src/p25_12.c
176
src/p25_12.c
@@ -1,24 +1,22 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* p25_12.c
|
||||
* P25p1 1/2 Rate Viterbi Trellis Decoder (Improved)
|
||||
* P25p1 1/2 Rate Simple Trellis Decoder
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2026-02 DSD-FME Florida Man Edition
|
||||
* 2023-10 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include <limits.h> // For UINT8_MAX as infinity proxy
|
||||
|
||||
uint8_t p25_interleave[98] = {
|
||||
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
|
||||
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
|
||||
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
|
||||
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95
|
||||
};
|
||||
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
|
||||
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
|
||||
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
|
||||
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
|
||||
|
||||
//this is a convertion table for converting the dibit pairs into constellation points
|
||||
uint8_t p25_constellation_map[16] = {
|
||||
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||
};
|
||||
|
||||
//digitized dibit to OTA symbol conversion for reference
|
||||
@@ -27,21 +25,19 @@ uint8_t p25_constellation_map[16] = {
|
||||
|
||||
//finite state machine values
|
||||
uint8_t p25_fsm[16] = {
|
||||
0,15,12,3,
|
||||
4,11,8,7,
|
||||
13,2,1,14,
|
||||
9,6,5,10
|
||||
};
|
||||
0,15,12,3,
|
||||
4,11,8,7,
|
||||
13,2,1,14,
|
||||
9,6,5,10 };
|
||||
|
||||
//this is a dibit-pair to trellis dibit transition matrix (SDRTrunk and Ossmann)
|
||||
//when evaluating hamming distance, we want to use this xor the dibit-pair nib,
|
||||
//and not the fsm xor the constellation point
|
||||
uint8_t p25_dtm[16] = {
|
||||
2,12,1,15,
|
||||
14,0,13,3,
|
||||
9,7,10,4,
|
||||
5,11,6,8
|
||||
};
|
||||
2,12,1,15,
|
||||
14,0,13,3,
|
||||
9,7,10,4,
|
||||
5,11,6,8 };
|
||||
|
||||
int count_bits(uint8_t b, int slen)
|
||||
{
|
||||
@@ -54,13 +50,37 @@ int count_bits(uint8_t b, int slen)
|
||||
return i;
|
||||
}
|
||||
|
||||
uint8_t find_min(uint8_t list[4], int len)
|
||||
{
|
||||
int min = list[0];
|
||||
uint8_t index = 0;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < len; i++)
|
||||
{
|
||||
if (list[i] < min)
|
||||
{
|
||||
min = list[i];
|
||||
index = (uint8_t)i;
|
||||
}
|
||||
|
||||
//NOTE: Disqualifying result on uniqueness can impact decoding
|
||||
//let the CRC determine if the result is good or bad
|
||||
//its not uncommon for two values of the same min
|
||||
//distance to emerge, so its 50/50 each time
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
int p25_12(uint8_t * input, uint8_t treturn[12])
|
||||
{
|
||||
int i, t;
|
||||
int total_err = 0; UNUSED(total_err);
|
||||
int i, j;
|
||||
int irr_err = 0;
|
||||
|
||||
uint8_t deinterleaved_dibits[98];
|
||||
memset(deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
|
||||
memset (deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
|
||||
|
||||
//deinterleave our input dibits
|
||||
for (i = 0; i < 98; i++)
|
||||
@@ -68,86 +88,80 @@ int p25_12(uint8_t * input, uint8_t treturn[12])
|
||||
|
||||
//pack the input into nibbles (dibit pairs)
|
||||
uint8_t nibs[49];
|
||||
memset(nibs, 0, sizeof(nibs));
|
||||
memset (nibs, 0, sizeof(nibs));
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
|
||||
|
||||
//convert our dibit pairs into constellation point values
|
||||
uint8_t point[49];
|
||||
memset(point, 0xFF, sizeof(point));
|
||||
memset (point, 0xFF, sizeof(point));
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
point[i] = p25_constellation_map[nibs[i]];
|
||||
|
||||
// Viterbi setup: 4 states, 49 stages
|
||||
// Use UINT8_MAX / 2 as proxy for infinity (max possible errors per path ~196)
|
||||
const uint8_t INF = UINT8_MAX / 2;
|
||||
uint8_t metric[2][4]; // Ping-pong: [0] prev, [1] curr
|
||||
uint8_t path[49][4]; // path[t][s] = best prev_state to reach state s at stage t
|
||||
uint8_t tdibits[49]; // Decoded trellis dibits (states along best path)
|
||||
//debug view points
|
||||
// fprintf (stderr, "\n P =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", point[i]);
|
||||
|
||||
// Initialize: Start from state 0 before first symbol, metric 0
|
||||
int curr = 0, prev = 1;
|
||||
memset(metric, 0xFF, sizeof(metric)); // Set to high value
|
||||
memset(path, 0, sizeof(path)); // Zero-init paths
|
||||
//convert constellation points into tdibit values using the FSM
|
||||
uint8_t state = 0;
|
||||
uint8_t tdibits[49]; //trellis dibits 1/2 rate
|
||||
memset (tdibits, 0xF, sizeof(tdibits));
|
||||
uint8_t hd[4]; //array of the four fsm words hamming distance
|
||||
memset (hd, 0, sizeof(hd));
|
||||
uint8_t min = 0;
|
||||
|
||||
// Stage 0 (first symbol): From initial state 0 to each possible next_s
|
||||
memset(metric[curr], INF, sizeof(metric[0]));
|
||||
for (int next_s = 0; next_s < 4; next_s++) {
|
||||
int branch = count_bits(nibs[0] ^ p25_dtm[0 * 4 + next_s], 4);
|
||||
metric[curr][next_s] = (uint8_t)branch;
|
||||
path[0][next_s] = 0; // Prev state is initial 0
|
||||
total_err += branch; // Accumulate for return (total path errors)
|
||||
}
|
||||
for (i = 0; i < 49; i++)
|
||||
{
|
||||
|
||||
// Stages 1 to 48
|
||||
for (t = 1; t < 49; t++) {
|
||||
// Swap buffers
|
||||
prev = curr;
|
||||
curr = 1 - curr;
|
||||
memset(metric[curr], INF, sizeof(metric[0]));
|
||||
|
||||
for (int next_s = 0; next_s < 4; next_s++) {
|
||||
uint8_t min_dist = INF;
|
||||
uint8_t best_prev = 0;
|
||||
for (int prev_s = 0; prev_s < 4; prev_s++) {
|
||||
if (metric[prev][prev_s] == INF) continue;
|
||||
int branch = count_bits(nibs[t] ^ p25_dtm[prev_s * 4 + next_s], 4);
|
||||
uint8_t cand = (uint8_t)(metric[prev][prev_s] + branch);
|
||||
if (cand < min_dist) {
|
||||
min_dist = cand;
|
||||
best_prev = (uint8_t)prev_s;
|
||||
}
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
if ( p25_fsm[(state*4)+j] == point[i] )
|
||||
{
|
||||
//return our tdibit value and state for the next point
|
||||
tdibits[i] = state = (uint8_t)j;
|
||||
break;
|
||||
}
|
||||
metric[curr][next_s] = min_dist;
|
||||
path[t][next_s] = best_prev;
|
||||
}
|
||||
}
|
||||
|
||||
// Find final state with minimum metric
|
||||
uint8_t min_final = 0;
|
||||
uint8_t min_metric = metric[curr][0];
|
||||
for (int s = 1; s < 4; s++) {
|
||||
if (metric[curr][s] < min_metric) {
|
||||
min_metric = metric[curr][s];
|
||||
min_final = (uint8_t)s;
|
||||
//if tdibit value is greater than 3, then we have a decoding error
|
||||
if (tdibits[i] == 0xF)
|
||||
{
|
||||
irr_err++; //increment number of errors
|
||||
|
||||
//debug position of error and state value
|
||||
// fprintf (stderr, " %d:%d;", i, state);
|
||||
|
||||
//this method only seems to be reliable up to 1-2 bit errors...sometimes
|
||||
for (j = 0; j < 4; j++)
|
||||
hd[j] = count_bits ( ((nibs[i] ^ p25_dtm[(state*4)+j]) & 0xF ), 4 );
|
||||
min = find_min (hd, 4);
|
||||
tdibits[i] = state = min;
|
||||
|
||||
memset (hd, 0, sizeof(hd));
|
||||
min = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Traceback: Reconstruct state sequence (tdibits[t] = state after symbol t)
|
||||
tdibits[48] = min_final;
|
||||
for (t = 48; t >= 1; t--) {
|
||||
tdibits[t - 1] = path[t][tdibits[t]];
|
||||
}
|
||||
//debug view tdibits/states
|
||||
// fprintf (stderr, "\n T =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", tdibits[i]);
|
||||
|
||||
//pack tdibits into return payload bytes (use first 48 as original)
|
||||
//pack tdibits into return payload bytes
|
||||
for (i = 0; i < 12; i++)
|
||||
treturn[i] = (tdibits[(i*4)+0] << 6) | (tdibits[(i*4)+1] << 4) | (tdibits[(i*4)+2] << 2) | tdibits[(i*4)+3];
|
||||
|
||||
//debug what was the cost metric on this
|
||||
// fprintf (stderr, " R12 Min Metric = %d; ", min_metric);
|
||||
//trellis point/state err tally
|
||||
// if (irr_err != 0)
|
||||
// fprintf (stderr, " P_ERR = %d", irr_err);
|
||||
|
||||
return (irr_err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Return total Hamming errors in best path
|
||||
return (int)min_metric;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
|
||||
*/
|
||||
|
||||
//return 0 if channel value is 0 or 0xFFFF
|
||||
// if (channel == 0) return 0; //this is a valid use-case on P25 (observed) this has been in code since Feb 2024, how was this issue not discovered before now
|
||||
if (channel == 0) return 0;
|
||||
if (channel == 0xFFFF) return 0;
|
||||
|
||||
//Note: Base Frequency is calculated as (Base Frequency) x (0.000005 MHz) from the IDEN_UP message.
|
||||
|
||||
@@ -310,18 +310,6 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
|
||||
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
|
||||
{
|
||||
|
||||
//clear stale keys if loaded
|
||||
if (state->keyloader == 1)
|
||||
{
|
||||
state->R = 0;
|
||||
state->A1[0] = 0;
|
||||
state->A2[0] = 0;
|
||||
state->A3[0] = 0;
|
||||
state->A4[0] = 0;
|
||||
state->aes_key_loaded[0] = 0;
|
||||
// state->H = 0; //shim for above (this apply here?)
|
||||
}
|
||||
|
||||
//Will we need to check for a symbolrate change here, can a P25p2 TDMA-CC system
|
||||
//revert back to a phase 1 traffic channel or carry a phase 1 traffic channel?
|
||||
if (state->p25_cc_is_tdma == 1)
|
||||
@@ -506,16 +494,16 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
|
||||
fprintf (stderr, " MFIDA4 (Harris) GPS Block 1");
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
memcpy(state->dmr_pdu_sf[0], LCW_bits, 16*sizeof(uint8_t)); //opcode and mfid for check
|
||||
memcpy(state->dmr_pdu_sf[0]+16, LCW_bits+16, 56*sizeof(uint8_t)); //contents of message in this block
|
||||
memcpy(state->dmr_pdu_sf[0]+40, LCW_bits+16, 56*sizeof(uint8_t)); //+40 offset to match the vPDU decoder
|
||||
}
|
||||
|
||||
else if (lc_mfid == 0xA4 && lc_opcode == 0x2B)
|
||||
{
|
||||
fprintf (stderr, " MFIDA4 (Harris) GPS Block 2");
|
||||
memcpy(state->dmr_pdu_sf[0]+16+56, LCW_bits+16, 56*sizeof(uint8_t)); //+16 +56 to offset first block
|
||||
memcpy(state->dmr_pdu_sf[0]+40+56, LCW_bits+16, 56*sizeof(uint8_t)); //+40 +56 to offset first block
|
||||
uint16_t check = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[0][0], 16);
|
||||
if (check == 0x2AA4)
|
||||
harris_lptt(opts, state, state->dmr_pdu_sf[0]+16, (uint32_t)state->lastsrc, 0, 1);
|
||||
nmea_harris(opts, state, state->dmr_pdu_sf[0], (uint32_t)state->lastsrc, 0);
|
||||
else fprintf (stderr, " Missing GPS Block 1");
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
}
|
||||
@@ -538,15 +526,6 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
|
||||
tait_iso7_embedded_alias_decode(opts, state, 0, 8, LCW_bits);
|
||||
}
|
||||
|
||||
//observed alongside Tait Talker Alias ID: 10T
|
||||
else if (lc_mfid == 0xD8 && lc_format == 0x01)
|
||||
{
|
||||
uint32_t wacn = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 20);
|
||||
uint32_t sys = (uint32_t)ConvertBitIntoBytes(&LCW_bits[36], 12);
|
||||
uint32_t rid = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||
fprintf (stderr, " MFIDD8 (Tait) Subscriber FQ-SUID: %05X.%03X.%d", wacn, sys, rid);
|
||||
}
|
||||
|
||||
//not a duplicate, this one will print if not MFID 0 or 1
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,138 +1,17 @@
|
||||
// BCH decoder implementation from OP25 (Copyright 2010, KA1RBI, GPL license)
|
||||
#include "dsd.h"
|
||||
#include <itpp/itcomm.h>
|
||||
|
||||
#include <vector>
|
||||
typedef std::vector<bool> bit_vector;
|
||||
#include "p25p1_check_nid.h"
|
||||
|
||||
static const int bchGFexp[64] = {
|
||||
1, 2, 4, 8, 16, 32, 3, 6, 12, 24, 48, 35, 5, 10, 20, 40,
|
||||
19, 38, 15, 30, 60, 59, 53, 41, 17, 34, 7, 14, 28, 56, 51, 37,
|
||||
9, 18, 36, 11, 22, 44, 27, 54, 47, 29, 58, 55, 45, 25, 50, 39,
|
||||
13, 26, 52, 43, 21, 42, 23, 46, 31, 62, 63, 61, 57, 49, 33, 0
|
||||
};
|
||||
|
||||
static const int bchGFlog[64] = {
|
||||
-1, 0, 1, 6, 2, 12, 7, 26, 3, 32, 13, 35, 8, 48, 27, 18,
|
||||
4, 24, 33, 16, 14, 52, 36, 54, 9, 45, 49, 38, 28, 41, 19, 56,
|
||||
5, 62, 25, 11, 34, 31, 17, 47, 15, 23, 53, 51, 37, 44, 55, 40,
|
||||
10, 61, 46, 30, 50, 22, 39, 43, 29, 60, 42, 21, 20, 59, 57, 58
|
||||
};
|
||||
|
||||
int bchDec(bit_vector& Codeword)
|
||||
{
|
||||
int elp[24][22], S[23];
|
||||
int D[23], L[24], uLu[24];
|
||||
int locn[11], reg[12];
|
||||
int i, j, U, q, count;
|
||||
int SynError, CantDecode;
|
||||
using namespace itpp;
|
||||
|
||||
SynError = 0; CantDecode = 0;
|
||||
// Ideas taken from http://op25.osmocom.org/trac/wiki.png/browser/op25/gr-op25/lib/decoder_ff_impl.cc
|
||||
// See also p25_training_guide.pdf page 48.
|
||||
// See also tia-102-baaa-a-project_25-fdma-common_air_interface.pdf page 40.
|
||||
// BCH encoder/decoder implementation from IT++. GNU GPL 3 license.
|
||||
|
||||
for (i = 1; i <= 22; i++) {
|
||||
S[i] = 0;
|
||||
for (j = 0; j <= 62; j++) {
|
||||
if (Codeword[j]) {
|
||||
S[i] = S[i] ^ bchGFexp[(i * j) % 63];
|
||||
}
|
||||
}
|
||||
if (S[i]) { SynError = 1; }
|
||||
S[i] = bchGFlog[S[i]];
|
||||
}
|
||||
|
||||
if (SynError) { // if there are errors, try to correct them
|
||||
L[0] = 0; uLu[0] = -1; D[0] = 0; elp[0][0] = 0;
|
||||
L[1] = 0; uLu[1] = 0; D[1] = S[1]; elp[1][0] = 1;
|
||||
for (i = 1; i <= 21; i++) {
|
||||
elp[0][i] = -1; elp[1][i] = 0;
|
||||
}
|
||||
U = 0;
|
||||
|
||||
do {
|
||||
U = U + 1;
|
||||
if (D[U] == -1) {
|
||||
L[U + 1] = L[U];
|
||||
for (i = 0; i <= L[U]; i++) {
|
||||
elp[U + 1][i] = elp[U][i];
|
||||
elp[U][i] = bchGFlog[elp[U][i]];
|
||||
}
|
||||
} else {
|
||||
q = U - 1;
|
||||
while ((D[q] == -1) && (q > 0)) { q = q - 1; }
|
||||
if (q > 0) {
|
||||
j = q;
|
||||
do { j = j - 1; if ((D[j] != -1) && (uLu[q] < uLu[j])) { q = j; }
|
||||
} while (j > 0);
|
||||
}
|
||||
|
||||
if (L[U] > L[q] + U - q) {
|
||||
L[U + 1] = L[U];
|
||||
} else {
|
||||
L[U + 1] = L[q] + U - q;
|
||||
}
|
||||
|
||||
for (i = 0; i <= 21; i++) {
|
||||
elp[U + 1][i] = 0;
|
||||
}
|
||||
for (i = 0; i <= L[q]; i++) {
|
||||
if (elp[q][i] != -1) {
|
||||
elp[U + 1][i + U - q] = bchGFexp[(D[U] + 63 - D[q] + elp[q][i]) % 63];
|
||||
}
|
||||
}
|
||||
for (i = 0; i <= L[U]; i++) {
|
||||
elp[U + 1][i] = elp[U + 1][i] ^ elp[U][i];
|
||||
elp[U][i] = bchGFlog[elp[U][i]];
|
||||
}
|
||||
}
|
||||
uLu[U + 1] = U - L[U + 1];
|
||||
|
||||
if (U < 22) {
|
||||
if (S[U + 1] != -1) { D[U + 1] = bchGFexp[S[U + 1]]; } else { D[U + 1] = 0; }
|
||||
for (i = 1; i <= L[U + 1]; i++) {
|
||||
if ((S[U + 1 - i] != -1) && (elp[U + 1][i] != 0)) {
|
||||
D[U + 1] = D[U + 1] ^ bchGFexp[(S[U + 1 - i] + bchGFlog[elp[U + 1][i]]) % 63];
|
||||
}
|
||||
}
|
||||
D[U + 1] = bchGFlog[D[U + 1]];
|
||||
}
|
||||
} while ((U < 22) && (L[U + 1] <= 11));
|
||||
|
||||
U = U + 1;
|
||||
if (L[U] <= 11) {
|
||||
for (i = 0; i <= L[U]; i++) {
|
||||
elp[U][i] = bchGFlog[elp[U][i]];
|
||||
}
|
||||
|
||||
for (i = 1; i <= L[U]; i++) {
|
||||
reg[i] = elp[U][i];
|
||||
}
|
||||
count = 0;
|
||||
for (i = 1; i <= 63; i++) {
|
||||
q = 1;
|
||||
for (j = 1; j <= L[U]; j++) {
|
||||
if (reg[j] != -1) {
|
||||
reg[j] = (reg[j] + j) % 63;
|
||||
q = q ^ bchGFexp[reg[j]];
|
||||
}
|
||||
}
|
||||
if (q == 0) {
|
||||
locn[count] = 63 - i;
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
if (count == L[U]) {
|
||||
for (i = 0; i <= L[U] - 1; i++) {
|
||||
Codeword[locn[i]] = Codeword[locn[i]] ^ 1;
|
||||
}
|
||||
CantDecode = count;
|
||||
} else {
|
||||
CantDecode = -1;
|
||||
}
|
||||
} else {
|
||||
CantDecode = -2;
|
||||
}
|
||||
}
|
||||
return CantDecode;
|
||||
}
|
||||
itpp::BCH bch(63, 16, 11, "6 3 3 1 1 4 1 3 6 7 2 3 5 4 5 3", true);
|
||||
|
||||
/**
|
||||
* Convenience class to calculate the parity of the DUID values. Keeps a table with the expected outcomes
|
||||
@@ -145,13 +24,13 @@ private:
|
||||
|
||||
unsigned char get_index(unsigned char x, unsigned char y)
|
||||
{
|
||||
return (x << 2) + y;
|
||||
return (x<<2) + y;
|
||||
}
|
||||
|
||||
public:
|
||||
ParityTable()
|
||||
{
|
||||
for (unsigned int i = 0; i < sizeof(table); i++) {
|
||||
for (unsigned int i=0; i<sizeof(table); i++) {
|
||||
table[i] = 0;
|
||||
}
|
||||
table[get_index(1,1)] = 1;
|
||||
@@ -160,8 +39,9 @@ public:
|
||||
|
||||
unsigned char get_value(unsigned char x, unsigned char y)
|
||||
{
|
||||
return table[get_index(x, y)];
|
||||
return table[get_index(x,y)];
|
||||
}
|
||||
|
||||
} parity_table;
|
||||
|
||||
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity)
|
||||
@@ -169,38 +49,31 @@ int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity
|
||||
int result;
|
||||
|
||||
// Fill up with the given input
|
||||
bit_vector codeword(63);
|
||||
for (unsigned int i = 0; i < 63; i++) {
|
||||
codeword[i] = bch_code[62-i];
|
||||
bvec input(63);
|
||||
for(unsigned int i=0; i<63; i++) {
|
||||
input[i] = bch_code[i];
|
||||
}
|
||||
|
||||
// Decode it
|
||||
int errors = bchDec(codeword);
|
||||
bool ok = (errors >= 0);
|
||||
bvec decoded, cw_isvalid;
|
||||
bool ok = bch.decode(input, decoded, cw_isvalid);
|
||||
|
||||
if (!ok) {
|
||||
// Decode failed
|
||||
// fprintf (stderr, "NID ERR: %d; ", errors);
|
||||
result = 0;
|
||||
} else {
|
||||
//reload the codeword back into correct bit order
|
||||
for (unsigned int i = 0; i < 63; i++) {
|
||||
bch_code[i] = codeword[62-i];
|
||||
}
|
||||
for (unsigned int i = 0; i < 63; i++) {
|
||||
codeword[i] = bch_code[i];
|
||||
}
|
||||
// Take the NAC from the decoded output. It's a 12 bit number starting from position 0.
|
||||
// Just convert the output from a binary sequence to an integer.
|
||||
int nac = 0;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
for (int i=0; i<12; i++) {
|
||||
nac <<= 1;
|
||||
nac |= (int)codeword[i];
|
||||
nac |= (int)decoded[i];
|
||||
}
|
||||
*new_nac = nac;
|
||||
|
||||
// Take the fixed DUID from the encoded output. 4 bit value starting at position 12.
|
||||
unsigned char new_duid_0 = (((int)codeword[12]) << 1) + ((int)codeword[13]);
|
||||
unsigned char new_duid_1 = (((int)codeword[14]) << 1) + ((int)codeword[15]);
|
||||
unsigned char new_duid_0 = (((int)decoded[12])<<1) + ((int)decoded[13]);
|
||||
unsigned char new_duid_1 = (((int)decoded[14])<<1) + ((int)decoded[15]);
|
||||
new_duid[0] = new_duid_0 + '0';
|
||||
new_duid[1] = new_duid_1 + '0';
|
||||
new_duid[2] = 0; // Null terminate
|
||||
@@ -209,12 +82,12 @@ int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity
|
||||
unsigned char expected_parity = parity_table.get_value(new_duid_0, new_duid_1);
|
||||
|
||||
if (expected_parity != parity) {
|
||||
// Parity mismatch – optional handling
|
||||
// fprintf(stderr, "Error in parity detected?\n");
|
||||
// Ignore, not sure what to do
|
||||
//fprintf (stderr, "Error in parity detected?");
|
||||
}
|
||||
|
||||
result = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ void processMPDU(dsd_opts * opts, dsd_state * state)
|
||||
//debug
|
||||
// fprintf (stderr, " J:%d;", j); //use this with the P_ERR inside of 34 rate decoder to see where the failures occur
|
||||
|
||||
ec[j] = viterbi_r34 (tsbk_dibit, r34byte_b);
|
||||
ec[j] = dmr_34 (tsbk_dibit, r34byte_b);
|
||||
|
||||
//shuffle 34 rate data into array
|
||||
if (j != 0) //should never happen, but just in case
|
||||
|
||||
@@ -457,18 +457,8 @@ void p25_decode_pdu_data(dsd_opts * opts, dsd_state * state, uint8_t * input, in
|
||||
if (sap == 0 || sap == 4) //User Data or Packet Data (both are UDP typically, same format dmr UDP/IP data)
|
||||
decode_ip_pdu (opts, state, len+1, input+ptr);
|
||||
|
||||
//NMEA Tier 1 Location Reporting w/ Supplimentary $PP25 string
|
||||
else if (sap == 48)
|
||||
{
|
||||
len += 12; //not sure how we ended up off by up to 12 on this, above negation too much?
|
||||
state->dmr_lrrp_source[state->currentslot] = state->lastsrc;
|
||||
state->dmr_lrrp_source[state->currentslot] = state->lasttg;
|
||||
uint8_t input_bits[(len+1)*8];
|
||||
memset(input_bits, 0, sizeof(input_bits));
|
||||
unpack_byte_array_into_bit_array(input, input_bits, len);
|
||||
fprintf (stderr, "\n ");
|
||||
nmea_sentence_checker(opts, state, input_bits+ptr*8, state->currentslot, len*8);
|
||||
}
|
||||
else if (sap == 48) //Tier 1 Location Service (or does it depend on the io bit?)
|
||||
utf8_to_text(state, 1, len-ptr+1, input+ptr); //TODO, read initial string, i.e., $GPRMC and properly decode
|
||||
|
||||
// else //default catch all (debug only)
|
||||
// {
|
||||
|
||||
@@ -489,53 +489,11 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
fprintf (stderr, " MFID 90 (Moto) Acknowledge Response: ");
|
||||
fprintf (stderr, " MFID 90 (Moto) Acknoledge Response: ");
|
||||
for (i = 2; i < 10; i++)
|
||||
fprintf (stderr, "%02X", tsbk_byte[i]);
|
||||
}
|
||||
|
||||
else if ( (tsbk_byte[0] & 0x3F) == 0x09 )
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " MFID 90 (Moto) Scan Marker: ");
|
||||
for (i = 2; i < 10; i++)
|
||||
fprintf (stderr, "%02X", tsbk_byte[i]);
|
||||
}
|
||||
|
||||
else if ( (tsbk_byte[0] & 0x3F) == 0x0B )
|
||||
{
|
||||
|
||||
uint16_t channel = (tsbk_byte[8] << 8) | tsbk_byte[9];
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " MFID 90 (Moto) ID Channel: %04X;", channel);
|
||||
char sid_string[10];
|
||||
memset(sid_string, 0, sizeof(sid_string));
|
||||
int k = 0;
|
||||
|
||||
if (tsbk_byte[2] != 0)
|
||||
{
|
||||
fprintf (stderr, " Station ID: ");
|
||||
uint8_t sid_bits[42];
|
||||
memset(sid_bits, 0, sizeof(sid_bits));
|
||||
unpack_byte_array_into_bit_array(tsbk_byte+2, sid_bits, 6);
|
||||
for (i = 0; i < 42; i+=6)
|
||||
{
|
||||
uint8_t bsi_char = convert_bits_into_output(sid_bits+i, 6) + 43;
|
||||
if (bsi_char > 0x1F && bsi_char < 0x7F)
|
||||
{
|
||||
fprintf (stderr, "%c", bsi_char);
|
||||
sid_string[k++] = bsi_char;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
//debug, may assign this string to something else later on
|
||||
// fprintf (stderr, " - Str: %s;", sid_string);
|
||||
|
||||
process_channel_to_freq(opts, state, channel);
|
||||
}
|
||||
|
||||
|
||||
//Some of these Opcodes that aren't found in any TIA manual come from SDRTrunk (or other sources),
|
||||
//but can't verify the accuracy of their meaning/context
|
||||
@@ -547,6 +505,22 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
// fprintf (stderr, "%02X", tsbk_byte[i]);
|
||||
// }
|
||||
|
||||
// else if ( (tsbk_byte[0] & 0x3F) == 0x09 )
|
||||
// {
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, " MFID 90 (Moto) Channel Loading: "); //don't understand the context for this one, waiting on units to arrive on channel?
|
||||
// for (i = 2; i < 10; i++)
|
||||
// fprintf (stderr, "%02X", tsbk_byte[i]);
|
||||
// }
|
||||
|
||||
// else if ( (tsbk_byte[0] & 0x3F) == 0x0B )
|
||||
// {
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, " MFID 90 (Moto) Control Channel: "); //this appears to echo the 16-bit channel number for the main control channel/RFSS
|
||||
// for (i = 2; i < 10; i++)
|
||||
// fprintf (stderr, "%02X", tsbk_byte[i]);
|
||||
// }
|
||||
|
||||
// else if ( (tsbk_byte[0] & 0x3F) == 0x0E )
|
||||
// {
|
||||
// fprintf (stderr, "\n");
|
||||
@@ -563,6 +537,14 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
// fprintf (stderr, "%02X", tsbk_byte[i]);
|
||||
// }
|
||||
|
||||
// else if ( (tsbk_byte[0] & 0x3F) == 0x15 ) //noted on RR, but not observed as of yet, this may actualy be a LCW and not a TSBK?
|
||||
// {
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, " MFID 90 (Moto) Talker Alias: ");
|
||||
// for (i = 2; i < 10; i++)
|
||||
// fprintf (stderr, "%02X", tsbk_byte[i]);
|
||||
// }
|
||||
|
||||
// else if ( (tsbk_byte[0] & 0x3F) == 0x16 )
|
||||
// {
|
||||
// fprintf (stderr, "\n");
|
||||
|
||||
@@ -1628,79 +1628,10 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
|
||||
}
|
||||
|
||||
//Extended Function Command - Abbreviated (Generic non MFID variant)
|
||||
if (MAC[1+len_a] == 0x64)
|
||||
{
|
||||
|
||||
uint8_t class = MAC[2+len_a];
|
||||
uint8_t operand = MAC[3+len_a];
|
||||
uint32_t argument = (MAC[4+len_a] << 16) | (MAC[5+len_a] << 8) | MAC[6+len_a];
|
||||
uint32_t target = (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | MAC[9+len_a];
|
||||
fprintf (stderr, "\n Extended Function Command: Class: %02X; Operand: %02X; Arg/Src: %06X; Target: %d; ", class, operand, argument, target);
|
||||
//WIP: Further Decode
|
||||
if (class == 0)
|
||||
{
|
||||
if ((operand & 0x7F) == 0x00) fprintf (stderr, "Radio Check; ");
|
||||
else if ((operand & 0x7F) == 0x7D) fprintf (stderr, "Radio Detach; ");
|
||||
else if ((operand & 0x7F) == 0x7E) fprintf (stderr, "Radio Uninhibit; ");
|
||||
else if ((operand & 0x7F) == 0x7F) fprintf (stderr, "Radio Inhibit; ");
|
||||
else fprintf (stderr, "Reserved; ");
|
||||
|
||||
//MSB is an ack of the above command
|
||||
if ((operand >> 7) == 1) fprintf (stderr, "Ack; ");
|
||||
}
|
||||
else fprintf (stderr, "Other Command; ");
|
||||
|
||||
}
|
||||
|
||||
//MFID90 Group Regroup Add Command
|
||||
if (MAC[1+len_a] == 0x81 && MAC[2+len_a] == 0x90)
|
||||
if (MAC[1+len_a] == 0x81 && MAC[2+len_a] == 0x90) //needs MAC message len update, may work same as explicit enc regroup?
|
||||
{
|
||||
fprintf (stderr, "\n MFID90 Group Regroup Add Command ");
|
||||
uint8_t len = MAC[3+len_a];
|
||||
len_b = len;
|
||||
}
|
||||
|
||||
//MFID90 Unknown Message <--was causing an issue on ABQ system w/ false GPS, SCCB and RFSS_STS_BCST MAC messages
|
||||
if (MAC[1+len_a] == 0x8F && MAC[2+len_a] == 0x90)
|
||||
{
|
||||
|
||||
uint8_t len = MAC[3+len_a]; //observed len values are 0x04, 0x08, and 0x0B (12)
|
||||
|
||||
//constraints on message len
|
||||
if (len > 12)
|
||||
len = 12;
|
||||
// else if (len < 4)
|
||||
// len = 4;
|
||||
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, "\n MFID 90 (Moto); Opcode: %02llX; ", MAC[1+len_a] & 0x3F); //mask, or not?
|
||||
for (int i = 4; i < len+1; i++)
|
||||
fprintf (stderr, "%02llX", MAC[i+len_a]);
|
||||
fprintf (stderr, " %s",KNRM);
|
||||
|
||||
len_b = len;
|
||||
}
|
||||
|
||||
//MFID90 Unknown Message 2 (usually in second message, unknown, maybe just filler?)
|
||||
if (MAC[1+len_a] == 0xBF && MAC[2+len_a] == 0x90)
|
||||
{
|
||||
|
||||
uint8_t len = MAC[3+len_a]; //observed len values are 0x03
|
||||
|
||||
//constraints on message len
|
||||
if (len > 3)
|
||||
len = 3;
|
||||
else if (len < 3)
|
||||
len = 3;
|
||||
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, "\n MFID 90 (Moto); Opcode: %02llX; ", MAC[1+len_a] & 0x3F); //mask, or not?
|
||||
for (int i = 4; i < len+1; i++)
|
||||
fprintf (stderr, "%02llX", MAC[i+len_a]);
|
||||
fprintf (stderr, " %s",KNRM);
|
||||
|
||||
len_b = len;
|
||||
}
|
||||
|
||||
//the len on these indicate they are always a single messages, foregoing the +len_a index pointer
|
||||
@@ -1841,15 +1772,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (MAC[len_a+1] == 0x80 && MAC[len_a+2] != 0xA4 && MAC[len_a+2] != 0x90)
|
||||
{
|
||||
int unk1 = MAC[len_a+1]; //assuming this is the octet set for the 'manufacturer specific' message, may only be the MSBit
|
||||
int unk2 = MAC[len_a+2]; //This field is observed as 0xAA, its possible its a derived message of 0x2A from P25p1 LCW format
|
||||
int unk2 = MAC[len_a+2]; //This field is observed as 0xAA, unknown if this is an opcode, or other MFID
|
||||
int mfid = MAC[len_a+3]; //This is where the 0xA4 (Harris) Identifier is found in this message, as opposed to +2
|
||||
int len = MAC[len_a+4] & 0x3F; //len of this message (should always be 17)
|
||||
|
||||
//bugfix observed on random errant second MAC message on Phase 2
|
||||
//confirmed this MAC message is always 17 (0x11)
|
||||
if (len != 0x11)
|
||||
goto END_PDU;
|
||||
|
||||
int len = MAC[len_a+4] & 0x3F;; //0x11 or 17 dec sounds reasonable, but cannot verify
|
||||
fprintf (stderr, "\n MFID %02X (Harris); Len: %d; Opcode: %02X/%02X;", mfid, len, unk1, unk2);
|
||||
|
||||
//convert bytes to bits, may move this up top
|
||||
@@ -1866,7 +1791,8 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (slot == 0 && state->lastsrc != 0) tsrc = state->lastsrc;
|
||||
if (slot == 1 && state->lastsrcR != 0) tsrc = state->lastsrcR;
|
||||
|
||||
harris_lptt (opts, state, mac_bits+40, tsrc, slot, 2);
|
||||
// harris_gps (opts, state, slot, mac_bits); //fallback
|
||||
nmea_harris (opts, state, mac_bits+0, tsrc, slot); //new
|
||||
|
||||
//debug - just dump payload
|
||||
// for (i = 0; i < 24; i++)
|
||||
|
||||
@@ -317,18 +317,6 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
||||
if (opts->floating_point == 1)
|
||||
state->aout_gain = opts->audio_gain;
|
||||
|
||||
//clear stale keys if loaded
|
||||
if (state->keyloader == 1)
|
||||
{
|
||||
state->R = 0;
|
||||
state->A1[0] = 0;
|
||||
state->A2[0] = 0;
|
||||
state->A3[0] = 0;
|
||||
state->A4[0] = 0;
|
||||
state->aes_key_loaded[0] = 0;
|
||||
// state->H = 0; //shim for above (this apply here?)
|
||||
}
|
||||
|
||||
}
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
@@ -358,18 +346,6 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
||||
if (opts->floating_point == 1)
|
||||
state->aout_gainR = opts->audio_gain;
|
||||
|
||||
//clear stale keys if loaded
|
||||
if (state->keyloader == 1)
|
||||
{
|
||||
state->RR = 0;
|
||||
state->A1[1] = 0;
|
||||
state->A2[1] = 0;
|
||||
state->A3[1] = 0;
|
||||
state->A4[1] = 0;
|
||||
state->aes_key_loaded[1] = 0;
|
||||
// state->H = 0; //shim for above (this apply here?)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Return to CC on MAC_END_PTT if other slot is idle
|
||||
@@ -784,18 +760,6 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
||||
if (opts->floating_point == 1)
|
||||
state->aout_gain = opts->audio_gain; //reset
|
||||
|
||||
//clear stale keys if loaded
|
||||
if (state->keyloader == 1)
|
||||
{
|
||||
state->R = 0;
|
||||
state->A1[0] = 0;
|
||||
state->A2[0] = 0;
|
||||
state->A3[0] = 0;
|
||||
state->A4[0] = 0;
|
||||
state->aes_key_loaded[0] = 0;
|
||||
// state->H = 0; //shim for above (this apply here?)
|
||||
}
|
||||
|
||||
}
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
@@ -821,19 +785,6 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
||||
//reset gain
|
||||
if (opts->floating_point == 1)
|
||||
state->aout_gainR = opts->audio_gain;
|
||||
|
||||
//clear stale keys if loaded
|
||||
if (state->keyloader == 1)
|
||||
{
|
||||
state->RR = 0;
|
||||
state->A1[1] = 0;
|
||||
state->A2[1] = 0;
|
||||
state->A3[1] = 0;
|
||||
state->A4[1] = 0;
|
||||
state->aes_key_loaded[1] = 0;
|
||||
// state->H = 0; //shim for above (this apply here?)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Return to CC on MAC_END_PTT if other slot is idle
|
||||
|
||||
3555
src/rtl_sdr_fm.cpp
3555
src/rtl_sdr_fm.cpp
File diff suppressed because it is too large
Load Diff
4632
src/sjis_unicode.c
4632
src/sjis_unicode.c
File diff suppressed because it is too large
Load Diff
@@ -1,541 +0,0 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* soft_viterbi_k5.c
|
||||
* Soft Decision Viterbi K5 Contraint for M17, NXDN, and YSF Fusion
|
||||
*
|
||||
* Viterbi Code from or Modified from libM17 (see bottom)
|
||||
* Wojciech Kaczmarski, SP5WWP
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2026-01 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
//Ripped from libM17
|
||||
#define K 5 //constraint length
|
||||
#define NUM_STATES (1 << (K - 1)) //number of states
|
||||
|
||||
static uint32_t prevMetrics[NUM_STATES];
|
||||
static uint32_t currMetrics[NUM_STATES];
|
||||
static uint32_t prevMetricsData[NUM_STATES];
|
||||
static uint32_t currMetricsData[NUM_STATES];
|
||||
static uint16_t viterbi_history[600*2];
|
||||
|
||||
/**
|
||||
* @brief Decode unpunctured convolutionally encoded data.
|
||||
*
|
||||
* @param out Destination array where decoded data is written.
|
||||
* @param in Input data.
|
||||
* @param len Input length in bits.
|
||||
* @return Number of bit errors corrected.
|
||||
*/
|
||||
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
|
||||
{
|
||||
|
||||
viterbi_reset();
|
||||
|
||||
size_t pos = 0;
|
||||
for(size_t i = 0; i < len; i += 2)
|
||||
{
|
||||
uint16_t s0 = in[i];
|
||||
uint16_t s1 = in[i + 1];
|
||||
|
||||
viterbi_decode_bit(s0, s1, pos);
|
||||
pos++;
|
||||
}
|
||||
uint32_t err = viterbi_chainback(out, pos, len/2);
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "\n vcb: \n");
|
||||
// for (size_t i = 0; i < len; i++)
|
||||
// fprintf (stderr, "%02X", out[i]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode punctured convolutionally encoded data.
|
||||
*
|
||||
* @param out Destination array where decoded data is written.
|
||||
* @param in Input data.
|
||||
* @param punct Puncturing matrix.
|
||||
* @param in_len Input data length.
|
||||
* @param p_len Puncturing matrix length (entries).
|
||||
* @return Number of bit errors corrected.
|
||||
*/
|
||||
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
|
||||
{
|
||||
|
||||
uint16_t umsg[600*2]; //unpunctured message
|
||||
uint16_t p=0; //puncturer matrix entry
|
||||
uint16_t u=0; //bits count - unpunctured message
|
||||
uint16_t i=0; //bits read from the input message
|
||||
|
||||
while(i<in_len)
|
||||
{
|
||||
if(punct[p])
|
||||
{
|
||||
umsg[u]=in[i];
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
umsg[u]=0x7FFF;
|
||||
}
|
||||
|
||||
u++;
|
||||
p++;
|
||||
p%=p_len;
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " p: %d, u: %d; p_len: %d; ", p, u, p_len); //
|
||||
|
||||
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode one bit and update trellis.
|
||||
*
|
||||
* @param s0 Cost of the first symbol.
|
||||
* @param s1 Cost of the second symbol.
|
||||
* @param pos Bit position in history.
|
||||
*/
|
||||
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos)
|
||||
{
|
||||
static const uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
|
||||
static const uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0, 0, 0xFFFF, 0xFFFF, 0};
|
||||
|
||||
for(uint8_t i = 0; i < NUM_STATES/2; i++)
|
||||
{
|
||||
uint32_t metric = q_abs_diff(COST_TABLE_0[i], s0)
|
||||
+ q_abs_diff(COST_TABLE_1[i], s1);
|
||||
|
||||
|
||||
uint32_t m0 = prevMetrics[i] + metric;
|
||||
uint32_t m1 = prevMetrics[i + NUM_STATES/2] + (0x1FFFE - metric);
|
||||
|
||||
uint32_t m2 = prevMetrics[i] + (0x1FFFE - metric);
|
||||
uint32_t m3 = prevMetrics[i + NUM_STATES/2] + metric;
|
||||
|
||||
uint8_t i0 = 2 * i;
|
||||
uint8_t i1 = i0 + 1;
|
||||
|
||||
if(m0 >= m1)
|
||||
{
|
||||
viterbi_history[pos]|=(1<<i0);
|
||||
currMetrics[i0] = m1;
|
||||
}
|
||||
else
|
||||
{
|
||||
viterbi_history[pos]&=~(1<<i0);
|
||||
currMetrics[i0] = m0;
|
||||
}
|
||||
|
||||
if(m2 >= m3)
|
||||
{
|
||||
viterbi_history[pos]|=(1<<i1);
|
||||
currMetrics[i1] = m3;
|
||||
}
|
||||
else
|
||||
{
|
||||
viterbi_history[pos]&=~(1<<i1);
|
||||
currMetrics[i1] = m2;
|
||||
}
|
||||
}
|
||||
|
||||
//swap
|
||||
uint32_t tmp[NUM_STATES];
|
||||
for(uint8_t i=0; i<NUM_STATES; i++)
|
||||
{
|
||||
tmp[i]=currMetrics[i];
|
||||
}
|
||||
for(uint8_t i=0; i<NUM_STATES; i++)
|
||||
{
|
||||
currMetrics[i]=prevMetrics[i];
|
||||
prevMetrics[i]=tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief History chainback to obtain final byte array.
|
||||
*
|
||||
* @param out Destination byte array for decoded data.
|
||||
* @param pos Starting position for the chainback.
|
||||
* @param len Length of the output in bits.
|
||||
* @return Minimum Viterbi cost at the end of the decode sequence.
|
||||
*/
|
||||
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
|
||||
{
|
||||
|
||||
uint8_t state = 0;
|
||||
size_t bitPos = len+4;
|
||||
|
||||
while(pos > 0)
|
||||
{
|
||||
bitPos--;
|
||||
pos--;
|
||||
uint16_t bit = viterbi_history[pos]&((1<<(state>>4)));
|
||||
state >>= 1;
|
||||
if(bit)
|
||||
{
|
||||
state |= 0x80;
|
||||
out[bitPos/8]|=1<<(7-(bitPos%8));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cost = prevMetrics[0];
|
||||
|
||||
for(size_t i = 0; i < NUM_STATES; i++)
|
||||
{
|
||||
uint32_t m = prevMetrics[i];
|
||||
if(m < cost) cost = m;
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "\n cb: \n");
|
||||
// for (size_t i = 0; i < len; i++)
|
||||
// fprintf (stderr, "%02X", out[i]);
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the decoder state. No args.
|
||||
*
|
||||
*/
|
||||
void viterbi_reset(void)
|
||||
{
|
||||
memset(viterbi_history, 0, sizeof(viterbi_history));
|
||||
memset(currMetrics, 0, sizeof(currMetrics));
|
||||
memset(prevMetrics, 0, sizeof(prevMetrics));
|
||||
memset(currMetricsData, 0, sizeof(currMetricsData));
|
||||
memset(prevMetricsData, 0, sizeof(prevMetricsData));
|
||||
}
|
||||
|
||||
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
|
||||
{
|
||||
if(v2 > v1) return v2 - v1;
|
||||
return v1 - v2;
|
||||
}
|
||||
|
||||
//same as symbol levels in m17.h (m17-fme version)
|
||||
const int8_t symbol_list[4]={-3, -1, +1, +3};
|
||||
|
||||
/**
|
||||
* @brief Slice payload symbols into soft dibits.
|
||||
* Input (RRC filtered baseband sampled at symbol centers)
|
||||
* should be already normalized to {-3, -1, +1 +3}.
|
||||
* @param out Soft valued dibits (type-4).
|
||||
* @param inp Array of 184 floats (1 sample per symbol).
|
||||
*/
|
||||
void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD])
|
||||
{
|
||||
|
||||
for(uint8_t i=0; i<SYM_PER_PLD; i++)
|
||||
{
|
||||
//bit 0
|
||||
if(inp[i]>=symbol_list[3])
|
||||
{
|
||||
out[i*2+1]=0xFFFF;
|
||||
}
|
||||
else if(inp[i]>=symbol_list[2])
|
||||
{
|
||||
out[i*2+1]=-(float)0xFFFF/(symbol_list[3]-symbol_list[2])*symbol_list[2]+inp[i]*(float)0xFFFF/(symbol_list[3]-symbol_list[2]);
|
||||
}
|
||||
else if(inp[i]>=symbol_list[1])
|
||||
{
|
||||
out[i*2+1]=0x0000;
|
||||
}
|
||||
else if(inp[i]>=symbol_list[0])
|
||||
{
|
||||
out[i*2+1]=(float)0xFFFF/(symbol_list[1]-symbol_list[0])*symbol_list[1]-inp[i]*(float)0xFFFF/(symbol_list[1]-symbol_list[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
out[i*2+1]=0xFFFF;
|
||||
}
|
||||
|
||||
//bit 1
|
||||
if(inp[i]>=symbol_list[2])
|
||||
{
|
||||
out[i*2]=0x0000;
|
||||
}
|
||||
else if(inp[i]>=symbol_list[1])
|
||||
{
|
||||
out[i*2]=0x7FFF-inp[i]*(float)0xFFFF/(symbol_list[2]-symbol_list[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
out[i*2]=0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Slice payload symbols into soft dibits.
|
||||
* Input (RRC filtered baseband sampled at symbol centers)
|
||||
* should be already normalized to {-3, -1, +1 +3}.
|
||||
* @param out Soft valued dibits (type-4).
|
||||
* @param inp Array of len floats (1 sample per symbol).
|
||||
* @param len legnth value to run this at
|
||||
*/
|
||||
void slice_symbols_to_len(uint16_t * out, const float * inp, int len)
|
||||
{
|
||||
|
||||
for(int i=0; i<len; i++)
|
||||
{
|
||||
//bit 0
|
||||
if(inp[i]>=symbol_list[3])
|
||||
{
|
||||
out[i*2+1]=0xFFFF;
|
||||
}
|
||||
else if(inp[i]>=symbol_list[2])
|
||||
{
|
||||
out[i*2+1]=-(float)0xFFFF/(symbol_list[3]-symbol_list[2])*symbol_list[2]+inp[i]*(float)0xFFFF/(symbol_list[3]-symbol_list[2]);
|
||||
}
|
||||
else if(inp[i]>=symbol_list[1])
|
||||
{
|
||||
out[i*2+1]=0x0000;
|
||||
}
|
||||
else if(inp[i]>=symbol_list[0])
|
||||
{
|
||||
out[i*2+1]=(float)0xFFFF/(symbol_list[1]-symbol_list[0])*symbol_list[1]-inp[i]*(float)0xFFFF/(symbol_list[1]-symbol_list[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
out[i*2+1]=0xFFFF;
|
||||
}
|
||||
|
||||
//bit 1
|
||||
if(inp[i]>=symbol_list[2])
|
||||
{
|
||||
out[i*2]=0x0000;
|
||||
}
|
||||
else if(inp[i]>=symbol_list[1])
|
||||
{
|
||||
out[i*2]=0x7FFF-inp[i]*(float)0xFFFF/(symbol_list[2]-symbol_list[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
out[i*2]=0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Soft logic NOT.
|
||||
*
|
||||
* @param a Input A.
|
||||
* @return uint16_t Output = not A.
|
||||
*/
|
||||
uint16_t soft_bit_NOT(const uint16_t a)
|
||||
{
|
||||
return 0xFFFFU-a;
|
||||
}
|
||||
|
||||
//randomizing pattern for M17
|
||||
const uint8_t rand_seq[46]=
|
||||
{
|
||||
0xD6, 0xB5, 0xE2, 0x30, 0x82, 0xFF, 0x84, 0x62, 0xBA, 0x4E, 0x96, 0x90, 0xD8, 0x98, 0xDD, 0x5D, 0x0C, 0xC8, 0x52, 0x43, 0x91, 0x1D, 0xF8,
|
||||
0x6E, 0x68, 0x2F, 0x35, 0xDA, 0x14, 0xEA, 0xCD, 0x76, 0x19, 0x8D, 0xD5, 0x80, 0xD1, 0x33, 0x87, 0x13, 0x57, 0x18, 0x2D, 0x29, 0x78, 0xC3
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Randomize type-4 soft bits.
|
||||
*
|
||||
* @param inp Input 368 soft type-4 bits.
|
||||
*/
|
||||
void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2])
|
||||
{
|
||||
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
|
||||
{
|
||||
if((rand_seq[i/8]>>(7-(i%8)))&1) //flip bit if '1'
|
||||
{
|
||||
inp[i]=soft_bit_NOT(inp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//interleaver pattern for M17
|
||||
const uint16_t intrl_seq[SYM_PER_PLD*2]=
|
||||
{
|
||||
0, 137, 90, 227, 180, 317, 270, 39, 360, 129, 82, 219, 172, 309, 262, 31,
|
||||
352, 121, 74, 211, 164, 301, 254, 23, 344, 113, 66, 203, 156, 293, 246, 15,
|
||||
336, 105, 58, 195, 148, 285, 238, 7, 328, 97, 50, 187, 140, 277, 230, 367,
|
||||
320, 89, 42, 179, 132, 269, 222, 359, 312, 81, 34, 171, 124, 261, 214, 351,
|
||||
304, 73, 26, 163, 116, 253, 206, 343, 296, 65, 18, 155, 108, 245, 198, 335,
|
||||
288, 57, 10, 147, 100, 237, 190, 327, 280, 49, 2, 139, 92, 229, 182, 319,
|
||||
272, 41, 362, 131, 84, 221, 174, 311, 264, 33, 354, 123, 76, 213, 166, 303,
|
||||
256, 25, 346, 115, 68, 205, 158, 295, 248, 17, 338, 107, 60, 197, 150, 287,
|
||||
240, 9, 330, 99, 52, 189, 142, 279, 232, 1, 322, 91, 44, 181, 134, 271,
|
||||
224, 361, 314, 83, 36, 173, 126, 263, 216, 353, 306, 75, 28, 165, 118, 255,
|
||||
208, 345, 298, 67, 20, 157, 110, 247, 200, 337, 290, 59, 12, 149, 102, 239,
|
||||
192, 329, 282, 51, 4, 141, 94, 231, 184, 321, 274, 43, 364, 133, 86, 223,
|
||||
176, 313, 266, 35, 356, 125, 78, 215, 168, 305, 258, 27, 348, 117, 70, 207,
|
||||
160, 297, 250, 19, 340, 109, 62, 199, 152, 289, 242, 11, 332, 101, 54, 191,
|
||||
144, 281, 234, 3, 324, 93, 46, 183, 136, 273, 226, 363, 316, 85, 38, 175,
|
||||
128, 265, 218, 355, 308, 77, 30, 167, 120, 257, 210, 347, 300, 69, 22, 159,
|
||||
112, 249, 202, 339, 292, 61, 14, 151, 104, 241, 194, 331, 284, 53, 6, 143,
|
||||
96, 233, 186, 323, 276, 45, 366, 135, 88, 225, 178, 315, 268, 37, 358, 127,
|
||||
80, 217, 170, 307, 260, 29, 350, 119, 72, 209, 162, 299, 252, 21, 342, 111,
|
||||
64, 201, 154, 291, 244, 13, 334, 103, 56, 193, 146, 283, 236, 5, 326, 95,
|
||||
48, 185, 138, 275, 228, 365, 318, 87, 40, 177, 130, 267, 220, 357, 310, 79,
|
||||
32, 169, 122, 259, 212, 349, 302, 71, 24, 161, 114, 251, 204, 341, 294, 63,
|
||||
16, 153, 106, 243, 196, 333, 286, 55, 8, 145, 98, 235, 188, 325, 278, 47
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Reorder (interleave) 368 soft bits.
|
||||
*
|
||||
* @param outp Reordered soft bits.
|
||||
* @param inp Input soft bits.
|
||||
*/
|
||||
void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_PLD*2])
|
||||
{
|
||||
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
|
||||
outp[i]=inp[intrl_seq[i]];
|
||||
}
|
||||
|
||||
//NXDN all-in-one function to go from punctured bits to valid output, return viterbi error
|
||||
uint32_t nxdn_soft_decision_viterbi(uint8_t * bits, const uint16_t * interleave, uint8_t * puncture, int d_len, int p_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes)
|
||||
{
|
||||
uint32_t error = 0;
|
||||
uint16_t soft_bit[1000];
|
||||
uint8_t temp_bits[500];
|
||||
memset(temp_bits, 0, sizeof(temp_bits));
|
||||
memset(soft_bit, 0, sizeof(soft_bit));
|
||||
|
||||
//deinterleaved bits
|
||||
uint8_t deinterleaved_bits[500];
|
||||
memset(deinterleaved_bits, 0, sizeof(deinterleaved_bits));
|
||||
|
||||
//deinterleaving the bits
|
||||
for (int i = 0; i < d_len; i++)
|
||||
deinterleaved_bits[interleave[i]] = bits[i];
|
||||
|
||||
//converting back to dibits
|
||||
uint8_t dbuf[500];
|
||||
memset(dbuf, 0, sizeof(dbuf));
|
||||
for (int i = 0; i < d_len/2; i++)
|
||||
dbuf[i] = (deinterleaved_bits[(i*2)+0] << 1) | deinterleaved_bits[(i*2)+1];
|
||||
|
||||
float sbuf[500];
|
||||
memset(sbuf, 0.0f, sizeof(sbuf));
|
||||
|
||||
//convert dbuf into a symbol array
|
||||
for (int i = 0; i < d_len/2; i++)
|
||||
{
|
||||
if (dbuf[i] == 0) sbuf[i] = +1.0f;
|
||||
else if (dbuf[i] == 1) sbuf[i] = +3.0f;
|
||||
else if (dbuf[i] == 2) sbuf[i] = -1.0f;
|
||||
else if (dbuf[i] == 3) sbuf[i] = -3.0f;
|
||||
else sbuf[i] = +0.0f;
|
||||
}
|
||||
|
||||
//slice symbols to soft dibits
|
||||
slice_symbols_to_len(soft_bit, sbuf, d_len/2);
|
||||
|
||||
//viterbi
|
||||
error = viterbi_decode_punctured(viterbi_bytes, soft_bit, puncture, d_len, p_len);
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " Ve: %1.1f; ", (float)error/(float)0xFFFF);
|
||||
|
||||
//debug
|
||||
// for (int i = 0; i < num_bytes+1; i++)
|
||||
// fprintf(stderr, "%02X", viterbi_bytes[i]);
|
||||
|
||||
//load viterbi_bytes into bits
|
||||
unpack_byte_array_into_bit_array(viterbi_bytes, temp_bits, num_bytes+1);
|
||||
|
||||
//rearrange bits to get correct offset
|
||||
for (int i = 0; i < num_bytes*8; i++)
|
||||
viterbi_bits[i] = temp_bits[i+offset];
|
||||
|
||||
//zero out viterbi_bytes to reload packed appropriately
|
||||
for (int i = 0; i < num_bytes; i++)
|
||||
viterbi_bytes[i] = 0;
|
||||
|
||||
pack_bit_array_into_byte_array(viterbi_bits, viterbi_bytes, num_bytes);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
//ysf fake puncture
|
||||
uint8_t ysf_puncture[4] = {1,1,1,1};
|
||||
|
||||
//YSF all-in-one function to go from dibits to valid output, return viterbi error
|
||||
uint32_t ysf_soft_decision_viterbi(uint8_t * dbuf, int d_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes)
|
||||
{
|
||||
uint32_t error = 0;
|
||||
uint16_t soft_bit[1000];
|
||||
uint8_t temp_bits[500];
|
||||
memset(temp_bits, 0, sizeof(temp_bits));
|
||||
memset(soft_bit, 0, sizeof(soft_bit));
|
||||
|
||||
float sbuf[500];
|
||||
memset(sbuf, 0.0f, sizeof(sbuf));
|
||||
|
||||
//convert dbuf into a symbol array
|
||||
for (int i = 0; i < d_len/2; i++)
|
||||
{
|
||||
if (dbuf[i] == 0) sbuf[i] = +1.0f;
|
||||
else if (dbuf[i] == 1) sbuf[i] = +3.0f;
|
||||
else if (dbuf[i] == 2) sbuf[i] = -1.0f;
|
||||
else if (dbuf[i] == 3) sbuf[i] = -3.0f;
|
||||
else sbuf[i] = +0.0f;
|
||||
}
|
||||
|
||||
//slice symbols to soft dibits
|
||||
slice_symbols_to_len(soft_bit, sbuf, d_len/2);
|
||||
|
||||
//viterbi
|
||||
error = viterbi_decode_punctured(viterbi_bytes, soft_bit, ysf_puncture, d_len, 4);
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " Ve: %1.1f; ", (float)error/(float)0xFFFF);
|
||||
|
||||
//debug
|
||||
// for (int i = 0; i < num_bytes+1; i++)
|
||||
// fprintf(stderr, "%02X", viterbi_bytes[i]);
|
||||
|
||||
//load viterbi_bytes into bits
|
||||
unpack_byte_array_into_bit_array(viterbi_bytes, temp_bits, num_bytes+1);
|
||||
|
||||
//rearrange bits to get correct offset
|
||||
for (int i = 0; i < num_bytes*8; i++)
|
||||
viterbi_bits[i] = temp_bits[i+offset];
|
||||
|
||||
//zero out viterbi_bytes to reload packed appropriately
|
||||
for (int i = 0; i < num_bytes; i++)
|
||||
viterbi_bytes[i] = 0;
|
||||
|
||||
pack_bit_array_into_byte_array(viterbi_bits, viterbi_bytes, num_bytes);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
//original sources for viterbi code and misc math related
|
||||
//--------------------------------------------------------------------
|
||||
// M17 C library - decode/viterbi.c
|
||||
//
|
||||
// This file contains:
|
||||
// - the Viterbi decoder
|
||||
//
|
||||
// Wojciech Kaczmarski, SP5WWP
|
||||
// M17 Project, 29 December 2023
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// M17 C library - math/math.c
|
||||
//
|
||||
// This file contains:
|
||||
// - absolute difference value
|
||||
// - Euclidean norm (L2) calculation for n-dimensional vectors (float)
|
||||
// - soft-valued arrays to integer conversion (and vice-versa)
|
||||
// - fixed-valued multiplication and division
|
||||
//
|
||||
// Wojciech Kaczmarski, SP5WWP
|
||||
// M17 Project, 29 December 2023
|
||||
//--------------------------------------------------------------------
|
||||
205
src/ysf.c
205
src/ysf.c
@@ -228,12 +228,7 @@ void ysf_dch_decode (dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint
|
||||
}
|
||||
|
||||
if (fn == ft) //last frame
|
||||
{
|
||||
char text_string[15]; memset (text_string, 0, sizeof(text_string));
|
||||
sprintf (text_string, "%s", "BUMBLEBEETUNA");
|
||||
if (strncmp(text_string, state->event_history_s[0].Event_History_Items[0].event_string, 13) != 0)
|
||||
fprintf (stderr, " %s", state->event_history_s[0].Event_History_Items[0].text_message);
|
||||
}
|
||||
fprintf (stderr, " %s", state->event_history_s[0].Event_History_Items[0].text_message);
|
||||
|
||||
break;
|
||||
|
||||
@@ -382,14 +377,15 @@ static inline uint16_t crc16ysf(const uint8_t buf[], int len)
|
||||
int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint8_t ft, uint8_t cm, uint8_t input[])
|
||||
{
|
||||
|
||||
int i, j, err;
|
||||
uint8_t viterbi_bits[100];
|
||||
int i, j, k, err;
|
||||
uint8_t s0, s1;
|
||||
uint8_t trellis_buf[100];
|
||||
uint8_t temp[210];
|
||||
uint8_t viterbi_bytes[100];
|
||||
uint8_t m_data[100];
|
||||
uint8_t bits[210];
|
||||
memset (viterbi_bits, 0, sizeof(viterbi_bits));
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (bits, 0, sizeof(bits));
|
||||
err = 0;
|
||||
|
||||
@@ -402,27 +398,56 @@ int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, u
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t v_error =
|
||||
ysf_soft_decision_viterbi(buf, 200, 13, 8, viterbi_bits, viterbi_bytes);
|
||||
k = 0;
|
||||
//convert dibits to bits
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
bits[k++] = (buf[i] >> 1) & 1;
|
||||
bits[k++] = (buf[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
//debug
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, " D2-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
|
||||
//setup for the convolutional decoder
|
||||
for (i = 0; i < 200; i++)
|
||||
temp[i] = bits[i] << 1;
|
||||
|
||||
uint16_t crc = crc16ysf(viterbi_bits, 96);
|
||||
CNXDNConvolution_start();
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
s0 = temp[(2*i)+0];
|
||||
s1 = temp[(2*i)+1];
|
||||
|
||||
CNXDNConvolution_decode(s0, s1);
|
||||
}
|
||||
|
||||
CNXDNConvolution_chainback(m_data, 96);
|
||||
|
||||
//96/8 = 12, last 4 (96-100) are trailing zeroes
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
||||
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
||||
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
||||
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
||||
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
||||
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
||||
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
uint16_t crc = crc16ysf(trellis_buf, 96);
|
||||
if (crc != 0) err = -2; // crc failure
|
||||
|
||||
for (i = 0; i < 80; i++)
|
||||
viterbi_bits[i] = viterbi_bits[i] ^ pn95[i];
|
||||
trellis_buf[i] = trellis_buf[i] ^ pn95[i];
|
||||
|
||||
//reload after de-whitening
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
for (i = 0; i < 12; i++)
|
||||
viterbi_bytes[i] = (uint8_t)ConvertBitIntoBytes(&viterbi_bits[i*8], 8);
|
||||
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
|
||||
|
||||
//decode the callsign, etc, found in the DCH when no errors
|
||||
if (err == 0)
|
||||
ysf_dch_decode2 (state, bn, bt, fn, ft, cm, viterbi_bits);
|
||||
ysf_dch_decode2 (state, bn, bt, fn, ft, cm, trellis_buf);
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
@@ -435,24 +460,25 @@ int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, u
|
||||
fprintf (stderr, "\n ");
|
||||
fprintf (stderr, "DCH2: ");
|
||||
for (i = 0; i < 12; i++)
|
||||
fprintf (stderr, "[%02X]", viterbi_bytes[i]);
|
||||
fprintf (stderr, "[%02X]", m_data[i]);
|
||||
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
//Soft Decision Full Rate, Type 1 CC, Headers and Terminators DCH (180 dibit version)
|
||||
//modified version of nxdn_deperm_facch1 -- this one for Full Rate, Type 1 CC, Headers and Terminators DCH (180 dibit version)
|
||||
int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint8_t ft, uint8_t cm, uint8_t input[])
|
||||
{
|
||||
int i, j, err;
|
||||
uint8_t viterbi_bits[190];
|
||||
int i, j, k, err;
|
||||
uint8_t s0, s1;
|
||||
uint8_t trellis_buf[190];
|
||||
uint8_t temp[370];
|
||||
uint8_t viterbi_bytes[100];
|
||||
uint8_t m_data[100];
|
||||
uint8_t bits[370];
|
||||
memset (viterbi_bits, 0, sizeof(viterbi_bits));
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (bits, 0, sizeof(bits));
|
||||
err = 0;
|
||||
|
||||
@@ -465,27 +491,56 @@ int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, ui
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t v_error =
|
||||
ysf_soft_decision_viterbi(buf, 360, 23, 8, viterbi_bits, viterbi_bytes);
|
||||
k = 0;
|
||||
//convert dibits to bits
|
||||
for (i = 0; i < 180; i++)
|
||||
{
|
||||
bits[k++] = (buf[i] >> 1) & 1;
|
||||
bits[k++] = (buf[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
//debug
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, " D1-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
|
||||
//setup for the convolutional decoder
|
||||
for (i = 0; i < 360; i++)
|
||||
temp[i] = bits[i] << 1;
|
||||
|
||||
uint16_t crc = crc16ysf(viterbi_bits, 176);
|
||||
CNXDNConvolution_start();
|
||||
for (i = 0; i < 180; i++)
|
||||
{
|
||||
s0 = temp[(2*i)+0];
|
||||
s1 = temp[(2*i)+1];
|
||||
|
||||
CNXDNConvolution_decode(s0, s1);
|
||||
}
|
||||
|
||||
CNXDNConvolution_chainback(m_data, 176);
|
||||
|
||||
//176/8 = 22, last 4 (176-180) are trailing zeroes
|
||||
for(i = 0; i < 22; i++)
|
||||
{
|
||||
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
||||
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
||||
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
||||
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
||||
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
||||
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
||||
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
uint16_t crc = crc16ysf(trellis_buf, 176);
|
||||
if (crc != 0) err = -2; // crc failure
|
||||
|
||||
for (i = 0; i < 160; i++)
|
||||
viterbi_bits[i] = viterbi_bits[i] ^ pn95[i];
|
||||
trellis_buf[i] = trellis_buf[i] ^ pn95[i];
|
||||
|
||||
//reload after de-whitening
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
for (i = 0; i < 22; i++)
|
||||
viterbi_bytes[i] = (uint8_t)ConvertBitIntoBytes(&viterbi_bits[i*8], 8);
|
||||
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
|
||||
|
||||
//decode the callsign, etc, found in the DCH when no errors
|
||||
if (err == 0)
|
||||
ysf_dch_decode (state, bn, bt, fn, ft, cm, viterbi_bits);
|
||||
ysf_dch_decode (state, bn, bt, fn, ft, cm, trellis_buf);
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
@@ -498,24 +553,25 @@ int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, ui
|
||||
fprintf (stderr, "\n ");
|
||||
fprintf (stderr, "DCH1: ");
|
||||
for (i = 0; i < 22; i++)
|
||||
fprintf (stderr, "[%02X]", viterbi_bytes[i]);
|
||||
fprintf (stderr, "[%02X]", m_data[i]);
|
||||
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
//FICH Soft Decision Viterbi
|
||||
int ysf_conv_fich (dsd_opts * opts, uint8_t input[], uint8_t dest[32])
|
||||
//modified version of nxdn_deperm_facch1
|
||||
int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
|
||||
{
|
||||
int i, j, err;
|
||||
uint8_t viterbi_bits[100];
|
||||
int i, j, k, err;
|
||||
uint8_t s0, s1;
|
||||
uint8_t trellis_buf[100];
|
||||
uint8_t temp[210];
|
||||
uint8_t viterbi_bytes[100];
|
||||
uint8_t m_data[100];
|
||||
uint8_t bits[210];
|
||||
memset (viterbi_bits, 0, sizeof(viterbi_bits));
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (bits, 0, sizeof(bits));
|
||||
err = 0;
|
||||
|
||||
@@ -528,12 +584,41 @@ int ysf_conv_fich (dsd_opts * opts, uint8_t input[], uint8_t dest[32])
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t v_error =
|
||||
ysf_soft_decision_viterbi(buf, 200, 13, 8, viterbi_bits, viterbi_bytes);
|
||||
k = 0;
|
||||
//convert dibits to bits
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
bits[k++] = (buf[i] >> 1) & 1;
|
||||
bits[k++] = (buf[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
//debug
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, " F-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
|
||||
//setup for the convolutional decoder
|
||||
for (i = 0; i < 200; i++) //192
|
||||
temp[i] = bits[i] << 1;
|
||||
|
||||
CNXDNConvolution_start();
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
s0 = temp[(2*i)+0];
|
||||
s1 = temp[(2*i)+1];
|
||||
|
||||
CNXDNConvolution_decode(s0, s1);
|
||||
}
|
||||
|
||||
CNXDNConvolution_chainback(m_data, 96);
|
||||
|
||||
//96/8 = 12, last 4 (96-100) are trailing zeroes
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
||||
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
||||
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
||||
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
||||
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
||||
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
||||
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
uint8_t fich_bits[12*4];
|
||||
uint8_t temp_b[24];
|
||||
@@ -546,27 +631,31 @@ int ysf_conv_fich (dsd_opts * opts, uint8_t input[], uint8_t dest[32])
|
||||
g[i] = FALSE;
|
||||
|
||||
for (j = 0; j < 24; j++)
|
||||
temp_b[j] = (char) viterbi_bits[(i*24)+j];
|
||||
temp_b[j] = (char) trellis_buf[(i*24)+j];
|
||||
|
||||
g[i] = Golay_24_12_decode(temp_b);
|
||||
if(g[i] == FALSE) err = -1;
|
||||
|
||||
for (j = 0; j < 24; j++)
|
||||
viterbi_bits[(i*24)+j] = (uint8_t) temp_b[j];
|
||||
trellis_buf[(i*24)+j] = (uint8_t) temp_b[j];
|
||||
}
|
||||
|
||||
//load corrected bits
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
fich_bits[(12*0)+i] = viterbi_bits[i+0];
|
||||
fich_bits[(12*1)+i] = viterbi_bits[i+24];
|
||||
fich_bits[(12*2)+i] = viterbi_bits[i+48];
|
||||
fich_bits[(12*3)+i] = viterbi_bits[i+72];
|
||||
fich_bits[(12*0)+i] = trellis_buf[i+0];
|
||||
fich_bits[(12*1)+i] = trellis_buf[i+24];
|
||||
fich_bits[(12*2)+i] = trellis_buf[i+48];
|
||||
fich_bits[(12*3)+i] = trellis_buf[i+72];
|
||||
}
|
||||
|
||||
uint16_t crc = crc16ysf(fich_bits, 48);
|
||||
if (crc != 0) err = -2; // crc failure
|
||||
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
for (i = 0; i < 12; i++)
|
||||
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
|
||||
|
||||
memcpy(dest, fich_bits, 32); //copy minus the crc16
|
||||
return err;
|
||||
}
|
||||
@@ -719,7 +808,7 @@ void processYSF(dsd_opts * opts, dsd_state * state)
|
||||
fichrawdibits[i] = getDibit(opts, state);
|
||||
|
||||
//from nxdn_deperm_facch1 w/ nxdn convolutional decoder
|
||||
err = ysf_conv_fich (opts, fichrawdibits, fich_decode);
|
||||
err = ysf_conv_fich (fichrawdibits, fich_decode);
|
||||
|
||||
//if errors decoding fich, then just treat it like the last frame that came in
|
||||
if (err == 0)
|
||||
|
||||
Reference in New Issue
Block a user