mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 01:07:24 -05:00
Compare commits
108 Commits
rtl_overha
...
aw_dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34753963b8 | ||
|
|
544d576551 | ||
|
|
72f8c1aff7 | ||
|
|
bfa4891965 | ||
|
|
daa47069bf | ||
|
|
587181f4de | ||
|
|
eb8048f1d9 | ||
|
|
d8f9bd190b | ||
|
|
6880087afe | ||
|
|
6106a608e3 | ||
|
|
1aae6d34c9 | ||
|
|
be57b5149b | ||
|
|
3e221f29f5 | ||
|
|
4754f6a59b | ||
|
|
7027b145b8 | ||
|
|
f38b5976f3 | ||
|
|
5244c85774 | ||
|
|
4005c9b313 | ||
|
|
72b3e5efe4 | ||
|
|
a3a6fb999a | ||
|
|
37855b817b | ||
|
|
4fe32db7f5 | ||
|
|
6819f9010e | ||
|
|
719d9709e6 | ||
|
|
26e4a619f6 | ||
|
|
0838a7f7ba | ||
|
|
a6ea60b345 | ||
|
|
9232d66fcb | ||
|
|
15515158b5 | ||
|
|
fa4a332582 | ||
|
|
4139b19bfe | ||
|
|
cd5f0f4a8e | ||
|
|
fb3dc75c47 | ||
|
|
f2254f413a | ||
|
|
c97400a704 | ||
|
|
cc6711fe4d | ||
|
|
198f0eacb5 | ||
|
|
5e5c3c4555 | ||
|
|
272f17bfa3 | ||
|
|
69d31151dd | ||
|
|
6fd7176659 | ||
|
|
c960f9932c | ||
|
|
e877e7b9de | ||
|
|
43d63b20dc | ||
|
|
e6d2dd7934 | ||
|
|
8c3ae61b1a | ||
|
|
f443063d3b | ||
|
|
ed1d1d630c | ||
|
|
56f6f6d78e | ||
|
|
a6bdfa913f | ||
|
|
ad77f266c4 | ||
|
|
f72eb31b30 | ||
|
|
441ba31d1b | ||
|
|
7d94abd76a | ||
|
|
7b89174bb1 | ||
|
|
f6f5f80361 | ||
|
|
b7cd501cc1 | ||
|
|
9f03ad123d | ||
|
|
d3303aea28 | ||
|
|
615f67536f | ||
|
|
12012a9eee | ||
|
|
53a256bee8 | ||
|
|
d7ce5e4cae | ||
|
|
5af38d3563 | ||
|
|
034855069a | ||
|
|
3ee5acef0a | ||
|
|
b5bd9a7238 | ||
|
|
56b491ed3e | ||
|
|
a26a680fd6 | ||
|
|
4ec2387140 | ||
|
|
385a9a1747 | ||
|
|
2267f5d007 | ||
|
|
99f422898a | ||
|
|
4924bd249a | ||
|
|
a5e000a14c | ||
|
|
53eaccc892 | ||
|
|
e95cd4dd3a | ||
|
|
48b0c4025b | ||
|
|
2171df515d | ||
|
|
83d7460a0b | ||
|
|
38ad11dbef | ||
|
|
eddd6cb8b1 | ||
|
|
abf28593b1 | ||
|
|
bc4aeff164 | ||
|
|
632041a44a | ||
|
|
f1cd83e924 | ||
|
|
50bf004427 | ||
|
|
85a6372c2e | ||
|
|
eb76ea2039 | ||
|
|
14edf325bc | ||
|
|
e11ed353b8 | ||
|
|
f8521fda99 | ||
|
|
fa47d0321b | ||
|
|
095d982b62 | ||
|
|
0060378321 | ||
|
|
8ea6415424 | ||
|
|
eb24239c51 | ||
|
|
ac1c658870 | ||
|
|
cf032ab955 | ||
|
|
2d04878506 | ||
|
|
748bb5b191 | ||
|
|
f3b89f1c51 | ||
|
|
b2d388a210 | ||
|
|
c883e2bf0b | ||
|
|
f8ca0a8e08 | ||
|
|
99b1fd2fe2 | ||
|
|
b0a46a541d | ||
|
|
3aabd85604 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -9,5 +9,4 @@ examples
|
||||
*.ans
|
||||
*.patch
|
||||
*.zip
|
||||
result
|
||||
.cache
|
||||
result
|
||||
@@ -39,6 +39,62 @@ 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)
|
||||
@@ -52,14 +108,13 @@ 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} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIRS})
|
||||
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES})
|
||||
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})
|
||||
|
||||
if(RTLSDR_FOUND)
|
||||
find_package(Threads)
|
||||
@@ -79,6 +134,7 @@ FILE(GLOB HEADERS include/*.h include/*.hpp)
|
||||
|
||||
if(NOT RTLSDR_FOUND)
|
||||
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp)
|
||||
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_tcp.c)
|
||||
endif(NOT RTLSDR_FOUND)
|
||||
|
||||
configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
# Digital Speech Decoder - Florida Man Edition
|
||||
|
||||
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and [Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, YSF, M17, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments.
|
||||
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and [Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP, ilyacodes, Phillipw1084 and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, YSF, M17, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments.
|
||||
|
||||
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, ilyacodes, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody.
|
||||
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# - 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/start-dsd-fme.bat dsd-fme-portable/
|
||||
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/*.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,7 +94,6 @@ 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/
|
||||
|
||||
@@ -128,7 +127,6 @@ 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/
|
||||
@@ -152,7 +150,6 @@ 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/
|
||||
@@ -179,7 +176,6 @@ 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,11 +17,6 @@ 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,18 +19,6 @@ 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,13 +12,7 @@ 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 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
|
||||
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
|
||||
git clone https://github.com/lwvmobile/mbelib
|
||||
cd mbelib
|
||||
git checkout ambe_tones
|
||||
|
||||
@@ -12,13 +12,7 @@ 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 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
|
||||
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
|
||||
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 Audio
|
||||
-fz M17 Stream
|
||||
```
|
||||
|
||||
## 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
|
||||
g - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25, NXDN)
|
||||
T - 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,6 +222,12 @@ 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)
|
||||
@@ -311,70 +317,3 @@ 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 libitpp-dev 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 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 libitpp-dev 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 libncurses-dev libncurses6 libcodec2-dev
|
||||
```
|
||||
|
||||
Fedora 36/37 -- from https://github.com/lwvmobile/dsd-fme/issues/99
|
||||
@@ -60,8 +60,6 @@ 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
|
||||
@@ -69,24 +67,7 @@ sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||
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 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 ..
|
||||
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
|
||||
```
|
||||
|
||||
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")
|
||||
@@ -127,6 +108,46 @@ LD_LIBRARY_PATH=/usr/local/lib
|
||||
export LD_LIBRARY_PATH
|
||||
```
|
||||
|
||||
### macOS Builds (Homebrew)
|
||||
|
||||
Building on macOS (Apple Silicon) using [Homebrew](https://brew.sh "Homebrew") for dependencies has been tested working, but keep in mind this project is primarily focused on Linux, so macOS support should be considered experimental. Unlike the Linux steps above, macOS has no `ldconfig`, so simply skip that step wherever it appears below.
|
||||
|
||||
Install dependencies first:
|
||||
|
||||
```
|
||||
brew install cmake git libsndfile pulseaudio rtl-sdr codec2 ncurses pkg-config
|
||||
```
|
||||
|
||||
Note: Homebrew's ncurses is keg-only (not symlinked into /opt/homebrew), so we will pass an extra CMake flag when configuring DSD-FME below so it can be found.
|
||||
|
||||
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")
|
||||
|
||||
```
|
||||
git clone https://github.com/lwvmobile/mbelib
|
||||
cd mbelib
|
||||
git checkout ambe_tones
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `sysctl -n hw.ncpu`
|
||||
sudo make install
|
||||
cd ../..
|
||||
```
|
||||
|
||||
Finish by cloning and building DSD-FME (note the added -DCMAKE_PREFIX_PATH flag for keg-only ncurses):
|
||||
|
||||
```
|
||||
git clone https://github.com/lwvmobile/dsd-fme
|
||||
cd dsd-fme
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix ncurses)
|
||||
make -j `sysctl -n hw.ncpu`
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Pulse Audio is still the default input/output in DSD-FME, so start the Homebrew Pulse Audio daemon before running dsd-fme (it will also restart at login): `brew services start pulseaudio`. To confirm your RTL-SDR dongle is detected, run `rtl_test` (ships with Homebrew's rtl-sdr package); unlike Linux, there is no driver blacklist to deal with on macOS.
|
||||
|
||||
### Windows Cygwin Builds
|
||||
|
||||
Cygwin builds now have an experimental semi-automatic installer, to run, follow steps below:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Build Version: AW 2025-182-g24c1dd9
|
||||
Build Version: AW 2026-47-ga6ea60b
|
||||
MBElib Version: 1.3.4
|
||||
CODEC2 Support Enabled
|
||||
|
||||
@@ -21,10 +21,9 @@ Input/Output options:
|
||||
pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O)
|
||||
rtl for rtl dongle (Default Values -- see below)
|
||||
rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)
|
||||
rtltcp:host:port:freq:gain:ppm:bw:sq:vol for rtl dongle on an rtl_tcp server (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)
|
||||
@@ -37,8 +36,6 @@ 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; )
|
||||
@@ -54,6 +51,8 @@ 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.
|
||||
@@ -64,7 +63,6 @@ Input/Output options:
|
||||
-q Reverse Mute - Mute Unencrypted Voice and Unmute Encrypted Voice
|
||||
-V <num> Enable TDMA Voice Synthesis on Slot 1 (1), Slot 2 (2), or Both (3); Default is 3;
|
||||
-z Set TDMA Voice Slot Preference when using /dev/dsp audio output (prevent lag and stuttering)
|
||||
-y Enable Experimental Pulse Audio Float Audio Output
|
||||
-v <hex> Set Filtering Bitmap Options (Advanced Option)
|
||||
1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on
|
||||
|
||||
@@ -80,27 +78,12 @@ RTL-SDR options:
|
||||
vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)
|
||||
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
|
||||
Usage: rtltcp:host:port:freq:gain:ppm:bw:sq:vol
|
||||
host <name> rtl_tcp Server Hostname or IP Address (default = localhost)
|
||||
port <num> rtl_tcp Server Port (default = 1234)
|
||||
freq, gain, ppm, bw, sq, vol as above
|
||||
Example: dsd-fme -fs -i rtltcp:192.168.1.10:1234:450M:26:-2:8 -C connect_plus_chan.csv -T
|
||||
|
||||
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
|
||||
@@ -112,7 +95,6 @@ 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*
|
||||
@@ -124,19 +106,19 @@ Decoder options:
|
||||
-fE Decode only EDACS EA/ProVoice with ESK 0xA0*
|
||||
-fm Decode only dPMR*
|
||||
-l Disable DMR, dPMR, NXDN, M17 input filtering
|
||||
-u <num> Unvoiced speech quality (default=3)
|
||||
-xx Expect non-inverted X2-TDMA signal
|
||||
-xr Expect inverted DMR signal
|
||||
-xd Expect inverted ICOM dPMR signal
|
||||
-xz Expect inverted M17 signal
|
||||
-xn Expect inverted NXDN/DCR/IDAS 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)
|
||||
@@ -150,10 +132,9 @@ 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.
|
||||
@@ -161,29 +142,69 @@ Advanced Decoder options:
|
||||
-H '736B9A9C5645288B 243AD5CB8701EF8A'
|
||||
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
|
||||
|
||||
-H <hex> Manually Enter AES-128 or AES-256 Hex Key (see example below)
|
||||
-H <hex> Manually Enter AES-128, AES-256, or Kirisun 128 or 256 Hex Key (see example below)
|
||||
Encapulate in Single Quotation Marks; Space every 16 chars.
|
||||
-H '736B9A9C5645288B 243AD5CB8701EF8A'
|
||||
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
|
||||
|
||||
-H <hex> Manually Enter Caltta 10/32/64 Char Basic Privacy Hex Key (see example below)
|
||||
-H C:40:1234567890
|
||||
-H C:128:736B9A9C5645288B243AD5CB8701EF8A'
|
||||
-H C:256:20029736A5D91042C923EB0697484433005EFC58A1905195E28E9C7836AA2DB8'
|
||||
|
||||
-H <hex> Manually Enter Hytera OTA Encryption 32 Char Hex Key (see example below)
|
||||
-H O:0:0 no keystream application, but check for Hytera OTA Encryption and DMRA EP Late Entry IV.
|
||||
-H O:128:17bfe59b29a06136fb7d3811fd1a4904 for 128-bit RC4 OTA Key Setup
|
||||
|
||||
-R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)
|
||||
|
||||
-1 <hex> Manually Enter RC4 or DES Key Value (DMR, P25, NXDN) (Hex Value)
|
||||
|
||||
-2 <hex> Manually Enter and Enforce TYT 16-bit BP Key Value (DMR) (Hex Value)
|
||||
|
||||
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) AP Hex Key (see example below)
|
||||
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) 128 or 256 bit 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)
|
||||
|
||||
-$ <string> Enter RAS Password 6-24 Char Key Value (DMR) (a-z A-Z, 0-9, $ # _ -)
|
||||
-$ Password123
|
||||
|
||||
Special Use Cases involving # and $, use parenthesis and put \ in front of all $ signs
|
||||
-$ "######"
|
||||
-$ "\$\$\$\$\$\$"
|
||||
|
||||
-y <hex> Enter RAS 56-bit Effective Key Value (DMR) (14 Char Hex Value)
|
||||
-y 6DF241BC4AE2EC
|
||||
|
||||
-, <string> Manually Enter and Enforce Auctus A6 (GoComm) 8 Char Key Value (DMR) (String Value)
|
||||
-, Secure04
|
||||
|
||||
-u <string> Manually Enter Vertex Standard 10 or 64 Char Hex Key
|
||||
-u 10:1234567891 or 64:AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD
|
||||
-u 0:0 no KS application, but apply the inversion to the interleave (clear errors on 256)
|
||||
|
||||
-S <str> Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits)
|
||||
For Example, enter 16-bit Keystream 0909 as:
|
||||
-S 16:0909
|
||||
@@ -191,6 +212,10 @@ 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
|
||||
For Example, Caltta 40-bit Keystream w/ optional 7-bit discard as:
|
||||
-S 40:1234567890:7
|
||||
|
||||
-k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.
|
||||
Only supports NXDN, DMR Basic Privacy (decimal value).
|
||||
@@ -203,6 +228,8 @@ 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.
|
||||
|
||||
@@ -219,7 +246,7 @@ Advanced Decoder options:
|
||||
-p Disable Tune to Private Calls (DMR TIII, P25, NXDN Type-C and Type-D)
|
||||
-E Disable Tune to Group Calls (DMR TIII, Con+, Cap+, P25, NXDN Type-C, and Type-D)
|
||||
-e Enable Tune to Data Calls (DMR TIII, Cap+, NXDN Type-C)
|
||||
(NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls
|
||||
(NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls)
|
||||
-I <dec> Specify TG to Hold During Trunking (DMR, P25, NXDN Type-C Trunking)
|
||||
-U <port> Enable RIGCTL/TCP; Set TCP Port for RIGCTL. (4532 on SDR++)
|
||||
-B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF)
|
||||
|
||||
@@ -22,27 +22,20 @@
|
||||
@REM set options to pass to dsd-fme
|
||||
set "options= -N -Z "
|
||||
|
||||
@REM Set Date Time for log (sourced from: https://stackoverflow.com/questions/1192476/format-date-and-time-in-a-windows-batch-script)
|
||||
@REM Create Folder and Set Date Time for logs
|
||||
@echo off
|
||||
REM "%date: =0%" replaces spaces with zeros
|
||||
set d=%date: =0%
|
||||
REM "set yyyy=%d:~-4%" pulls the last 4 characters
|
||||
set yyyy=%d:~-4%
|
||||
set mm=%d:~4,2%
|
||||
set dd=%d:~7,2%
|
||||
set dow=%d:~0,3%
|
||||
set d=%yyyy%%mm%%dd%
|
||||
|
||||
set t=%TIME: =0%
|
||||
REM "%t::=%" removes semi-colons
|
||||
REM Instead of above, you could use "%t::=-%" to
|
||||
REM replace semi-colons with hyphens (or any
|
||||
REM non-special character)
|
||||
set t=%t::=%
|
||||
set t=%t:.=%
|
||||
|
||||
set datetimestr=%d%_%t%
|
||||
@REM @echo Long date time str = %datetimestr%
|
||||
mkdir logs
|
||||
@REM Locale-independent date/time for log filenames (yyyyMMdd_HHmmss)
|
||||
@REM Works on older Windows without wmic or PowerShell
|
||||
for /f "tokens=1-6 delims=/: " %%a in ('robocopy "|" . /njh ^| find ":"') do (
|
||||
set "YYYY=%%a"
|
||||
set "MM=%%b"
|
||||
set "DD=%%c"
|
||||
set "HH=%%d"
|
||||
set "Min=%%e"
|
||||
set "Sec=%%f"
|
||||
)
|
||||
set "datetimestr=%YYYY%%MM%%DD%_%HH%%Min%%Sec%"
|
||||
|
||||
@REM random number is used so that two log files don't share the same name
|
||||
set rnd=%RANDOM%
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
*
|
||||
* 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,9 +5,6 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
261
include/dsd.h
261
include/dsd.h
@@ -59,8 +59,9 @@
|
||||
#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
|
||||
@@ -172,8 +173,8 @@ typedef struct pa_devicelist
|
||||
typedef struct
|
||||
{
|
||||
unsigned long int groupNumber;
|
||||
char groupMode[8]; //char *?
|
||||
char groupName[50];
|
||||
char groupMode[3];
|
||||
char groupName[100];
|
||||
} groupinfo;
|
||||
|
||||
|
||||
@@ -323,6 +324,7 @@ 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];
|
||||
@@ -353,25 +355,26 @@ 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_pwr;
|
||||
long int rtl_rms;
|
||||
int rtltcp_enabled;
|
||||
char rtltcp_hostname[1024];
|
||||
int rtltcp_portno;
|
||||
int monitor_input_audio;
|
||||
int analog_only;
|
||||
int pulse_raw_rate_in;
|
||||
@@ -414,7 +417,6 @@ typedef struct
|
||||
short int aggressive_framesync;
|
||||
|
||||
int frame_m17;
|
||||
int inverted_m17;
|
||||
|
||||
FILE *symbolfile;
|
||||
int call_alert;
|
||||
@@ -431,12 +433,6 @@ 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;
|
||||
@@ -643,7 +639,7 @@ typedef struct
|
||||
unsigned long long int K2;
|
||||
unsigned long long int K3;
|
||||
unsigned long long int K4;
|
||||
int M;
|
||||
uint8_t forced_alg_id;
|
||||
int menuopen;
|
||||
|
||||
//AES Key Segments
|
||||
@@ -656,6 +652,14 @@ typedef struct
|
||||
//xl specific, we need to know if the ESS is from HDU, or from LDU2
|
||||
int xl_is_hdu;
|
||||
|
||||
//Vertex Standard Keys
|
||||
// #define VERTEX_KEY40_BY_TG
|
||||
// #define VERTEX_KEY256_BY_TG
|
||||
char vtx256_key[64];
|
||||
unsigned long long int vtx40_key;
|
||||
unsigned long long int vtx256_ekey;
|
||||
uint8_t vtx_key_loaded;
|
||||
|
||||
unsigned int debug_audio_errors;
|
||||
unsigned int debug_audio_errorsR;
|
||||
unsigned int debug_header_errors;
|
||||
@@ -879,7 +883,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[0x3FF]; //max supported by Cygwin is 3FFF, I hope nobody actually tries to import this many groups
|
||||
groupinfo * group_array;
|
||||
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
|
||||
@@ -898,6 +902,7 @@ 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];
|
||||
@@ -924,6 +929,26 @@ typedef struct
|
||||
//Remus DMR End Call Alert Beep
|
||||
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
|
||||
|
||||
//RAS Key Variables
|
||||
// #define DEBUG_RAS
|
||||
unsigned long long int ras_effective_key;
|
||||
int ras_permutation_order[104];
|
||||
uint8_t dmr_is_ras; //indicate a partial or complete RAS enabled system
|
||||
|
||||
//Hytera OTA Key Variables
|
||||
// #define DEBUG_HOTA
|
||||
uint8_t hota_enc_check;
|
||||
uint8_t hota_key_loaded;
|
||||
uint8_t dmr_is_hota;
|
||||
int hota_bit_shift;
|
||||
int hota_byte_shifts[12];
|
||||
uint8_t hota_rc4_key[16];
|
||||
uint8_t hota_rc4_ks[80];
|
||||
uint8_t hota_lc_ctx[2][4];
|
||||
uint8_t hota_ctx_ok[2];
|
||||
int hota_sbrc_idx1[2];
|
||||
int hota_sbrc_idx2[2];
|
||||
|
||||
//Bitmap Filtering Options
|
||||
int audio_smoothing;
|
||||
|
||||
@@ -952,15 +977,11 @@ 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];
|
||||
@@ -968,18 +989,22 @@ typedef struct
|
||||
char m17_src_str[50];
|
||||
char m17_dst_str[50];
|
||||
|
||||
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
|
||||
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;
|
||||
|
||||
//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
|
||||
@@ -991,6 +1016,9 @@ 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;
|
||||
@@ -998,9 +1026,16 @@ 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;
|
||||
int straight_discard;
|
||||
|
||||
uint8_t static_ks_bits[2][882];
|
||||
int static_ks_counter[2];
|
||||
@@ -1014,13 +1049,6 @@ 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"
|
||||
@@ -1190,7 +1218,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, Event_History_I * event_struct);
|
||||
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_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);
|
||||
@@ -1204,7 +1232,6 @@ void printFrameInfo (dsd_opts * opts, dsd_state * state);
|
||||
void processFrame (dsd_opts * opts, dsd_state * state);
|
||||
void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation);
|
||||
int getFrameSync (dsd_opts * opts, dsd_state * state);
|
||||
int comp (const void *a, const void *b);
|
||||
void noCarrier (dsd_opts * opts, dsd_state * state);
|
||||
void initOpts (dsd_opts * opts);
|
||||
void initState (dsd_state * state);
|
||||
@@ -1244,13 +1271,6 @@ 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)
|
||||
@@ -1276,25 +1296,25 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state);
|
||||
uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c);
|
||||
void ncursesClose ();
|
||||
|
||||
//new NXDN Functions start here!
|
||||
//NXDN Functions Start Here
|
||||
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
||||
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_pn95_dibit_scrambler(dsd_state * state, uint8_t * dibits, int len);
|
||||
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[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);
|
||||
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);
|
||||
|
||||
//OP25 NXDN CRC functions
|
||||
int load_i(const uint8_t val[], int len);
|
||||
@@ -1303,15 +1323,18 @@ 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);
|
||||
|
||||
/* 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();
|
||||
//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);
|
||||
|
||||
//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);
|
||||
@@ -1319,6 +1342,9 @@ 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,
|
||||
@@ -1327,16 +1353,29 @@ 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);
|
||||
|
||||
@@ -1350,11 +1389,14 @@ int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
|
||||
|
||||
//BPTC (Block Product Turbo Code) functions
|
||||
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
|
||||
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
|
||||
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3], uint8_t * is_ras);
|
||||
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
|
||||
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32], uint32_t ParityCheckTypeOdd);
|
||||
uint32_t BPTC_128x77_Check_Errors(uint8_t InputDataMatrix[8][16]);
|
||||
|
||||
//Reed Solomon (12,9) functions
|
||||
//Reed Solomon DMR functions
|
||||
void rs_16_13_encode(uint8_t * input);
|
||||
void rs_12_9_encode(uint8_t * input);
|
||||
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
|
||||
uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
|
||||
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
|
||||
@@ -1367,6 +1409,7 @@ uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes,
|
||||
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
|
||||
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData);
|
||||
uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData);
|
||||
uint32_t ComputeCrc32BitV(uint8_t * DMRData, uint32_t NbData);
|
||||
|
||||
//new simplified dmr functions
|
||||
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
|
||||
@@ -1376,7 +1419,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 dmr_34(uint8_t * input, uint8_t treturn[18]); //simplier trellis decoder
|
||||
uint32_t viterbi_r34 (uint8_t * input, uint8_t * output);
|
||||
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
|
||||
|
||||
@@ -1398,10 +1441,11 @@ 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 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 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 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);
|
||||
@@ -1487,6 +1531,11 @@ void LFSRP(dsd_state * state);
|
||||
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
|
||||
void LFSR64(dsd_state * state);
|
||||
|
||||
//IV Reversals using LFSR tap +1 arrangements
|
||||
uint64_t reverse_lfsr_32a_to_len(uint8_t * iv, int16_t len);
|
||||
uint64_t reverse_lfsr_32d_to_len(uint8_t * iv, int16_t len);
|
||||
uint64_t reverse_lfsr_64_to_len(dsd_opts * opts, uint8_t * iv, int16_t len);
|
||||
|
||||
void Hamming_7_4_init();
|
||||
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
|
||||
bool Hamming_7_4_decode(unsigned char *rxBits);
|
||||
@@ -1580,7 +1629,6 @@ 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);
|
||||
@@ -1610,9 +1658,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);
|
||||
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);
|
||||
|
||||
//15-bit Scrambler
|
||||
void pdu_scrambler_keystream_creation(uint8_t * ks, int lfsr, int len);
|
||||
|
||||
//RC4 function prototypes
|
||||
void rc4_voice_decrypt (int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[]);
|
||||
@@ -1630,25 +1678,58 @@ 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 Encryption Modes
|
||||
//Tytera / Retevis / Anytone / Kenwood / Vertex / Baofeng / Misc DMR 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);
|
||||
|
||||
//Misc Other Encryption Modes
|
||||
void retevis_rc2_keystream_creation(dsd_state *state, char *input);
|
||||
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);
|
||||
void caltta_bp_keystream_creation(dsd_state * state, char * input);
|
||||
void auctus_keystream_creation(dsd_state * state, char * input);
|
||||
void vertex_keystream_setup(dsd_state * state, char * input);
|
||||
void vertex_40_keystream_creation(dsd_state * state, unsigned long long int key_value);
|
||||
void vertex_256_keystream_creation(dsd_state * state, char * key_string);
|
||||
void vtx256_ambe2_interleave_inversion(char ambe_fr[4][24]);
|
||||
|
||||
//Kirisun LFSR and General Keystream Creator
|
||||
uint32_t kirisun_lfsr(unsigned long long int mi);
|
||||
void kirisun_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);
|
||||
void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value, int len);
|
||||
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len);
|
||||
void hytera_enhanced_alg_refresh(dsd_state * state);
|
||||
|
||||
//RAS
|
||||
uint32_t ras_mac_calculator(dsd_state * state, uint8_t * input, int input_len, int type);
|
||||
void ras_effective_key_creation(dsd_state * state, char * input);
|
||||
void ras_permute_order(uint32_t seed, int * order);
|
||||
|
||||
//Hytera OTA Encryption
|
||||
void hytera_ota_keystream_setup(dsd_state * state, char * input);
|
||||
void hytera_ota_decrypt_bptc_96(dsd_state * state, uint8_t * input, uint8_t type);
|
||||
void hytera_ota_decrypt_emb_lc(dsd_state * state, uint8_t input[8][16]);
|
||||
void hytera_ota_search_emb_lc(dsd_state * state, uint8_t input[8][16]);
|
||||
void hytera_ota_decrypt_sb_rc(dsd_state * state, uint8_t * input);
|
||||
void hytera_ota_search_sb_rc(dsd_state * state, uint8_t * input, int ptype);
|
||||
void hytera_create_lc_ctx(dsd_state * state, uint8_t * input);
|
||||
void hytera_ota_unit_testing(dsd_state * state);
|
||||
|
||||
//Hytera Decryption for Data Blocks
|
||||
uint8_t hytera_bp_data_decrypt(dsd_state * state, uint8_t * input, int len);
|
||||
uint8_t hytera_ep_data_decrypt(dsd_opts * opts, dsd_state * state, uint8_t * input, int len);
|
||||
|
||||
//LFSR to expand either a DMR 32-bit or P25/NXDN 64-bit MI into a 128-bit IV for AES
|
||||
void lfsr_32_to_128(uint8_t * iv);
|
||||
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
|
||||
@@ -1661,8 +1742,23 @@ 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_pwr();
|
||||
long int rtl_return_rms();
|
||||
void rtl_clean_queue();
|
||||
|
||||
//rtl_tcp client (rtl_sdr_tcp.c)
|
||||
int rtltcp_open(const char *hostname, int portno);
|
||||
int rtlsdr_or_tcp_close(rtlsdr_dev_t *dev);
|
||||
int rtlsdr_or_tcp_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
|
||||
int rtlsdr_or_tcp_cancel_async(rtlsdr_dev_t *dev);
|
||||
int rtlsdr_or_tcp_reset_buffer(rtlsdr_dev_t *dev);
|
||||
int rtlsdr_or_tcp_get_tuner_gains(rtlsdr_dev_t *dev, int *gains);
|
||||
int rtlsdr_or_tcp_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq);
|
||||
int rtlsdr_or_tcp_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate);
|
||||
int rtlsdr_or_tcp_set_direct_sampling(rtlsdr_dev_t *dev, int on);
|
||||
int rtlsdr_or_tcp_set_offset_tuning(rtlsdr_dev_t *dev, int on);
|
||||
int rtlsdr_or_tcp_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual);
|
||||
int rtlsdr_or_tcp_set_tuner_gain(rtlsdr_dev_t *dev, int gain);
|
||||
int rtlsdr_or_tcp_set_freq_correction(rtlsdr_dev_t *dev, int ppm);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1672,6 +1768,9 @@ 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 uint8_t PERM_12_5[] = {
|
||||
static const uint16_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 uint8_t PERM_12_5[] = {
|
||||
11,23,35,47,59
|
||||
};
|
||||
|
||||
static const uint8_t PERM_16_9[] = {
|
||||
static const uint16_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,
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
|
||||
#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
Normal file
147
include/pc5.h
Normal file
@@ -0,0 +1,147 @@
|
||||
#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
|
||||
43
include/rc2.h
Normal file
43
include/rc2.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#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,7 +6,6 @@
|
||||
, ncurses
|
||||
, pulseaudio
|
||||
, rtl-sdr
|
||||
, itpp
|
||||
, codec2
|
||||
, portaudioSupport ? true
|
||||
, portaudio ? null
|
||||
@@ -23,7 +22,6 @@ stdenv.mkDerivation {
|
||||
buildInputs = [
|
||||
mbelib
|
||||
libsndfile
|
||||
itpp
|
||||
rtl-sdr
|
||||
ncurses.dev
|
||||
pulseaudio.dev
|
||||
@@ -39,7 +37,7 @@ stdenv.mkDerivation {
|
||||
tap audio and synthesize the decoded speech. Speech synthesis requires
|
||||
mbelib, which is a separate package.
|
||||
'';
|
||||
homepage = "https://github.com/szechyjs/dsd";
|
||||
homepage = "https://github.com/lwvmobile/dsd-fme";
|
||||
license = licenses.mit;
|
||||
platforms = platforms.unix;
|
||||
};
|
||||
|
||||
671
src/bptc.c
671
src/bptc.c
@@ -167,223 +167,295 @@ void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output)
|
||||
|
||||
|
||||
/*
|
||||
* @brief : This function extract the 96 bits of a deinteleaved 196 bits
|
||||
* buffer using BPTC (196,96)
|
||||
*
|
||||
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (196 bytes)
|
||||
*
|
||||
* @param DMRDataExtracted : Pointer where the DMR data will be written (96 bytes)
|
||||
*
|
||||
* @return The total number of irrecoverable Hamming check errors
|
||||
* BPTC (196,96) extract — ETSI TS 102 361-1 B.1.1
|
||||
* Two-pass row Hamming(15,11) + column Hamming(13,9).
|
||||
* Debug: define BPTC_196x96_DEBUG 1 for row/col correction chatter.
|
||||
*/
|
||||
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3])
|
||||
|
||||
// #define BPTC_196x96_DEBUG 1
|
||||
|
||||
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196],
|
||||
uint8_t DMRDataExtracted[96],
|
||||
uint8_t R[3],
|
||||
uint8_t * is_ras)
|
||||
{
|
||||
uint32_t i, j, k;
|
||||
uint8_t DataMatrix[13][15];
|
||||
uint8_t LineUncorrected[15];
|
||||
uint8_t LineBefore[15];
|
||||
uint8_t LineCorrected[11];
|
||||
uint8_t ColumnUncorrected[13];
|
||||
uint8_t ColumnBefore[13];
|
||||
uint8_t ColumnCorrected[9];
|
||||
uint32_t HammingIrrecoverableErrorNb = 0;
|
||||
uint32_t row_corr_p1 = 0, col_corr_p1 = 0;
|
||||
uint32_t row_fail_p1 = 0, col_fail_p1 = 0;
|
||||
uint32_t row_corr_p2 = 0, col_corr_p2 = 0;
|
||||
uint32_t row_fail_p2 = 0, col_fail_p2 = 0;
|
||||
|
||||
/* First step : Reconstitute the BPTC 15x11 matrix
|
||||
* Note : Input data shall be deinterleaved */
|
||||
k = 1; /* Discard R(3) bit - See DMR standard chapter B1.1 BPTC (196,96) */
|
||||
for(i = 0; i < 13; i++)
|
||||
UNUSED(row_corr_p1); UNUSED(col_corr_p1);
|
||||
UNUSED(row_fail_p1); UNUSED(col_fail_p1);
|
||||
UNUSED(row_corr_p2); UNUSED(col_corr_p2);
|
||||
UNUSED(row_fail_p2); UNUSED(col_fail_p2);
|
||||
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf (stderr, "\n");
|
||||
#endif
|
||||
|
||||
/* Rebuild 13×15 matrix; discard R(3) at index 0 — DMR B.1.1 */
|
||||
k = 1;
|
||||
for (i = 0; i < 13; i++)
|
||||
{
|
||||
for(j = 0; j < 15; j++)
|
||||
for (j = 0; j < 15; j++)
|
||||
{
|
||||
/* Only the LSBit of the byte is stored */
|
||||
DataMatrix[i][j] = (InputDeInteleavedData[k] & 1);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */
|
||||
// DataMatrix[0][0] = 0; /* R(2) */
|
||||
// DataMatrix[0][1] = 0; /* R(1) */
|
||||
// DataMatrix[0][2] = 0; /* R(0) */
|
||||
|
||||
/* Init the Hamming (15,11,3) library
|
||||
* Not needed because it has already been done
|
||||
* in the "InitAllFecFunction()" function */
|
||||
//Hamming_15_11_init();
|
||||
|
||||
/* Init the Hamming (13,9,3) library
|
||||
* Not needed because it has already been done
|
||||
* in the "InitAllFecFunction()" function */
|
||||
//Hamming_13_9_init();
|
||||
|
||||
/* Init the Hamming error counter */
|
||||
HammingIrrecoverableErrorNb = 0;
|
||||
|
||||
/* Process the the Hamming (15,11,3) code
|
||||
* check on each line (first time).
|
||||
* Do not check the 4th last lines */
|
||||
for(i = 0; i < 9; i++)
|
||||
/* ========== Pass 1: row Hamming(15,11) on rows 0..8 ========== */
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
/* Get a full line */
|
||||
for(j = 0; j < 15; j++)
|
||||
for (j = 0; j < 15; j++)
|
||||
{
|
||||
LineUncorrected[j] = DataMatrix[i][j];
|
||||
LineBefore[j] = LineUncorrected[j];
|
||||
}
|
||||
|
||||
/* Apply Hamming (15,11,3) code correction */
|
||||
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
if (Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
}
|
||||
|
||||
/* Re-inject the line in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Process the the Hamming (15,11,3) code
|
||||
* check on each column/row (first time) */
|
||||
for(i = 0; i < 15; i++)
|
||||
{
|
||||
/* Get a full column */
|
||||
for(j = 0; j < 13; j++)
|
||||
{
|
||||
ColumnUncorrected[j] = DataMatrix[j][i];
|
||||
}
|
||||
|
||||
/* Apply Hamming (15,11,3) code correction */
|
||||
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
}
|
||||
|
||||
/* Re-inject the column in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 9; j++)
|
||||
{
|
||||
DataMatrix[j][i] = ColumnCorrected[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* The first Hamming code check has maybe corrected bit
|
||||
* witch can be use to correct other bit, so make the
|
||||
* same operation twice */
|
||||
|
||||
/* Reset the number of irrecoverable errors */
|
||||
HammingIrrecoverableErrorNb = 0;
|
||||
|
||||
|
||||
/* Process the the Hamming (15,11,3) code
|
||||
* check on each line (second time).
|
||||
* Do not check the 4th last lines */
|
||||
for(i = 0; i < 9; i++)
|
||||
{
|
||||
/* Get a full line */
|
||||
for(j = 0; j < 15; j++)
|
||||
{
|
||||
LineUncorrected[j] = DataMatrix[i][j];
|
||||
}
|
||||
|
||||
/* Apply Hamming (15,11,3) code correction */
|
||||
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
/* Row/Line contains irrecoverable errors */
|
||||
HammingIrrecoverableErrorNb++;
|
||||
row_fail_p1++;
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf(stderr, " BPTC196 P1 ROW FAIL row=%u rx=", (unsigned)i);
|
||||
for (j = 0; j < 15; j++)
|
||||
fprintf(stderr, "%u", LineBefore[j] & 1);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
/* Keep original data bits on failure */
|
||||
for (j = 0; j < 11; j++)
|
||||
DataMatrix[i][j] = LineBefore[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Row/Line contains correct data */
|
||||
}
|
||||
|
||||
/* Re-inject the line in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
/* rxBits were corrected in place; note any flip */
|
||||
for (j = 0; j < 15; j++)
|
||||
{
|
||||
if ((LineBefore[j] & 1) != (LineUncorrected[j] & 1))
|
||||
{
|
||||
row_corr_p1++;
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC196 P1 ROW CORRECTED row=%u bit=%u %u->%u\n",
|
||||
(unsigned)i, (unsigned)j,
|
||||
(unsigned)(LineBefore[j] & 1),
|
||||
(unsigned)(LineUncorrected[j] & 1));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 11; j++)
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
}
|
||||
}
|
||||
#if BPTC_196x96_DEBUG
|
||||
if (row_fail_p1 || row_corr_p1)
|
||||
fprintf(stderr, " BPTC196 P1 ROW summary: fail=%u corrected_bits=%u\n",
|
||||
(unsigned)row_fail_p1, (unsigned)row_corr_p1);
|
||||
#endif
|
||||
|
||||
|
||||
/* Process the the Hamming (15,11,3) code
|
||||
* check on each column/row (second time) */
|
||||
for(i = 0; i < 15; i++)
|
||||
/* ========== Pass 1: column Hamming(13,9) on cols 0..14 ========== */
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
/* Get a full column */
|
||||
for(j = 0; j < 13; j++)
|
||||
for (j = 0; j < 13; j++)
|
||||
{
|
||||
ColumnUncorrected[j] = DataMatrix[j][i];
|
||||
ColumnBefore[j] = ColumnUncorrected[j];
|
||||
}
|
||||
|
||||
/* Apply Hamming (15,11,3) code correction */
|
||||
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
|
||||
if (Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
|
||||
{
|
||||
/* Row/Line contains irrecoverable errors */
|
||||
HammingIrrecoverableErrorNb++;
|
||||
col_fail_p1++;
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf(stderr, " BPTC196 P1 COL FAIL col=%u rx=", (unsigned)i);
|
||||
for (j = 0; j < 13; j++)
|
||||
fprintf(stderr, "%u", ColumnBefore[j] & 1);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
for (j = 0; j < 9; j++)
|
||||
DataMatrix[j][i] = ColumnBefore[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Row/Line contains correct data */
|
||||
}
|
||||
|
||||
/* Re-inject the column in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 9; j++)
|
||||
{
|
||||
DataMatrix[j][i] = ColumnCorrected[j];
|
||||
for (j = 0; j < 13; j++)
|
||||
{
|
||||
if ((ColumnBefore[j] & 1) != (ColumnUncorrected[j] & 1))
|
||||
{
|
||||
col_corr_p1++;
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC196 P1 COL CORRECTED col=%u bit=%u %u->%u\n",
|
||||
(unsigned)i, (unsigned)j,
|
||||
(unsigned)(ColumnBefore[j] & 1),
|
||||
(unsigned)(ColumnUncorrected[j] & 1));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 9; j++)
|
||||
DataMatrix[j][i] = ColumnCorrected[j];
|
||||
}
|
||||
}
|
||||
#if BPTC_196x96_DEBUG
|
||||
if (col_fail_p1 || col_corr_p1)
|
||||
fprintf(stderr, " BPTC196 P1 COL summary: fail=%u corrected_bits=%u\n",
|
||||
(unsigned)col_fail_p1, (unsigned)col_corr_p1);
|
||||
#endif
|
||||
|
||||
/* Extract the DMR data (96 bit) from the matrix */
|
||||
/* First line : Do not take R(2), R(1) and R(0) */
|
||||
/* Second pass can fix more after the first cascade */
|
||||
HammingIrrecoverableErrorNb = 0;
|
||||
|
||||
/* ========== Pass 2: rows ========== */
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
for (j = 0; j < 15; j++)
|
||||
{
|
||||
LineUncorrected[j] = DataMatrix[i][j];
|
||||
LineBefore[j] = LineUncorrected[j];
|
||||
}
|
||||
|
||||
if (Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
row_fail_p2++;
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf(stderr, " BPTC196 P2 ROW FAIL row=%u rx=", (unsigned)i);
|
||||
for (j = 0; j < 15; j++)
|
||||
fprintf(stderr, "%u", LineBefore[j] & 1);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
for (j = 0; j < 11; j++)
|
||||
DataMatrix[i][j] = LineBefore[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j = 0; j < 15; j++)
|
||||
{
|
||||
if ((LineBefore[j] & 1) != (LineUncorrected[j] & 1))
|
||||
{
|
||||
row_corr_p2++;
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC196 P2 ROW CORRECTED row=%u bit=%u %u->%u\n",
|
||||
(unsigned)i, (unsigned)j,
|
||||
(unsigned)(LineBefore[j] & 1),
|
||||
(unsigned)(LineUncorrected[j] & 1));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 11; j++)
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
}
|
||||
}
|
||||
#if BPTC_196x96_DEBUG
|
||||
if (row_fail_p2 || row_corr_p2)
|
||||
fprintf(stderr, " BPTC196 P2 ROW summary: fail=%u corrected_bits=%u\n",
|
||||
(unsigned)row_fail_p2, (unsigned)row_corr_p2);
|
||||
#endif
|
||||
|
||||
/* ========== Pass 2: columns ========== */
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
for (j = 0; j < 13; j++)
|
||||
{
|
||||
ColumnUncorrected[j] = DataMatrix[j][i];
|
||||
ColumnBefore[j] = ColumnUncorrected[j];
|
||||
}
|
||||
|
||||
if (Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
col_fail_p2++;
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf(stderr, " BPTC196 P2 COL FAIL col=%u rx=", (unsigned)i);
|
||||
for (j = 0; j < 13; j++)
|
||||
fprintf(stderr, "%u", ColumnBefore[j] & 1);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
for (j = 0; j < 9; j++)
|
||||
DataMatrix[j][i] = ColumnBefore[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j = 0; j < 13; j++)
|
||||
{
|
||||
if ((ColumnBefore[j] & 1) != (ColumnUncorrected[j] & 1))
|
||||
{
|
||||
col_corr_p2++;
|
||||
#if BPTC_196x96_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC196 P2 COL CORRECTED col=%u bit=%u %u->%u\n",
|
||||
(unsigned)i, (unsigned)j,
|
||||
(unsigned)(ColumnBefore[j] & 1),
|
||||
(unsigned)(ColumnUncorrected[j] & 1));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 9; j++)
|
||||
DataMatrix[j][i] = ColumnCorrected[j];
|
||||
}
|
||||
}
|
||||
#if BPTC_196x96_DEBUG
|
||||
if (col_fail_p2 || col_corr_p2)
|
||||
fprintf(stderr, " BPTC196 P2 COL summary: fail=%u corrected_bits=%u\n",
|
||||
(unsigned)col_fail_p2, (unsigned)col_corr_p2);
|
||||
fprintf(stderr,
|
||||
" BPTC196 FINAL irrecoverable=%u P1(row_f=%u col_f=%u row_c=%u col_c=%u) "
|
||||
"P2(row_f=%u col_f=%u row_c=%u col_c=%u)\n",
|
||||
(unsigned)HammingIrrecoverableErrorNb,
|
||||
(unsigned)row_fail_p1, (unsigned)col_fail_p1,
|
||||
(unsigned)row_corr_p1, (unsigned)col_corr_p1,
|
||||
(unsigned)row_fail_p2, (unsigned)col_fail_p2,
|
||||
(unsigned)row_corr_p2, (unsigned)col_corr_p2);
|
||||
#endif
|
||||
|
||||
/* Extract 96 data bits */
|
||||
k = 0;
|
||||
|
||||
for(i = 3; i < 11; i++)
|
||||
for (i = 3; i < 11; i++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[0][i];
|
||||
k++;
|
||||
DMRDataExtracted[k++] = DataMatrix[0][i];
|
||||
}
|
||||
for (i = 1; i < 9; i++)
|
||||
{
|
||||
for (j = 0; j < 11; j++)
|
||||
DMRDataExtracted[k++] = DataMatrix[i][j];
|
||||
}
|
||||
|
||||
/* Next lines */
|
||||
for(i = 1; i < 9; i++)
|
||||
if (HammingIrrecoverableErrorNb == 0)
|
||||
{
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[i][j];
|
||||
k++;
|
||||
}
|
||||
R[0] = DataMatrix[0][2]; /* R(0) */
|
||||
R[1] = DataMatrix[0][1]; /* R(1) */
|
||||
R[2] = DataMatrix[0][0]; /* R(2) */
|
||||
}
|
||||
|
||||
/* R(0) to R(2) may be used to transport some
|
||||
* Restricted Access System (RAS) information,
|
||||
* So save these three bits after hamming correction
|
||||
* See patent US 2013/0288643 A1 */
|
||||
R[0] = DataMatrix[0][2]; /* Save R(0) */
|
||||
R[1] = DataMatrix[0][1]; /* Save R(1) */
|
||||
R[2] = DataMatrix[0][0]; /* Save R(2) */
|
||||
if (R[0] == 0 && R[1] == 0 && R[2] == 1)
|
||||
*is_ras = 1;
|
||||
else
|
||||
*is_ras = 0;
|
||||
|
||||
/* Return the number of irrecoverable Hamming errors */
|
||||
return HammingIrrecoverableErrorNb;
|
||||
} /* End BPTC_196x96_Extract_Data() */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief : This function extract the 77 bits of a deinteleaved 128 bits
|
||||
* buffer using BPTC (128,77).
|
||||
* BPTC (128,77) extract with iterative Hamming + parity-guided repair
|
||||
* and diagnostic logging.
|
||||
* See ETSI TS 102 361-1 B.2.1
|
||||
*
|
||||
* @note : See DMR standard ETSI TS 102 361-1 chapter B.2.1 for details
|
||||
*
|
||||
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (128 bytes
|
||||
act as a 16 x 8 byte data matrix)
|
||||
*
|
||||
* @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes)
|
||||
* DMRDataExtracted[0..71] = Util data (hamming corrected)
|
||||
* DMRDataExtracted[72..76] = 5 bit of CRC extracted (to be checked)
|
||||
*
|
||||
* @return 0 = All is OK
|
||||
* Other = Error - Irrecoverable Hamming parity check error number
|
||||
* Enable/disable chatter with BPTC_128x77_DEBUG (1 = on).
|
||||
*/
|
||||
|
||||
// #define BPTC_128x77_DEBUG 1
|
||||
|
||||
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77])
|
||||
{
|
||||
uint32_t i, j, k;
|
||||
@@ -394,102 +466,193 @@ uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDat
|
||||
uint32_t ParityCheckErrorNb = 0;
|
||||
uint32_t NbOfOne;
|
||||
|
||||
/* First step : Reconstitute the BPTC 16x8 matrix */
|
||||
for(i = 0; i < 8; i++)
|
||||
uint8_t row_bad[7];
|
||||
uint8_t col_bad[16];
|
||||
|
||||
#if BPTC_128x77_DEBUG
|
||||
fprintf (stderr, "\n");
|
||||
#endif
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
for(j = 0; j < 16; j++)
|
||||
{
|
||||
/* Only the LSBit of the byte is stored */
|
||||
for (j = 0; j < 16; j++)
|
||||
DataMatrix[i][j] = (InputDataMatrix[i][j] & 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Init the Hamming (16,11,4) library
|
||||
* Not needed because it has already been done
|
||||
* in the "InitAllFecFunction()" function */
|
||||
//Hamming_16_11_4_init();
|
||||
|
||||
/* Process the the Hamming (16,11,4) code
|
||||
* check on each line.
|
||||
* Do not check the last line (line nb 8) */
|
||||
for(i = 0; i < 7; i++)
|
||||
/* ---------- Pass 1: Hamming (16,11,4) on rows 0..6 ---------- */
|
||||
memset(row_bad, 0, sizeof(row_bad));
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
/* Get a full line */
|
||||
for(j = 0; j < 16; j++)
|
||||
{
|
||||
for (j = 0; j < 16; j++)
|
||||
LineUncorrected[j] = DataMatrix[i][j];
|
||||
}
|
||||
|
||||
/* Apply Hamming (16,11,4) code correction */
|
||||
if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
if (Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
row_bad[i] = 1;
|
||||
#if BPTC_128x77_DEBUG
|
||||
fprintf(stderr, " BPTC128 P1 Hamming FAIL row=%u rx=", (unsigned)i);
|
||||
for (j = 0; j < 16; j++)
|
||||
fprintf(stderr, "%u", LineUncorrected[j] & 1);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
for (j = 0; j < 11; j++)
|
||||
DataMatrix[i][j] = LineUncorrected[j];
|
||||
}
|
||||
|
||||
/* Re-inject the line in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 11; j++)
|
||||
else
|
||||
{
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
for (j = 0; j < 11; j++)
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
}
|
||||
}
|
||||
#if BPTC_128x77_DEBUG
|
||||
if (HammingIrrecoverableErrorNb)
|
||||
fprintf(stderr, " BPTC128 P1 Hamming irrecoverable rows=%u\n",
|
||||
(unsigned)HammingIrrecoverableErrorNb);
|
||||
#endif
|
||||
|
||||
/* Extract the DMR data (77 bit) from the matrix */
|
||||
k = 0;
|
||||
|
||||
/* 2 first lines */
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[i][j];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5 Next lines */
|
||||
for(i = 2; i < 7; i++)
|
||||
{
|
||||
for(j = 0; j < 10; j++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[i][j];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5 bit of CRC */
|
||||
for(i = 2; i < 7; i++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[i][10];
|
||||
k++;
|
||||
}
|
||||
|
||||
/* Verify the data integrity by checking
|
||||
* all column parity bit */
|
||||
/* ---------- Column even-parity check (row 7) ---------- */
|
||||
memset(col_bad, 0, sizeof(col_bad));
|
||||
ParityCheckErrorNb = 0;
|
||||
for(i = 0; i < 16; i++)
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
NbOfOne = 0;
|
||||
|
||||
/* Get a full column */
|
||||
for(j = 0; j < 7; j++)
|
||||
{
|
||||
for (j = 0; j < 7; j++)
|
||||
NbOfOne += DataMatrix[j][i];
|
||||
}
|
||||
|
||||
/* Check the parity bit (number of "1"
|
||||
* must be even) */
|
||||
if((NbOfOne % 2) != DataMatrix[7][i])
|
||||
if ((NbOfOne % 2) != DataMatrix[7][i])
|
||||
{
|
||||
ParityCheckErrorNb++;
|
||||
col_bad[i] = 1;
|
||||
#if BPTC_128x77_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC128 P1 parity FAIL col=%u sum7=%u parity_bit=%u (expect even)\n",
|
||||
(unsigned)i, (unsigned)NbOfOne, (unsigned)DataMatrix[7][i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if BPTC_128x77_DEBUG
|
||||
if (ParityCheckErrorNb)
|
||||
fprintf(stderr, " BPTC128 P1 parity bad columns=%u\n",
|
||||
(unsigned)ParityCheckErrorNb);
|
||||
#endif
|
||||
|
||||
/* ---------- Parity-guided single-bit repair ---------- */
|
||||
{
|
||||
uint32_t n_bad_rows = 0, n_bad_cols = 0;
|
||||
uint32_t br = 0, bc = 0;
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
if (row_bad[i]) { n_bad_rows++; br = i; }
|
||||
for (i = 0; i < 16; i++)
|
||||
if (col_bad[i]) { n_bad_cols++; bc = i; }
|
||||
|
||||
#if BPTC_128x77_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC128 repair candidates: bad_rows=%u bad_cols=%u",
|
||||
(unsigned)n_bad_rows, (unsigned)n_bad_cols);
|
||||
if (n_bad_rows == 1)
|
||||
fprintf(stderr, " row=%u", (unsigned)br);
|
||||
if (n_bad_cols == 1)
|
||||
fprintf(stderr, " col=%u", (unsigned)bc);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
|
||||
if (n_bad_rows == 1 && n_bad_cols == 1 && bc < 11)
|
||||
{
|
||||
DataMatrix[br][bc] ^= 1;
|
||||
row_bad[br] = 0;
|
||||
#if BPTC_128x77_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC128 parity-guided FLIP at row=%u col=%u -> %u\n",
|
||||
(unsigned)br, (unsigned)bc, (unsigned)DataMatrix[br][bc]);
|
||||
#endif
|
||||
}
|
||||
#if BPTC_128x77_DEBUG
|
||||
else if (n_bad_rows || n_bad_cols)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" BPTC128 no single-intersection flip "
|
||||
"(need exactly 1 bad row and 1 data col)\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---------- Pass 2: Hamming (16,11,4) again ---------- */
|
||||
HammingIrrecoverableErrorNb = 0;
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
for (j = 0; j < 16; j++)
|
||||
LineUncorrected[j] = DataMatrix[i][j];
|
||||
|
||||
if (Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
#if BPTC_128x77_DEBUG
|
||||
fprintf(stderr, " BPTC128 P2 Hamming FAIL row=%u rx=", (unsigned)i);
|
||||
for (j = 0; j < 16; j++)
|
||||
fprintf(stderr, "%u", LineUncorrected[j] & 1);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
for (j = 0; j < 11; j++)
|
||||
DataMatrix[i][j] = LineUncorrected[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j = 0; j < 11; j++)
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
}
|
||||
}
|
||||
#if BPTC_128x77_DEBUG
|
||||
if (HammingIrrecoverableErrorNb)
|
||||
fprintf(stderr, " BPTC128 P2 Hamming irrecoverable rows=%u\n",
|
||||
(unsigned)HammingIrrecoverableErrorNb);
|
||||
#endif
|
||||
|
||||
/* ---------- Final column parity recount ---------- */
|
||||
ParityCheckErrorNb = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
NbOfOne = 0;
|
||||
for (j = 0; j < 7; j++)
|
||||
NbOfOne += DataMatrix[j][i];
|
||||
|
||||
if ((NbOfOne % 2) != DataMatrix[7][i])
|
||||
{
|
||||
ParityCheckErrorNb++;
|
||||
#if BPTC_128x77_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC128 P2 parity FAIL col=%u sum7=%u parity_bit=%u\n",
|
||||
(unsigned)i, (unsigned)NbOfOne, (unsigned)DataMatrix[7][i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of irrecoverable Hamming errors +
|
||||
* the number of parity check error */
|
||||
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
|
||||
} /* End BPTC_128x77_Extract_Data() */
|
||||
#if BPTC_128x77_DEBUG
|
||||
fprintf(stderr,
|
||||
" BPTC128 result: Hamming_err=%u parity_err=%u total=%u\n",
|
||||
(unsigned)HammingIrrecoverableErrorNb,
|
||||
(unsigned)ParityCheckErrorNb,
|
||||
(unsigned)(HammingIrrecoverableErrorNb + ParityCheckErrorNb));
|
||||
#endif
|
||||
|
||||
/* ---------- Extract 77 bits ---------- */
|
||||
k = 0;
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
for (j = 0; j < 11; j++)
|
||||
DMRDataExtracted[k++] = DataMatrix[i][j];
|
||||
}
|
||||
for (i = 2; i < 7; i++)
|
||||
{
|
||||
for (j = 0; j < 10; j++)
|
||||
DMRDataExtracted[k++] = DataMatrix[i][j];
|
||||
}
|
||||
for (i = 2; i < 7; i++)
|
||||
DMRDataExtracted[k++] = DataMatrix[i][10];
|
||||
|
||||
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief : This function extract the 32 bits of a deinteleaved 32 bits
|
||||
@@ -577,6 +740,52 @@ uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDat
|
||||
|
||||
} /* End BPTC_16x2_Extract_Data() */
|
||||
|
||||
uint32_t BPTC_128x77_Check_Errors(uint8_t InputDataMatrix[8][16])
|
||||
{
|
||||
uint32_t i, j;
|
||||
uint32_t ham_bad = 0, ham_fix = 0, par_err = 0;
|
||||
uint8_t line[16], before[16], decoded[11];
|
||||
uint32_t ones;
|
||||
int changed;
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
before[j] = (InputDataMatrix[i][j] & 1);
|
||||
line[j] = before[j];
|
||||
}
|
||||
|
||||
if (Hamming_16_11_4_decode(line, decoded, 1) == false)
|
||||
{
|
||||
ham_bad++;
|
||||
}
|
||||
else
|
||||
{
|
||||
changed = 0;
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
if (line[j] != before[j])
|
||||
{
|
||||
changed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
ham_fix++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
ones = 0;
|
||||
for (j = 0; j < 7; j++)
|
||||
ones += (InputDataMatrix[j][i] & 1);
|
||||
if ((ones % 2) != (InputDataMatrix[7][i] & 1))
|
||||
par_err++;
|
||||
}
|
||||
|
||||
return ham_bad + ham_fix + par_err;
|
||||
}
|
||||
|
||||
/* End of file */
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "dsd.h"
|
||||
#include "bp.h"
|
||||
|
||||
#define AES_BLOCKLEN 16
|
||||
unsigned Nb = 4;
|
||||
@@ -510,6 +511,57 @@ void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
|
||||
|
||||
}
|
||||
|
||||
//Vertex Standard "AES" Encryption - convert AES key into a round key at 256 Nb,Nk,Nr values
|
||||
//then select entries from the BP table to invert and convert to a keystream
|
||||
void vertex_256_keystream_creation(dsd_state * state, char * key_string)
|
||||
{
|
||||
|
||||
Nb = 4;
|
||||
Nk = 8;
|
||||
Nr = 14;
|
||||
|
||||
uint8_t key[32];
|
||||
memset (key, 0, sizeof(key));
|
||||
|
||||
parse_raw_user_string(key_string, key);
|
||||
|
||||
struct AES_ctx ctx;
|
||||
KeyExpansion(ctx.RoundKey, key);
|
||||
|
||||
uint64_t vtx_key = 0;
|
||||
|
||||
//use the bytes from round key output to select 4 BP key 16-bit words
|
||||
for (int i = 224; i < 228; i++)
|
||||
{
|
||||
vtx_key <<= 16;
|
||||
vtx_key |= BPK[ctx.RoundKey[i]];
|
||||
}
|
||||
|
||||
//invert value
|
||||
vtx_key = ~vtx_key;
|
||||
|
||||
fprintf (stderr,"Vertex Round Key Bytes: ");
|
||||
for (uint16_t i = 224; i < 228; i++)
|
||||
fprintf (stderr, "%02X ", ctx.RoundKey[i]);
|
||||
fprintf (stderr, "Vertex Keystream: %016llX;", (unsigned long long int)vtx_key);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
uint8_t vtx_bytes[8];
|
||||
memset (vtx_bytes, 0, sizeof(vtx_bytes));
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
vtx_bytes[i] = (vtx_key >> (56-(i*8))) & 0xFF;
|
||||
|
||||
unpack_byte_array_into_bit_array(vtx_bytes, state->static_ks_bits[0], 8);
|
||||
unpack_byte_array_into_bit_array(vtx_bytes, state->static_ks_bits[1], 8);
|
||||
|
||||
//hex representation of the effective keystream value
|
||||
state->vtx256_ekey = convert_bits_into_output(state->static_ks_bits[0], 64);
|
||||
|
||||
state->vtx_key_loaded = 1;
|
||||
|
||||
}
|
||||
|
||||
//byte-wise output of AES OFB Keystream
|
||||
//input iv is a 16-byte uint8_t array of initialization vector
|
||||
//input key is up to 32-byte uint8_t array of key value
|
||||
|
||||
@@ -287,7 +287,7 @@ void des_cipher (uint8_t * main_key, uint8_t * input_register, uint8_t * output_
|
||||
memcpy (left, initial_permutation+0, sizeof(left));
|
||||
memcpy (right, initial_permutation+4, sizeof(right));
|
||||
|
||||
//16 fiestel rounds
|
||||
//16 feistel rounds
|
||||
for (uint8_t f = 0; f < 16; f++)
|
||||
{
|
||||
|
||||
@@ -339,7 +339,102 @@ void des_cipher (uint8_t * main_key, uint8_t * input_register, uint8_t * output_
|
||||
memcpy (left, right, sizeof(left));
|
||||
memcpy (right, rperm, sizeof(right));
|
||||
|
||||
} //end fiestel rounds f
|
||||
} //end feistel rounds f
|
||||
|
||||
//combine right and left into the pre_end_permutation array
|
||||
memcpy (pre_end_permutation+0, right, sizeof(right));
|
||||
memcpy (pre_end_permutation+4, left, sizeof(left));
|
||||
|
||||
//carry out the final permuation to get the completed output_register for this cipher iteration
|
||||
permute (pre_end_permutation, output_register, final_register_permutation, 0, 64);
|
||||
|
||||
}
|
||||
|
||||
//kirisun variation on this cipher w/ preconfigured unique key schedule
|
||||
void ksun_des_cipher (uint8_t * key, uint8_t * input_register, uint8_t * output_register, uint8_t de)
|
||||
{
|
||||
|
||||
//starting and ending permutations
|
||||
uint8_t initial_permutation[8]; memset(initial_permutation, 0, sizeof(initial_permutation));
|
||||
uint8_t pre_end_permutation[8]; memset(pre_end_permutation, 0, sizeof(pre_end_permutation));
|
||||
|
||||
//intermediate left, right, and expansion/selection arrays
|
||||
uint8_t left[4]; memset(left, 0, sizeof(left));
|
||||
uint8_t right[4]; memset(right, 0, sizeof(right));
|
||||
uint8_t rperm[4]; memset(rperm, 0, sizeof(rperm));
|
||||
uint8_t exp[6]; memset(exp, 0, sizeof(exp));
|
||||
uint8_t sel[4]; memset(sel, 0, sizeof(sel));
|
||||
|
||||
//key sets
|
||||
uint8_t Ks[16][6]; memset(Ks, 0, sizeof(Ks));
|
||||
|
||||
//file the key into the keysets
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
for (int j = 0; j < 6; j++)
|
||||
Ks[i][j] = key[(i*6) + j];
|
||||
}
|
||||
|
||||
//permute the input_register with the ip (initial_register_permutation) table
|
||||
permute (input_register, initial_permutation, initial_register_permutation, 0, 64);
|
||||
|
||||
//copy the initial permutation to left and right half arrays
|
||||
memcpy (left, initial_permutation+0, sizeof(left));
|
||||
memcpy (right, initial_permutation+4, sizeof(right));
|
||||
|
||||
//16 feistel rounds
|
||||
for (uint8_t f = 0; f < 16; f++)
|
||||
{
|
||||
|
||||
//reset rperm and exp arrays
|
||||
memset(rperm, 0, sizeof(rperm));
|
||||
memset(exp, 0, sizeof(exp));
|
||||
|
||||
//expand right half from 32-bit to 48-bit
|
||||
permute (right, exp, message_expansion, 0, 48);
|
||||
|
||||
//apply current iteration keyset to the expanded array (encryption cycles Ks forward, decryption goes in reverse)
|
||||
if (de == 1)
|
||||
{
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
exp[i] ^= Ks[f][i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
exp[i] ^= Ks[15-f][i];
|
||||
}
|
||||
|
||||
//extract 6 bits for each sbox swap from the expanded array
|
||||
uint8_t e1 = exp[0] >> 2;
|
||||
uint8_t e2 = ((exp[0] & 0x3) << 4) | (exp[1] >> 4);
|
||||
uint8_t e3 = ((exp[1] & 0xF) << 2) | (exp[2] >> 6);
|
||||
uint8_t e4 = exp[2] & 0x3F;
|
||||
|
||||
uint8_t e5 = exp[3] >> 2;
|
||||
uint8_t e6 = ((exp[3] & 0x3) << 4) | (exp[4] >> 4);
|
||||
uint8_t e7 = ((exp[4] & 0xF) << 2) | (exp[5] >> 6);
|
||||
uint8_t e8 = exp[5] & 0x3F;
|
||||
|
||||
//select 32-bits of each 48-bit expansion via modified S Box Look Up Tables
|
||||
//swap the 6 bits for 4 bits via SLUT tables and OR them together into a selection array
|
||||
sel[0] = SLUT1[e1] | SLUT2[e2];
|
||||
sel[1] = SLUT3[e3] | SLUT4[e4];
|
||||
sel[2] = SLUT5[e5] | SLUT6[e6];
|
||||
sel[3] = SLUT7[e7] | SLUT8[e8];
|
||||
|
||||
//permute the selected 32-bits into a new right half
|
||||
permute (sel, rperm, right_half_permutation, 0, 32);
|
||||
|
||||
//xor new right half against left half
|
||||
for (uint8_t i = 0; i < 4; i++)
|
||||
rperm[i] ^= left[i];
|
||||
|
||||
//swap old right half to left, set new right half to rperm
|
||||
memcpy (left, right, sizeof(left));
|
||||
memcpy (right, rperm, sizeof(right));
|
||||
|
||||
} //end feistel rounds f
|
||||
|
||||
//combine right and left into the pre_end_permutation array
|
||||
memcpy (pre_end_permutation+0, right, sizeof(right));
|
||||
|
||||
228
src/crypt-etc.c
228
src/crypt-etc.c
@@ -1,4 +1,5 @@
|
||||
#include "dsd.h"
|
||||
#include "dmr_const.h"
|
||||
|
||||
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input)
|
||||
{
|
||||
@@ -92,6 +93,9 @@ void straight_mod_xor_keystream_creation(dsd_state * state, char * input)
|
||||
}
|
||||
else goto END_KS;
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "\ncurr: %s\n", curr);
|
||||
|
||||
uint8_t ks_bytes[112];
|
||||
memset (ks_bytes, 0, sizeof(ks_bytes));
|
||||
parse_raw_user_string(curr, ks_bytes);
|
||||
@@ -122,5 +126,229 @@ void straight_mod_xor_keystream_creation(dsd_state * state, char * input)
|
||||
|
||||
if (curr == NULL)
|
||||
fprintf (stderr, "Straight KS String Malformed! No KS Created!\n");
|
||||
else
|
||||
{
|
||||
curr = strtok(NULL, ":"); //optional discard value
|
||||
if (curr != NULL)
|
||||
{
|
||||
len = 0;
|
||||
sscanf (curr, "%hd", &len);
|
||||
if (len != 0 && len < 8)
|
||||
{
|
||||
state->straight_discard = (int)len;
|
||||
fprintf (stderr, "Optional Straight XOR Keystream Discard: %d; \n", len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void caltta_bp_keystream_creation(dsd_state * state, char * input)
|
||||
{
|
||||
uint16_t len = 0;
|
||||
char * curr;
|
||||
curr = strtok(input, ":"); //should be len (mod) of key (decimal)
|
||||
if (curr != NULL)
|
||||
sscanf (curr, "%hd", &len);
|
||||
else goto END_KS;
|
||||
|
||||
//len sanity check, can't be greater than 882
|
||||
if (len > 256)
|
||||
len = 256;
|
||||
|
||||
curr = strtok(NULL, ":"); //should be key in hex
|
||||
if (curr != NULL)
|
||||
{
|
||||
//continue
|
||||
}
|
||||
else goto END_KS;
|
||||
|
||||
uint8_t ks_bytes[112];
|
||||
memset (ks_bytes, 0, sizeof(ks_bytes));
|
||||
parse_raw_user_string(curr, ks_bytes);
|
||||
|
||||
uint8_t ks_bits[896];
|
||||
memset(ks_bits, 0, sizeof(ks_bits));
|
||||
|
||||
uint16_t unpack_len = len / 8;
|
||||
if (len % 8)
|
||||
unpack_len++;
|
||||
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, unpack_len);
|
||||
|
||||
for (uint16_t i = 0; i < len; i++)
|
||||
{
|
||||
state->static_ks_bits[0][i] = ks_bits[i];
|
||||
state->static_ks_bits[1][i] = ks_bits[i];
|
||||
}
|
||||
|
||||
fprintf (stderr,"Caltta BP %d-bit Keystream: ", len);
|
||||
for (uint16_t i = 0; i < unpack_len; i++)
|
||||
fprintf (stderr, "%02X", ks_bytes[i]);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
state->straight_ks = 0x26;
|
||||
state->straight_mod = (int)len;
|
||||
|
||||
END_KS:
|
||||
|
||||
if (curr == NULL)
|
||||
fprintf (stderr, "Caltta BP KS String Malformed! No KS Created!\n");
|
||||
|
||||
}
|
||||
|
||||
void hytera_bp_keystream_creation(dsd_state * state, char * input)
|
||||
{
|
||||
uint16_t len = 0;
|
||||
char * curr;
|
||||
curr = strtok(input, ":"); //should be len (mod) of key (decimal)
|
||||
if (curr != NULL)
|
||||
sscanf (curr, "%hd", &len);
|
||||
else goto END_KS;
|
||||
|
||||
//len sanity check, can't be greater than 882
|
||||
if (len > 256)
|
||||
len = 256;
|
||||
|
||||
curr = strtok(NULL, ":"); //should be key in hex
|
||||
if (curr != NULL)
|
||||
{
|
||||
//continue
|
||||
}
|
||||
else goto END_KS;
|
||||
|
||||
uint8_t ks_bytes[112];
|
||||
memset (ks_bytes, 0, sizeof(ks_bytes));
|
||||
parse_raw_user_string(curr, ks_bytes);
|
||||
|
||||
uint8_t ks_bits[896];
|
||||
memset(ks_bits, 0, sizeof(ks_bits));
|
||||
|
||||
uint16_t unpack_len = len / 8;
|
||||
if (len % 8)
|
||||
unpack_len++;
|
||||
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, unpack_len);
|
||||
|
||||
for (uint16_t i = 0; i < len; i++)
|
||||
{
|
||||
state->static_ks_bits[0][i] = ks_bits[i];
|
||||
state->static_ks_bits[1][i] = ks_bits[i];
|
||||
}
|
||||
|
||||
fprintf (stderr,"Hytera BP %d-bit Keystream: ", len);
|
||||
for (uint16_t i = 0; i < unpack_len; i++)
|
||||
fprintf (stderr, "%02X", ks_bytes[i]);
|
||||
|
||||
state->straight_ks = 0x68;
|
||||
state->straight_mod = (int)len;
|
||||
|
||||
END_KS:
|
||||
|
||||
if (curr == NULL)
|
||||
fprintf (stderr, "Hytera BP KS String Malformed! No KS Created!\n");
|
||||
|
||||
}
|
||||
|
||||
//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++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void vtx256_ambe2_interleave_inversion(char ambe_fr[4][24])
|
||||
{
|
||||
|
||||
uint32_t silence_check = 0;
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
silence_check <<= 1;
|
||||
silence_check |= (uint8_t)ambe_fr[0][23-i];
|
||||
}
|
||||
|
||||
if (silence_check == 0xF8014)
|
||||
goto END_INV;
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
//invert the bits in the 49 to 71 position
|
||||
for (int8_t i = 49; i < 72; i++)
|
||||
interleaved[i] ^= 1;
|
||||
|
||||
//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++;
|
||||
}
|
||||
|
||||
END_INV: ; //do nothing
|
||||
|
||||
}
|
||||
1242
src/crypt-kir.c
Normal file
1242
src/crypt-kir.c
Normal file
File diff suppressed because it is too large
Load Diff
458
src/crypt-md5.c
Normal file
458
src/crypt-md5.c
Normal file
@@ -0,0 +1,458 @@
|
||||
//MD5 Code Originally Sourced From: https://github.com/Zunawe/md5-c
|
||||
|
||||
/*
|
||||
* Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm
|
||||
* and modified slightly to be functionally identical but condensed into control structures.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
typedef struct{
|
||||
uint64_t size; // Size of input in bytes
|
||||
uint32_t buffer[4]; // Current accumulation of hash
|
||||
uint8_t input[64]; // Input to be used in the next step
|
||||
uint8_t digest[16]; // Result of algorithm
|
||||
}MD5Context;
|
||||
|
||||
/*
|
||||
* Constants defined by the MD5 algorithm
|
||||
*/
|
||||
#define A 0x67452301
|
||||
#define B 0xefcdab89
|
||||
#define C 0x98badcfe
|
||||
#define D 0x10325476
|
||||
|
||||
static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
|
||||
|
||||
static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
|
||||
|
||||
/*
|
||||
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
|
||||
*/
|
||||
static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/*
|
||||
* Bit-manipulation functions defined by the MD5 algorithm
|
||||
*/
|
||||
#define F(X, Y, Z) ((X & Y) | (~X & Z))
|
||||
#define G(X, Y, Z) ((X & Z) | (Y & ~Z))
|
||||
#define H(X, Y, Z) (X ^ Y ^ Z)
|
||||
#define I(X, Y, Z) (Y ^ (X | ~Z))
|
||||
|
||||
/*
|
||||
* Rotates a 32-bit word left by n bits
|
||||
*/
|
||||
uint32_t rotateLeft(uint32_t x, uint32_t n){
|
||||
return (x << n) | (x >> (32 - n));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize a context
|
||||
*/
|
||||
void md5Init(MD5Context *ctx){
|
||||
ctx->size = (uint64_t)0;
|
||||
|
||||
ctx->buffer[0] = (uint32_t)A;
|
||||
ctx->buffer[1] = (uint32_t)B;
|
||||
ctx->buffer[2] = (uint32_t)C;
|
||||
ctx->buffer[3] = (uint32_t)D;
|
||||
}
|
||||
|
||||
/*
|
||||
* Step on 512 bits of input with the main MD5 algorithm.
|
||||
*/
|
||||
void md5Step(uint32_t *buffer, uint32_t *input){
|
||||
uint32_t AA = buffer[0];
|
||||
uint32_t BB = buffer[1];
|
||||
uint32_t CC = buffer[2];
|
||||
uint32_t DD = buffer[3];
|
||||
|
||||
uint32_t E;
|
||||
|
||||
unsigned int j;
|
||||
|
||||
for(unsigned int i = 0; i < 64; ++i){
|
||||
switch(i / 16){
|
||||
case 0:
|
||||
E = F(BB, CC, DD);
|
||||
j = i;
|
||||
break;
|
||||
case 1:
|
||||
E = G(BB, CC, DD);
|
||||
j = ((i * 5) + 1) % 16;
|
||||
break;
|
||||
case 2:
|
||||
E = H(BB, CC, DD);
|
||||
j = ((i * 3) + 5) % 16;
|
||||
break;
|
||||
default:
|
||||
E = I(BB, CC, DD);
|
||||
j = (i * 7) % 16;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp = DD;
|
||||
DD = CC;
|
||||
CC = BB;
|
||||
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);
|
||||
AA = temp;
|
||||
}
|
||||
|
||||
buffer[0] += AA;
|
||||
buffer[1] += BB;
|
||||
buffer[2] += CC;
|
||||
buffer[3] += DD;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add some amount of input to the context
|
||||
*
|
||||
* If the input fills out a block of 512 bits, apply the algorithm (md5Step)
|
||||
* and save the result in the buffer. Also updates the overall size.
|
||||
*/
|
||||
void md5Update(MD5Context *ctx, uint8_t *input_buffer, size_t input_len){
|
||||
uint32_t input[16];
|
||||
unsigned int offset = ctx->size % 64;
|
||||
ctx->size += (uint64_t)input_len;
|
||||
|
||||
// Copy each byte in input_buffer into the next space in our context input
|
||||
for(unsigned int i = 0; i < input_len; ++i){
|
||||
ctx->input[offset++] = (uint8_t)*(input_buffer + i);
|
||||
|
||||
// If we've filled our context input, copy it into our local array input
|
||||
// then reset the offset to 0 and fill in a new buffer.
|
||||
// Every time we fill out a chunk, we run it through the algorithm
|
||||
// to enable some back and forth between cpu and i/o
|
||||
if(offset % 64 == 0){
|
||||
for(unsigned int j = 0; j < 16; ++j){
|
||||
// Convert to little-endian
|
||||
// The local variable `input` our 512-bit chunk separated into 32-bit words
|
||||
// we can use in calculations
|
||||
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
|
||||
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
|
||||
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
|
||||
(uint32_t)(ctx->input[(j * 4)]);
|
||||
}
|
||||
md5Step(ctx->buffer, input);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Pad the current input to get to 448 bytes, append the size in bits to the very end,
|
||||
* and save the result of the final iteration into digest.
|
||||
*/
|
||||
void md5Finalize(MD5Context *ctx){
|
||||
uint32_t input[16];
|
||||
unsigned int offset = ctx->size % 64;
|
||||
unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
|
||||
|
||||
// Fill in the padding and undo the changes to size that resulted from the update
|
||||
md5Update(ctx, PADDING, padding_length);
|
||||
ctx->size -= (uint64_t)padding_length;
|
||||
|
||||
// Do a final update (internal to this function)
|
||||
// Last two 32-bit words are the two halves of the size (converted from bytes to bits)
|
||||
for(unsigned int j = 0; j < 14; ++j){
|
||||
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
|
||||
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
|
||||
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
|
||||
(uint32_t)(ctx->input[(j * 4)]);
|
||||
}
|
||||
input[14] = (uint32_t)(ctx->size * 8);
|
||||
input[15] = (uint32_t)((ctx->size * 8) >> 32);
|
||||
|
||||
md5Step(ctx->buffer, input);
|
||||
|
||||
// Move the result into digest (convert from little-endian)
|
||||
for(unsigned int i = 0; i < 4; ++i){
|
||||
ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF));
|
||||
ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >> 8);
|
||||
ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16);
|
||||
ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions that run the algorithm on the provided input and put the digest into result.
|
||||
* result should be able to store 16 bytes.
|
||||
*/
|
||||
void md5String(char *input, uint8_t *result){
|
||||
MD5Context ctx;
|
||||
md5Init(&ctx);
|
||||
md5Update(&ctx, (uint8_t *)input, strlen(input));
|
||||
md5Finalize(&ctx);
|
||||
|
||||
memcpy(result, ctx.digest, 16);
|
||||
}
|
||||
|
||||
void md5File(FILE *file, uint8_t *result){
|
||||
char *input_buffer = malloc(1024);
|
||||
size_t input_size = 0;
|
||||
|
||||
MD5Context ctx;
|
||||
md5Init(&ctx);
|
||||
|
||||
while((input_size = fread(input_buffer, 1, 1024, file)) > 0){
|
||||
md5Update(&ctx, (uint8_t *)input_buffer, input_size);
|
||||
}
|
||||
|
||||
md5Finalize(&ctx);
|
||||
|
||||
free(input_buffer);
|
||||
|
||||
memcpy(result, ctx.digest, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* RAS 104-bit permutation
|
||||
*
|
||||
* Generates a permutation of bit positions 0..103 using the 32-bit seed
|
||||
* (the last four active key bytes interpreted as a big-endian uint32).
|
||||
*
|
||||
* Algorithm:
|
||||
* - Maintain a circular list of the still-available positions.
|
||||
* - Repeatedly select (seed % remaining_count) steps from the current
|
||||
* position, record that index, remove it, and continue from its successor.
|
||||
*
|
||||
* Parameters:
|
||||
* seed - 32-bit seed (e.g. 0x92604BD5)
|
||||
* order - output array of 104 integers. order[i] = source bit position
|
||||
* that should be placed at output bit i.
|
||||
*/
|
||||
void ras_permute_order(uint32_t seed, int * order)
|
||||
{
|
||||
int remaining[104];
|
||||
int count = 104;
|
||||
int pos = 0; /* current index inside the remaining[] array */
|
||||
|
||||
for (int i = 0; i < 104; i++)
|
||||
remaining[i] = i;
|
||||
|
||||
for (int i = 0; i < 104; i++)
|
||||
{
|
||||
unsigned idx = seed % (unsigned)count;
|
||||
int actual = (pos + (int)idx) % count;
|
||||
|
||||
order[i] = remaining[actual];
|
||||
|
||||
#ifdef DEBUG_RAS
|
||||
//debug
|
||||
// fprintf (stderr, "\n I: %d; C: %d; P: %d; IDX: %u; ACT: %d; ORD: %d; ", i, count, pos, idx, actual, order[i]);
|
||||
#endif
|
||||
|
||||
/* remove the chosen entry */
|
||||
for (int j = actual; j < count - 1; j++)
|
||||
remaining[j] = remaining[j + 1];
|
||||
count--;
|
||||
|
||||
/* continue from the successor of the removed element */
|
||||
if (count > 0)
|
||||
pos = actual % count;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_RAS
|
||||
//debug
|
||||
// fprintf(stderr, "\n Order: ");
|
||||
// for (int i = 0; i < 104; i++)
|
||||
// fprintf (stderr, "%03d,", order[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void apply_ras_permutation(const uint8_t * src, uint8_t * dst, const int * order)
|
||||
{
|
||||
for (int i = 0; i < 104; i++)
|
||||
{
|
||||
int src_bit = order[i];
|
||||
int src_byte = src_bit / 8;
|
||||
int src_off = 7 - (src_bit % 8); /* MSB of byte is lowest bit index */
|
||||
|
||||
int bit = (src[src_byte] >> src_off) & 1;
|
||||
|
||||
int dst_byte = i / 8;
|
||||
int dst_off = 7 - (i % 8);
|
||||
|
||||
if (bit)
|
||||
dst[dst_byte] |= (uint8_t)(1u << dst_off);
|
||||
}
|
||||
}
|
||||
|
||||
//Full RAS workflow, return value is the the calculated MAC attachment
|
||||
//input is a pointer to byte-wise payload from dburst of RAS enabled payloads
|
||||
uint32_t ras_mac_calculator(dsd_state * state, uint8_t * input, int input_len, int type)
|
||||
{
|
||||
|
||||
if (state->ras_effective_key == 0)
|
||||
return 0;
|
||||
|
||||
uint32_t mac = 0;
|
||||
uint8_t ras_input[13];
|
||||
memset (ras_input, 0, sizeof(ras_input));
|
||||
|
||||
for (int i = 0; i < input_len; i++)
|
||||
ras_input[i] = input[i];
|
||||
|
||||
int shift = 48;
|
||||
for (int i = input_len; i < 13; i++)
|
||||
{
|
||||
ras_input[i] = (state->ras_effective_key >> shift) & 0xFF;
|
||||
shift -= 8;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_RAS
|
||||
//debug input value
|
||||
if (type != 2)
|
||||
{
|
||||
fprintf (stderr, "\n RAS Input: ");
|
||||
for (int i = 0; i < 13; i++)
|
||||
fprintf (stderr, "%02X", ras_input[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t ras_output[16]; memset(ras_output, 0, sizeof(ras_output));
|
||||
|
||||
if (type != 2)
|
||||
{
|
||||
apply_ras_permutation(ras_input, ras_output, state->ras_permutation_order);
|
||||
|
||||
#ifdef DEBUG_RAS
|
||||
//debug output value
|
||||
fprintf (stderr, "\n RAS Output: ");
|
||||
for (int i = 0; i < 13; i++)
|
||||
fprintf (stderr, "%02X", ras_output[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
//Run Appropriate CRC or RS calculation on Permuted Output
|
||||
uint8_t output_bits[104]; memset(output_bits, 0, sizeof(output_bits));
|
||||
unpack_byte_array_into_bit_array(ras_output, output_bits, 13);
|
||||
|
||||
if (type == 0)
|
||||
mac = ComputeCrcCCITT16d(output_bits, 13*8);
|
||||
else if (type == 1)
|
||||
{
|
||||
rs_16_13_encode(ras_output);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
mac <<= 8;
|
||||
mac |= ras_output[i+13];
|
||||
}
|
||||
}
|
||||
else if (type == 2)
|
||||
{
|
||||
mac = ComputeCrc5Bit(input);
|
||||
mac += state->ras_effective_key & 0x1F;
|
||||
mac %= 31;
|
||||
}
|
||||
|
||||
return mac;
|
||||
|
||||
}
|
||||
|
||||
void ras_effective_key_creation(dsd_state * state, char * input)
|
||||
{
|
||||
|
||||
uint8_t md5_key[30];
|
||||
memset (md5_key, 0, sizeof(md5_key));
|
||||
|
||||
uint16_t len = strlen((const char*)input);
|
||||
|
||||
//sanity check on provided char len
|
||||
if (len > 24)
|
||||
len = 24;
|
||||
|
||||
//load chars as uint8_t into key array
|
||||
for (int i = 0; i < len; i++)
|
||||
md5_key[i] = (uint8_t)input[i];
|
||||
|
||||
//debug
|
||||
fprintf (stderr, "RAS Key String: %s; LEN: %d; HEX: ", input, len);
|
||||
for (int i = 0; i < len; i++)
|
||||
fprintf (stderr, "%02X ", md5_key[i]);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
MD5Context ctx;
|
||||
md5Init(&ctx);
|
||||
md5Update(&ctx, md5_key, len);
|
||||
md5Finalize(&ctx);
|
||||
|
||||
//debug dump the digest
|
||||
fprintf (stderr, "MD5: ");
|
||||
for (int i = 0; i < 16; i++)
|
||||
fprintf (stderr, "%02X ", ctx.digest[i]);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
//collect 7 bytes of the MD5 Hash to create the flat key value
|
||||
unsigned long long int flat_key = 0;
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
flat_key <<= 8;
|
||||
flat_key |= ctx.digest[i];
|
||||
}
|
||||
|
||||
//On real world testing, it was discovered that by selecting a password (VluByDZhkPenguinkeeper) which has
|
||||
//an MD5 partial hash of FFFFFFFFFEDFFF, the value actually placed into the radio was FFFFFFFFFEE001
|
||||
//this indicates the entire value will roll-over, and not just a byte rollvoer (mod)
|
||||
|
||||
//The known rule is that the last 5 bits of the flat key value (prior to reverse)
|
||||
//is that it cannot equal 0x1F (all 1 bits), if so, a correction of +2 is
|
||||
//performaed onto a flat value, which increments the entire value appropriately
|
||||
|
||||
//Below patent also suggest all bytes cannot equal zero
|
||||
//and if so, the next portion of MD5 may be used,
|
||||
//or some other method may be utilized to prevent fringe cases
|
||||
//https://patents.google.com/patent/US20130288643A1
|
||||
|
||||
if ((flat_key & 0x1F) == 0x1F)
|
||||
{
|
||||
fprintf (stderr, "Flat Correction: %014llX -> %014llX;\n", flat_key, flat_key+2);
|
||||
flat_key += 2;
|
||||
}
|
||||
|
||||
//byte-reverse the flat_key into the effective_key
|
||||
unsigned long long int effective_key = 0;
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
effective_key <<= 8;
|
||||
effective_key |= (flat_key >> (i*8)) & 0xFF;
|
||||
}
|
||||
|
||||
uint32_t ras_seed = effective_key & 0xFFFFFFFF;
|
||||
|
||||
ras_permute_order(ras_seed, state->ras_permutation_order);
|
||||
|
||||
//debug
|
||||
fprintf (stderr, "RAS Effective Key Value: %014llX;", effective_key);
|
||||
|
||||
state->ras_effective_key = effective_key;
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
}
|
||||
468
src/crypt-pc5.c
Normal file
468
src/crypt-pc5.c
Normal file
@@ -0,0 +1,468 @@
|
||||
#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
Normal file
404
src/crypt-rc2.c
Normal file
@@ -0,0 +1,404 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
1378
src/crypt-rc4.c
1378
src/crypt-rc4.c
File diff suppressed because it is too large
Load Diff
@@ -159,28 +159,96 @@ 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[16] = {0};
|
||||
unsigned char key2[16] = {0};
|
||||
|
||||
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;
|
||||
|
||||
u64_to_bytes_be(K1, &key1[0]);
|
||||
u64_to_bytes_be(K2, &key1[8]);
|
||||
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++) key2[i] = key1[15-i];
|
||||
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;
|
||||
}
|
||||
|
||||
/* Create key schedule */
|
||||
create_keys(&ctx, key2, sizeof(key2));
|
||||
ctx.rounds = nbround;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -238,4 +306,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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
476
src/dmr_34.c
476
src/dmr_34.c
@@ -1,12 +1,35 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_34.c
|
||||
* DMR (and P25) 3/4 Rate Simple Trellis Decoder
|
||||
* DMR (and P25) 3/4 Rate Viterbi Trellis Decoder (Improved)
|
||||
*
|
||||
* Code Sourced From: Unknown
|
||||
* No License Provided
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2023-12 DSD-FME Florida Man Edition
|
||||
* 2026-02 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,
|
||||
@@ -14,111 +37,288 @@ 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};
|
||||
|
||||
//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 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 }
|
||||
|
||||
};
|
||||
|
||||
//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)
|
||||
static int16_t staticY[INSTATIC][INRANGE] =
|
||||
{
|
||||
int i, j, k, counter, best_p, best_v, survivors;
|
||||
uint8_t temp_s, tri, t;
|
||||
{ -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 }
|
||||
|
||||
//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;
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
int i, j;
|
||||
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;
|
||||
uint32_t irr_err = 0; //irrecoverable errors
|
||||
|
||||
uint8_t deinterleaved_dibits[98];
|
||||
@@ -135,80 +335,20 @@ uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
|
||||
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));
|
||||
uint16_t pindata[49]; memset (pindata, 0, sizeof(pindata));
|
||||
uint16_t outdata[18]; memset (outdata, 0, sizeof(outdata));
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
point[i] = constellation_map[nibs[i]];
|
||||
for (int i = 0; i < 49; i++)
|
||||
pindata[i] = (uint16_t)nibs[i];
|
||||
|
||||
//debug view points
|
||||
// fprintf (stderr, "\n P =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", point[i]);
|
||||
Virterbi(pindata, outdata);
|
||||
|
||||
//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++)
|
||||
int k = 0;
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
|
||||
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--;
|
||||
|
||||
}
|
||||
|
||||
output[k++] = (outdata[i] >> 8) & 0xFF;
|
||||
output[k++] = (outdata[i] >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
//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);
|
||||
return irr_err; //TODO: Need to pull viterbi metrics from upper functions
|
||||
}
|
||||
|
||||
|
||||
254
src/dmr_block.c
254
src/dmr_block.c
@@ -187,6 +187,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||
else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera");
|
||||
else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera");
|
||||
else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot");
|
||||
else if (p_mfid == 0x77) sprintf (mfid_string, "%s", "VTX STD");
|
||||
else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard");
|
||||
else sprintf (mfid_string, "%s", "Other");
|
||||
}
|
||||
@@ -270,13 +271,12 @@ 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, "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_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_class == 2) strcat (rsp_string, "SACK - Retry");
|
||||
// if (r_status) strcat (rsp_string, " - %d", r_status);
|
||||
@@ -293,8 +293,19 @@ 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)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
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.
|
||||
|
||||
@@ -335,7 +346,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 (moto) has been observed as the first block of LRRP data (unique fixed format?)
|
||||
//p_sap 1 on mfid 10 MNIS Header
|
||||
if (p_mfid == 0x10 && p_sap == 1)
|
||||
{
|
||||
//add the header to the first 10 bytes of the storage (sans this header's CRC)
|
||||
@@ -365,7 +376,81 @@ 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 == 0x10)
|
||||
if (p_sap != 1 && p_mfid == 0x77)
|
||||
{
|
||||
|
||||
fprintf (stderr, "\n Vertex Standard PDU ENC Header;");
|
||||
|
||||
//This Extended Header only seems to exist to signal encryption,
|
||||
//but is otherwise mostly empty aside from a FID and single 0x01 value
|
||||
//[4F][77][01][00][00][00][00][00][00][00][XX][XX]
|
||||
|
||||
//Further analysis shows that using different key types on different channels
|
||||
//does not change the byte values of this header, and what is reported is that
|
||||
//the Vertex Standard radios only allow one encryption type and key per channel.
|
||||
|
||||
//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;
|
||||
|
||||
//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;
|
||||
|
||||
//reset ks start value
|
||||
state->data_ks_start[slot] = 0;
|
||||
|
||||
}
|
||||
else if (p_sap != 1 && p_mfid == 0x68)
|
||||
{
|
||||
|
||||
//TODO: Assume Alg, Key ID, IV and other values present in this header
|
||||
//if using Hytera Enhanced RC4, otherwise, may use DMRA header
|
||||
//ALG 1 = BP (observed), ALG 2 = Hytera Enhanced (not observed, but speculation)
|
||||
|
||||
/*
|
||||
15:44:00 Sync: +DMR MS/DM MODE/MONO | Color Code=01 | DATA
|
||||
Slot 1 Data Header - Extended - SAP 03 [UDP Comp] - MFID 68 [Hytera]
|
||||
Unknown Extended Header: 0101000000000000
|
||||
DMR PDU Payload [3F][68][01][01][00][00][00][00][00][00][E6][71] <-- key 1
|
||||
|
||||
16:06:21 Sync: +DMR MS/DM MODE/MONO | Color Code=02 | DATA
|
||||
Slot 1 Data Header - Extended - SAP 03 [UDP Comp] - MFID 68 [Hytera]
|
||||
Hytera PDU ENC Header;
|
||||
DMR PDU Payload [3F][68][01][02][00][00][00][00][00][00][3E][F3] <--key 2
|
||||
|
||||
*/
|
||||
|
||||
uint8_t alg = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
|
||||
uint8_t key = (uint8_t)ConvertBitIntoBytes(&dheader_bits[24], 8);
|
||||
uint64_t mi = (uint64_t)ConvertBitIntoBytes(&dheader_bits[32], 40);
|
||||
|
||||
fprintf (stderr, "\n Hytera PDU ENC Header: MFID: %02X; ALG: %02X; Key ID: %02X;", p_mfid, alg, key);
|
||||
if (mi != 0) fprintf (stderr, "MI: %010llX", (unsigned long long int)mi);
|
||||
|
||||
//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;
|
||||
|
||||
//going to use algid of 0x68*alg to signal a Hytera enc method
|
||||
//should be 0x68 for bp, or 0xD0 for enhanced
|
||||
if (state->currentslot == 0)
|
||||
state->payload_algid = 0x68 * alg;
|
||||
else state->payload_algidR = 0x68 * alg;
|
||||
|
||||
if (state->currentslot == 0)
|
||||
state->payload_keyid = key;
|
||||
else state->payload_keyidR = key;
|
||||
|
||||
if (state->currentslot == 0)
|
||||
state->payload_mi = mi;
|
||||
else state->payload_miR = mi;
|
||||
|
||||
//reset ks start value
|
||||
state->data_ks_start[slot] = 0;
|
||||
}
|
||||
else 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)
|
||||
@@ -376,7 +461,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||
else state->dmr_soR = 0x100;
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n PDU ENC Header:");
|
||||
fprintf (stderr, "\n DMRA 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));
|
||||
|
||||
@@ -993,15 +1078,30 @@ 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];
|
||||
uint8_t empt[32];
|
||||
uint8_t maes[16];
|
||||
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));
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
kaes[i+0] = ((state->rkey_array[kid+0x000]) >> (56-(i*8))) & 0xFF;
|
||||
kaes[i+8] = ((state->rkey_array[kid+0x101]) >> (56-(i*8))) & 0xFF;
|
||||
kaes[i+16] = ((state->rkey_array[kid+0x201]) >> (56-(i*8))) & 0xFF;
|
||||
kaes[i+24] = ((state->rkey_array[kid+0x301]) >> (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 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;
|
||||
@@ -1022,19 +1122,27 @@ 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) fprintf (stderr, " MI(32): %08llX;", mi);
|
||||
if (alg != 0 && mi != 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 (alg == 0x68) fprintf (stderr, " Hytera BP;");
|
||||
if (alg == 0xD0) fprintf (stderr, " Hytera EP;");
|
||||
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 == 5)
|
||||
if (alg == 4 || 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)
|
||||
@@ -1049,30 +1157,113 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
decrypted_pdu = 1;
|
||||
}
|
||||
|
||||
if (alg == 5 && akl == 1) //AES-256
|
||||
else if (alg == 0x68) //Hytera BP
|
||||
{
|
||||
//just passing through to see if a key was loaded and the blocks decrypted
|
||||
decrypted_pdu = hytera_bp_data_decrypt(state, empt, 1);
|
||||
}
|
||||
|
||||
else if (alg == 0xD0) //Hytera Enhanced RC4
|
||||
{
|
||||
//just passing through to see if a key was loaded and the blocks decrypted
|
||||
decrypted_pdu = hytera_ep_data_decrypt(opts, state, empt, 1);
|
||||
}
|
||||
|
||||
else if (alg == 7 && state->vtx_key_loaded == 1) //Vertex Standard, either enc type
|
||||
{
|
||||
pack_bit_array_into_byte_array(state->static_ks_bits[slot], ob, 8);
|
||||
end += (poc + 4); //also perform application over padding bytes and crc value
|
||||
for (i = 0; i < end; i++)
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[i%8];
|
||||
decrypted_pdu = 1;
|
||||
|
||||
//Recalcuate CRC32
|
||||
uint8_t reordered_bits[1000];
|
||||
memset (reordered_bits, 0, sizeof(reordered_bits));
|
||||
|
||||
//rearrage for ridiculously stupid CRC32 LSO/MSO ordering
|
||||
for(i = 0, j = 0; i < ctr; i+=2, j+=16)
|
||||
{
|
||||
reordered_bits[j + 0] = (state->dmr_pdu_sf[slot][i+1] >> 7) & 0x01;
|
||||
reordered_bits[j + 1] = (state->dmr_pdu_sf[slot][i+1] >> 6) & 0x01;
|
||||
reordered_bits[j + 2] = (state->dmr_pdu_sf[slot][i+1] >> 5) & 0x01;
|
||||
reordered_bits[j + 3] = (state->dmr_pdu_sf[slot][i+1] >> 4) & 0x01;
|
||||
reordered_bits[j + 4] = (state->dmr_pdu_sf[slot][i+1] >> 3) & 0x01;
|
||||
reordered_bits[j + 5] = (state->dmr_pdu_sf[slot][i+1] >> 2) & 0x01;
|
||||
reordered_bits[j + 6] = (state->dmr_pdu_sf[slot][i+1] >> 1) & 0x01;
|
||||
reordered_bits[j + 7] = (state->dmr_pdu_sf[slot][i+1] >> 0) & 0x01;
|
||||
|
||||
reordered_bits[j + 8] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||
reordered_bits[j + 9] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||
reordered_bits[j + 10] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
||||
reordered_bits[j + 11] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
||||
reordered_bits[j + 12] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
||||
reordered_bits[j + 13] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
||||
reordered_bits[j + 14] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
||||
reordered_bits[j + 15] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||
}
|
||||
CRCComputed = (uint32_t)ComputeCrc32BitV(reordered_bits, (ctr * 8) - 32);
|
||||
CRCExtracted = (uint32_t)convert_bits_into_output(reordered_bits+((ctr * 8) - 32), 32);
|
||||
if (CRCComputed == CRCExtracted)
|
||||
CRCCorrect = 1;
|
||||
else CRCCorrect = 0;
|
||||
|
||||
//set back to original value
|
||||
end -= (poc + 4);
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " CTR: %d; CRC32 CMP: %08X EXT: %08X", ctr, CRCComputed, CRCExtracted);
|
||||
|
||||
}
|
||||
|
||||
else if (alg == 5 && akl == 1 && mi != 0) //AES-256 OFB
|
||||
{
|
||||
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
|
||||
if (alg == 2 && R != 0) //Tait DES
|
||||
else 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;
|
||||
}
|
||||
|
||||
if (alg == 4 && akl == 1) //AES-128
|
||||
else if (alg == 4 && akl == 1 && mi != 0) //AES-128 OFB
|
||||
{
|
||||
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 alg is 1 or 4 AND key is available!
|
||||
//apply keystream here, only if key is available and keystream created, not ECB.
|
||||
if (alg == 1 && R != 0)
|
||||
{
|
||||
for (i = 0; i < end; i++)
|
||||
@@ -1084,15 +1275,17 @@ 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)
|
||||
|
||||
else if (alg == 4 && akl != 0 && mi != 0)
|
||||
{
|
||||
for (i = 0; i < end; i++)
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[(i+16)%3096];
|
||||
}
|
||||
else if (alg == 5 && akl != 0)
|
||||
|
||||
else if (alg == 5 && akl != 0 && mi != 0)
|
||||
{
|
||||
for (i = 0; i < end; i++)
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
|
||||
state->dmr_pdu_sf[slot][i+start] ^= ob[(i+16)%3096];
|
||||
}
|
||||
|
||||
//BP key application
|
||||
@@ -1174,7 +1367,8 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
}
|
||||
else if (state->data_header_sap[slot] == 2 || state->data_header_sap[slot] == 3) //TCP and UDP Compression (may only be 3 UDP compression, unknown)
|
||||
{
|
||||
uint16_t len = ((blocks+1)*block_len)-4; //total number of bytes in PDU minus 4 CRC32 bytes
|
||||
uint8_t poc = state->data_block_poc[slot]; //need to test if others also need this
|
||||
uint16_t len = ((blocks+1)*block_len)-4 - poc; //total number of bytes in PDU minus 4 CRC32 bytes and padding bytes
|
||||
dmr_udp_comp_pdu (opts, state, len, state->dmr_pdu_sf[slot]);
|
||||
}
|
||||
else if (state->data_header_sap[slot] == 1 && state->dmr_pdu_sf[slot][1] == 0x10) //MNIS Proprietary Header
|
||||
@@ -1512,4 +1706,12 @@ void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
|
||||
// sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||
sprintf (state->dmr_lrrp_gps[0], "%s", "");
|
||||
sprintf (state->dmr_lrrp_gps[1], "%s", "");
|
||||
|
||||
//Hytera OTA Encryption
|
||||
memset(state->hota_lc_ctx, 0, sizeof(state->hota_lc_ctx));
|
||||
memset(state->hota_ctx_ok, 0, sizeof(state->hota_ctx_ok));
|
||||
state->hota_sbrc_idx1[0] = -1;
|
||||
state->hota_sbrc_idx2[0] = -1;
|
||||
state->hota_sbrc_idx1[1] = -1;
|
||||
state->hota_sbrc_idx2[1] = -1;
|
||||
}
|
||||
48
src/dmr_bs.c
48
src/dmr_bs.c
@@ -462,6 +462,12 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
if (internalslot == 0 && vc1 == 6)
|
||||
{
|
||||
if (state->hota_enc_check && state->forced_alg_id > 1)
|
||||
{
|
||||
state->dmr_so = 0x40;
|
||||
state->payload_algid = state->forced_alg_id;
|
||||
state->payload_keyid = 0xFF;
|
||||
}
|
||||
//this needs to run prior to embedded link control
|
||||
if (state->payload_algid == 0x02)
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
@@ -475,6 +481,12 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
if (internalslot == 1 && vc2 == 6)
|
||||
{
|
||||
if (state->hota_enc_check && state->forced_alg_id > 1)
|
||||
{
|
||||
state->dmr_soR = 0x40;
|
||||
state->payload_algidR = state->forced_alg_id;
|
||||
state->payload_keyidR = 0xFF;
|
||||
}
|
||||
//this needs to run prior to embedded link control
|
||||
if (state->payload_algidR == 0x02)
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
@@ -502,6 +514,20 @@ 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);
|
||||
}
|
||||
|
||||
if (state->vtx256_ekey != 0)
|
||||
{
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr);
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr2);
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr3);
|
||||
}
|
||||
|
||||
#ifdef PRINT_AMBE72
|
||||
ambe2_codeword_print_i(opts, ambe_fr);
|
||||
ambe2_codeword_print_i(opts, ambe_fr2);
|
||||
@@ -695,8 +721,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else if (state->payload_algid == 0x02)
|
||||
{
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
state->currentslot = 0;
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
if (state->payload_algidR >= 0x21)
|
||||
@@ -706,8 +732,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else if (state->payload_algidR == 0x02)
|
||||
{
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
state->currentslot = 1;
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
|
||||
@@ -954,6 +980,20 @@ 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);
|
||||
}
|
||||
|
||||
if (state->vtx256_ekey != 0)
|
||||
{
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr);
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr2);
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr3);
|
||||
}
|
||||
|
||||
#ifdef PRINT_AMBE72
|
||||
ambe2_codeword_print_i(opts, ambe_fr);
|
||||
ambe2_codeword_print_i(opts, ambe_fr2);
|
||||
@@ -1055,10 +1095,10 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
state->currentslot = 1;
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
else if (state->payload_algid == 0x02)
|
||||
else if (state->payload_algidR == 0x02)
|
||||
{
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
state->currentslot = 1;
|
||||
hytera_enhanced_alg_refresh(state);
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
|
||||
|
||||
@@ -940,14 +940,21 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
//MassReg
|
||||
if (a_type == 4)
|
||||
{
|
||||
uint8_t mreg_res = (uint8_t)ConvertBitIntoBytes(&bpbits1[0], 5);
|
||||
uint8_t reg_window = (uint8_t)ConvertBitIntoBytes(&bpbits1[5], 4); //7.2.19.5.1 Table 7.77 convert raw to seconds
|
||||
uint8_t aloha_mask = (uint8_t)ConvertBitIntoBytes(&bpbits1[9], 5);
|
||||
|
||||
//ADDRNull or MS Individual Address
|
||||
uint8_t reg_address = (uint8_t)ConvertBitIntoBytes(&bpbits2[16], 8); //see clause 7.2.41
|
||||
uint32_t reg_address = bparms2; //see clause 7.2.41
|
||||
|
||||
fprintf (stderr, "\n"); //raw value only on the reg window, see aforementioned table
|
||||
fprintf (stderr, " Reg Window: %X; Aloha Mask: %02X; Target: %d; ", reg_window, aloha_mask, reg_address);
|
||||
dmr_gateway_identifier(0,reg_address);
|
||||
|
||||
if (mreg_res != 0)
|
||||
fprintf (stderr, "Res: %02X", mreg_res);
|
||||
|
||||
if (reg_address != 0)
|
||||
dmr_gateway_identifier(0, reg_address);
|
||||
}
|
||||
|
||||
//debug
|
||||
|
||||
221
src/dmr_dburst.c
221
src/dmr_dburst.c
@@ -6,7 +6,7 @@
|
||||
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2023-12 DSD-FME Florida Man Edition
|
||||
* 2026-09 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
//TODO: Test USBD LIP Decoder with Real World Samples (if/when available)
|
||||
@@ -51,14 +51,15 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
|
||||
//PDU Bytes and Bits
|
||||
uint8_t DMR_PDU[25];
|
||||
uint8_t DMR_PDU_bits[196];
|
||||
uint8_t DMR_PDU_bits[200];
|
||||
memset (DMR_PDU, 0, sizeof (DMR_PDU));
|
||||
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
||||
|
||||
uint8_t R[3];
|
||||
uint8_t BPTCReservedBits = 0;
|
||||
uint8_t is_ras = 0;
|
||||
uint8_t crc_original_validity = 0;
|
||||
uint8_t hota_enc = 0;
|
||||
uint8_t is_ras = 0;
|
||||
uint8_t bptc_res_bits[3];
|
||||
memset (bptc_res_bits, 0, sizeof(bptc_res_bits));
|
||||
uint8_t crc_original_validity = 0;
|
||||
|
||||
uint32_t crcmask = 0;
|
||||
uint8_t crclen = 0;
|
||||
@@ -76,6 +77,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
|
||||
switch(databurst){
|
||||
case 0x00: //PI
|
||||
hota_enc = 1;
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0x6969; //insert pi and 69 jokes here
|
||||
@@ -83,6 +85,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
sprintf(state->fsubtype, " PI ");
|
||||
break;
|
||||
case 0x01: //VLC
|
||||
hota_enc = 1;
|
||||
is_bptc = 1;
|
||||
is_lc = 1;
|
||||
crclen = 24;
|
||||
@@ -91,6 +94,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
sprintf(state->fsubtype, " VLC ");
|
||||
break;
|
||||
case 0x02: //TLC
|
||||
hota_enc = 1;
|
||||
is_bptc = 1;
|
||||
is_lc = 1;
|
||||
crcmask = 0x999999;
|
||||
@@ -99,6 +103,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
sprintf(state->fsubtype, " TLC ");
|
||||
break;
|
||||
case 0x03: //CSBK
|
||||
hota_enc = 1;
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0xA5A5;
|
||||
@@ -106,6 +111,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
sprintf(state->fsubtype, " CSBK");
|
||||
break;
|
||||
case 0x04: //MBC Header
|
||||
hota_enc = 1;
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0xAAAA;
|
||||
@@ -119,6 +125,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
sprintf(state->fsubtype, " MBCC");
|
||||
break;
|
||||
case 0x06: //Data Header
|
||||
hota_enc = 1;
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0xCCCC;
|
||||
@@ -176,6 +183,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
}
|
||||
break;
|
||||
case 0x0B: //Unified Single Block Data USBD
|
||||
hota_enc = 1;
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0x3333;
|
||||
@@ -185,6 +193,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
|
||||
//special types (not real data 'sync' bursts)
|
||||
case 0xEB: //Embedded Signalling
|
||||
hota_enc = 1;
|
||||
crclen = 5;
|
||||
is_emb = 1;
|
||||
pdu_len = 9;
|
||||
@@ -246,28 +255,41 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
|
||||
|
||||
/* Extract the BPTC 196,96 DMR data */
|
||||
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
||||
|
||||
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
|
||||
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x04);
|
||||
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, bptc_res_bits, &is_ras);
|
||||
|
||||
#ifdef DEBUG_RAS
|
||||
//debug print
|
||||
//fprintf (stderr, " RAS? %X - %d %d %d", BPTCReservedBits, R[0], R[1], R[2]);
|
||||
if (bptc_res_bits[0] != 0 || bptc_res_bits[1] != 0 || bptc_res_bits[2] != 0)
|
||||
fprintf (stderr, " BPTC R BITS: %d %d %d", bptc_res_bits[0], bptc_res_bits[1], bptc_res_bits[2]);
|
||||
#endif
|
||||
|
||||
/* Convert the 96 bits BPTC data into 12 bytes */
|
||||
k = 0;
|
||||
for(i = 0; i < 12; i++)
|
||||
//TODO: Checkdown no irrecoverable errors prior to OTA Decryption?
|
||||
|
||||
//Hytera OTA Encryption
|
||||
if (state->hota_key_loaded && hota_enc)
|
||||
hytera_ota_decrypt_bptc_96(state, BPTCDmrDataBit, databurst);
|
||||
|
||||
//Hytera Data Block Level Decryption
|
||||
if (databurst == 0x7) //Rate 1_2 Data Only
|
||||
{
|
||||
BPTCDmrDataByte[i] = 0;
|
||||
for(j = 0; j < 8; j++)
|
||||
{
|
||||
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1;
|
||||
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01);
|
||||
k++;
|
||||
}
|
||||
uint8_t bytes[12];
|
||||
memset(bytes, 0, sizeof(bytes));
|
||||
pack_bit_array_into_byte_array(BPTCDmrDataBit, bytes, 12);
|
||||
hytera_bp_data_decrypt(state, bytes, 12);
|
||||
hytera_ep_data_decrypt(opts, state, bytes, 12);
|
||||
unpack_byte_array_into_bit_array(bytes, BPTCDmrDataBit, 12);
|
||||
}
|
||||
|
||||
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
|
||||
/* Convert the 96 bits BPTC data into 12 bytes */
|
||||
pack_bit_array_into_byte_array(BPTCDmrDataBit, BPTCDmrDataByte, 12);
|
||||
|
||||
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
|
||||
if (IrrecoverableErrors == 0 && BPTCDmrDataByte[1] == 0x68) is_ras = 0;
|
||||
|
||||
//make sure if this is a PI header, the FID is set to 0x10
|
||||
if (IrrecoverableErrors == 0 && databurst == 0x00 && BPTCDmrDataByte[1] != 0x10) is_ras = 0;
|
||||
|
||||
/* Fill the CRC extracted */
|
||||
CRCExtracted = 0;
|
||||
for(i = 0; i < crclen; i++)
|
||||
{
|
||||
@@ -279,7 +301,24 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
CRCExtracted = CRCExtracted ^ crcmask;
|
||||
|
||||
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
||||
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
||||
if (is_lc)
|
||||
{
|
||||
if (is_ras)
|
||||
{
|
||||
CRCComputed = ras_mac_calculator(state, BPTCDmrDataByte, 9, 1);
|
||||
if (CRCComputed == CRCExtracted)
|
||||
CRCCorrect = 1;
|
||||
|
||||
#ifdef DEBUG_RAS
|
||||
fprintf (stderr, " EXT: %06X; MAC: %06X;", CRCExtracted, CRCComputed);
|
||||
#endif
|
||||
}
|
||||
else CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
||||
|
||||
//Hytera OTA Encryption LC Context Creation for VLC Header
|
||||
if (state->hota_key_loaded && CRCCorrect && databurst == 0x01)
|
||||
hytera_create_lc_ctx(state, BPTCDmrDataBit);
|
||||
}
|
||||
|
||||
//set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these)
|
||||
else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
|
||||
@@ -315,20 +354,23 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
//run CCITT on other data forms
|
||||
else
|
||||
{
|
||||
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
||||
if (is_ras == 1)
|
||||
{
|
||||
CRCComputed = ras_mac_calculator(state, BPTCDmrDataByte, 10, 0);
|
||||
|
||||
#ifdef DEBUG_RAS
|
||||
fprintf (stderr, " EXT: %04X; MAC: %04X;", CRCExtracted, CRCComputed);
|
||||
#endif
|
||||
}
|
||||
else CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
||||
|
||||
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||
else CRCCorrect = 0;
|
||||
}
|
||||
|
||||
//set the 'RAS Flag', if no irrecoverable errors but bad crc, only when enabled by user (to prevent a lot of bad data CSBKs)
|
||||
if (opts->aggressive_framesync == 0 && CRCCorrect == 0 && IrrecoverableErrors == 0 && BPTCReservedBits == 4) is_ras = 1;
|
||||
|
||||
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
|
||||
if (BPTCDmrDataByte[1] == 0x68) is_ras = 0;
|
||||
|
||||
//if this is a RAS system, set the CRC to okay if irrecoverable errors are okay
|
||||
//if we don't do this, then we can't view some data (CSBKs on RAS enabled systems)
|
||||
if (is_ras == 1)
|
||||
if (is_ras == 1 && state->ras_effective_key == 0)
|
||||
{
|
||||
crc_original_validity = CRCCorrect;
|
||||
CRCCorrect = 1;
|
||||
@@ -344,17 +386,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
}
|
||||
|
||||
/* Convert corrected x bytes into x*8 bits */
|
||||
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
||||
{
|
||||
BPTCDmrDataBit[j + 0] = (BPTCDmrDataByte[i+pdu_start] >> 7) & 0x01;
|
||||
BPTCDmrDataBit[j + 1] = (BPTCDmrDataByte[i+pdu_start] >> 6) & 0x01;
|
||||
BPTCDmrDataBit[j + 2] = (BPTCDmrDataByte[i+pdu_start] >> 5) & 0x01;
|
||||
BPTCDmrDataBit[j + 3] = (BPTCDmrDataByte[i+pdu_start] >> 4) & 0x01;
|
||||
BPTCDmrDataBit[j + 4] = (BPTCDmrDataByte[i+pdu_start] >> 3) & 0x01;
|
||||
BPTCDmrDataBit[j + 5] = (BPTCDmrDataByte[i+pdu_start] >> 2) & 0x01;
|
||||
BPTCDmrDataBit[j + 6] = (BPTCDmrDataByte[i+pdu_start] >> 1) & 0x01;
|
||||
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
||||
}
|
||||
unpack_byte_array_into_bit_array(BPTCDmrDataByte, BPTCDmrDataBit, pdu_len);
|
||||
|
||||
//convert to DMR_PDU and DMR_PDU_bits
|
||||
for (i = 0; i < pdu_len; i++)
|
||||
@@ -398,8 +430,42 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
} /* End for(j = 0; j < 8; j++) */
|
||||
} /* End for(i = 0; i < 16; i++) */
|
||||
|
||||
//Hytera OTA Encryption
|
||||
// if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0)
|
||||
// hytera_ota_search_emb_lc(state, BptcDataMatrix); //only run as a debug option
|
||||
if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 1)
|
||||
hytera_ota_decrypt_emb_lc(state, BptcDataMatrix);
|
||||
|
||||
/* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */
|
||||
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
||||
if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0)
|
||||
{
|
||||
//just fill with all ones, would be garbage from FEC anyways
|
||||
memset(LC_DataBit, 1, sizeof(LC_DataBit));
|
||||
IrrecoverableErrors = 1;
|
||||
|
||||
//TODO: columns instead of rows to match application of keystream
|
||||
#ifdef DEBUG_HOTA
|
||||
fprintf(stderr, "\n BPTC 128: ");
|
||||
for (int i = 0; i < 8; i++)
|
||||
fprintf (stderr, "%04X", (uint16_t)convert_bits_into_output(BptcDataMatrix[i], 16));
|
||||
fprintf (stderr, "\n");
|
||||
#endif
|
||||
}
|
||||
else if (state->hota_enc_check == 1)
|
||||
{
|
||||
//just fill with all ones, would be garbage from FEC anyways
|
||||
memset(LC_DataBit, 1, sizeof(LC_DataBit));
|
||||
IrrecoverableErrors = 1;
|
||||
|
||||
//TODO: columns instead of rows to match application of keystream
|
||||
#ifdef DEBUG_HOTA
|
||||
fprintf(stderr, "\n BPTC 128: ");
|
||||
for (int i = 0; i < 8; i++)
|
||||
fprintf (stderr, "%04X", (uint16_t)convert_bits_into_output(BptcDataMatrix[i], 16));
|
||||
fprintf (stderr, "\n");
|
||||
#endif
|
||||
}
|
||||
else IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
||||
|
||||
/* Reconstitute the 5 bit CRC */
|
||||
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5);
|
||||
@@ -407,6 +473,16 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
/* Compute the 5 bit CRC */
|
||||
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
||||
|
||||
//read ahead first two bytes, look for 1X10 or 2X10 pattern
|
||||
uint16_t ras_test = (uint16_t)(ConvertBitIntoBytes(&LC_DataBit[0], 16) & 0x30FF);
|
||||
if ((IrrecoverableErrors == 0) && (ras_test == 0x2010 || ras_test == 0x1010))
|
||||
{
|
||||
is_ras = 1;
|
||||
if (state->ras_effective_key != 0)
|
||||
CRCComputed = ras_mac_calculator(state, LC_DataBit, 9, 2);
|
||||
}
|
||||
else is_ras = 0;
|
||||
|
||||
if(CRCExtracted == CRCComputed) CRCCorrect = 1;
|
||||
else CRCCorrect = 0;
|
||||
|
||||
@@ -438,7 +514,11 @@ 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 = dmr_34(tdibits, TrellisReturn);
|
||||
IrrecoverableErrors = viterbi_r34(tdibits, TrellisReturn);
|
||||
|
||||
//Hytera Data Block Level Decryption
|
||||
hytera_bp_data_decrypt(state, TrellisReturn, 18);
|
||||
hytera_ep_data_decrypt(opts, state, TrellisReturn, 18);
|
||||
|
||||
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
|
||||
//they may have been successfully corrected, the CRC will reveal as much
|
||||
@@ -518,6 +598,10 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
bit_ptr = 100; byte_ptr = 12-pdu_start;
|
||||
pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12);
|
||||
|
||||
//Hytera Data Block Level Decryption
|
||||
hytera_bp_data_decrypt(state, DMR_PDU, 24);
|
||||
hytera_ep_data_decrypt(opts, state, DMR_PDU, 24);
|
||||
|
||||
//set CRC to correct on unconfirmed 1 rate data blocks (for reporting due to no CRC available on these)
|
||||
if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
|
||||
|
||||
@@ -593,19 +677,8 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
else fprintf (stderr, "Reserved %d ", usbd_st);
|
||||
}
|
||||
|
||||
//set the original CRCCorrect back to its original value if the RAS flag was tripped
|
||||
if (is_ras == 1) CRCCorrect = crc_original_validity;
|
||||
|
||||
//start printing relevant fec/crc/ras messages, don't print on idle or MBC continuation blocks (handled in dmr_block.c)
|
||||
// if (IrrecoverableErrors == 0 && CRCCorrect == 1 && databurst != 0x09 && databurst != 0x05) fprintf(stderr, "(CRC OK)");
|
||||
|
||||
// if (IrrecoverableErrors == 0 && CRCCorrect == 0 && databurst != 0x09 && databurst != 0x05)
|
||||
// {
|
||||
// fprintf (stderr, "%s", KYEL);
|
||||
// fprintf(stderr, " (FEC OK)");
|
||||
// fprintf (stderr, "%s", KNRM);
|
||||
|
||||
// }
|
||||
//set the original CRCCorrect back to its original value if the RAS flag was tripped (if no key value present)
|
||||
if (is_ras == 1 && state->ras_effective_key == 0) CRCCorrect = crc_original_validity;
|
||||
|
||||
if (IrrecoverableErrors != 0 && databurst != 0x08 && databurst != 0x09) //&& databurst != 0x05
|
||||
{
|
||||
@@ -614,16 +687,25 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
//print whether or not the 'RAS Field' bits are set to indicate RAS enabled (to be verified)
|
||||
if (is_ras == 1)
|
||||
//print whether or not the 'RAS Field' bit is set to indicate RAS
|
||||
//detected for RAS enabled burst types and disposition if key available
|
||||
if (IrrecoverableErrors == 0 && is_ras == 1 && (is_bptc || is_emb) && databurst != 0x05 && databurst != 0x07)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " -RAS ");
|
||||
//the value of this field seems to always, or usually, be 4, or just R[2] bit is set
|
||||
if (opts->payload == 1) fprintf (stderr, "%X ", BPTCReservedBits);
|
||||
fprintf (stderr, " {RAS ");
|
||||
if (state->ras_effective_key != 0 && CRCComputed != CRCExtracted)
|
||||
fprintf (stderr, "Fail} ");
|
||||
else if (state->ras_effective_key != 0 && CRCComputed == CRCExtracted)
|
||||
fprintf (stderr, "Okay} ");
|
||||
else if (state->ras_effective_key == 0)
|
||||
fprintf (stderr, "Skip} ");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
//might flicker the terminal on partially enabled systems, may need to find a better placement
|
||||
if (IrrecoverableErrors == 0 && (is_bptc || is_emb) && databurst != 0x05 && databurst != 0x07 && databurst != 0x09)
|
||||
state->dmr_is_ras = is_ras;
|
||||
|
||||
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
@@ -639,9 +721,24 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
fprintf (stderr, " DMR PDU Payload ");
|
||||
for (i = 0; i < pdu_len; i++)
|
||||
{
|
||||
#ifdef DEBUG_RAS
|
||||
fprintf (stderr, "%02X", DMR_PDU[i]);
|
||||
#else
|
||||
fprintf (stderr, "[%02X]", DMR_PDU[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
//RAS 5-bit - always same differential
|
||||
//calculation should be 5-bit portion of RAS Effective Key's Last byte & 0x1F
|
||||
//5-bit + CRCComputed % 31 = CRCExtracted
|
||||
#ifdef DEBUG_RAS
|
||||
if (opts->payload == 1 && is_emb && is_ras)
|
||||
{
|
||||
int32_t diff = ((int32_t)CRCExtracted - (int32_t)CRCComputed + 31) % 31;
|
||||
fprintf (stderr, " CRC CMP: %02X EXT: %02X DIFF: %02X", CRCComputed, CRCExtracted, diff);
|
||||
}
|
||||
#endif
|
||||
|
||||
//debug print
|
||||
// if (dbsn) fprintf (stderr, " SN %X", dbsn);
|
||||
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
||||
|
||||
153
src/dmr_flco.c
153
src/dmr_flco.c
@@ -32,6 +32,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
int is_alias = 0;
|
||||
int is_gps = 0;
|
||||
UNUSED(capsite);
|
||||
int is_ras = 0;
|
||||
|
||||
//XPT 'Things'
|
||||
int is_xpt = 0;
|
||||
@@ -72,6 +73,71 @@ 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;
|
||||
}
|
||||
|
||||
//Hytera OTA Check Enabled, Set SRC from VLC (TLC and ELC differ)
|
||||
//NOTE: VLC will always be same for each radio if its a group call
|
||||
//while this is not the true src, it should be unique
|
||||
//TGT will also be random between calls in each VLC,
|
||||
//so assign static value
|
||||
if (state->hota_enc_check)
|
||||
{
|
||||
uint32_t t_target = 8686868;
|
||||
if (type == 1)
|
||||
{
|
||||
if (slot == 0)
|
||||
{
|
||||
state->lastsrc = source;
|
||||
state->lasttg = t_target;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->lastsrcR = source;
|
||||
state->lasttgR = t_target;
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n%s", KYEL);
|
||||
fprintf (stderr, " SLOT %d ", slot+1);
|
||||
fprintf(stderr, "TGT=%u SRC=%u ", t_target, source);
|
||||
fprintf (stderr, "Encrypted Pseudo Call Information");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (type == 2)
|
||||
{
|
||||
fprintf (stderr, "\n%s", KRED);
|
||||
fprintf (stderr, " SLOT %d", slot+1);
|
||||
fprintf (stderr, " TLC Encrypted;");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
return;
|
||||
}
|
||||
else if (type == 3)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " SLOT %d", slot+1);
|
||||
fprintf (stderr, " EMB LC Encrypted;");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//HOTA CTX was not recovered, so dump that and return;
|
||||
else if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0 && type == 3)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " SLOT %d", slot+1);
|
||||
fprintf (stderr, " EMB LC Missing CTX; Encrypted;");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
return;
|
||||
}
|
||||
|
||||
//read ahead a little to get this for the xpt flag
|
||||
if (*IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68)
|
||||
{
|
||||
@@ -108,6 +174,17 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
if (slot == 0) state->dmr_flco = flco;
|
||||
else state->dmr_flcoR = flco;
|
||||
|
||||
//debug output for potential RAS indicators on Moto Systems during EMB (0x2X and 0x1X indicators)
|
||||
//TODO: Either leave handling of link control as is, or rework entire file to handle all the exceptions
|
||||
//that have been added to it over the years.
|
||||
if ( (flco & 0x30) && (fid == 0x10) && (type == 3) )
|
||||
{
|
||||
is_ras = 1;
|
||||
UNUSED(is_ras);
|
||||
// flco &= 0xCF; //mask off the RAS bits
|
||||
//NOTE: Mask breaks below check on talker alias on RAS systems WIP: Fixing That
|
||||
}
|
||||
|
||||
//FID 0x10 and FLCO 0x14, 0x15, 0x16 and 0x17 confirmed as Moto EMB Alias
|
||||
//Can probably assume FID 0x10 FLCO 0x18 is Moto EMB GPS -- to be tested
|
||||
if ( fid == 0x10 && (flco == 0x14 || flco == 0x15 || flco == 0x16 || flco == 0x17 || flco == 0x18) )
|
||||
@@ -117,7 +194,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
}
|
||||
|
||||
//Embedded Talker Alias Header Only (format and len storage)
|
||||
if ( (fid == 0 || fid == 0x68) && type == 3 && flco == 0x04)
|
||||
if ((fid == 0 || fid == 0x68) && type == 3 && flco == 0x04)
|
||||
{
|
||||
is_alias = 1;
|
||||
dmr_talker_alias_lc_header(opts, state, slot, lc_bits);
|
||||
@@ -131,7 +208,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
}
|
||||
|
||||
//Embedded GPS
|
||||
if ( (fid == 0 || fid == 0x68) && fid == 0 && type == 3 && flco == 0x08)
|
||||
if ((fid == 0 || fid == 0x68) && type == 3 && flco == 0x08)
|
||||
{
|
||||
is_gps = 1;
|
||||
dmr_embedded_gps(opts, state, lc_bits);
|
||||
@@ -193,8 +270,29 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
goto END_FLCO;
|
||||
}
|
||||
|
||||
//Tait Talker Alias
|
||||
if (fid == 0x58 && flco == 0x08 && type == 3)
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||
fprintf (stderr, " Tait Talker Alias: ");
|
||||
char tta_str[8]; memset(tta_str, 0, sizeof(tta_str));
|
||||
for (int i = 0; i < 7; i++)
|
||||
tta_str[i] = (uint8_t)ConvertBitIntoBytes(&lc_bits[16+(i*8)], 8);
|
||||
fprintf (stderr, "%s", tta_str);
|
||||
uint32_t source = 0;
|
||||
if (slot == 0)
|
||||
source = state->lastsrc;
|
||||
else source = state->lastsrcR;
|
||||
|
||||
if (state->event_history_s[slot].Event_History_Items[0].source_id == source)
|
||||
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s; ", tta_str);
|
||||
sprintf (state->generic_talker_alias[slot], "Talker Alias: %s; ", tta_str);
|
||||
|
||||
goto END_FLCO;
|
||||
}
|
||||
//Unknown Tait Things
|
||||
if (fid == 0x58)
|
||||
else if (fid == 0x58)
|
||||
{
|
||||
//NOTE: fid 0x58 (tait) had a single flco 0x06 emb observed, but without the other blocks (4,5,7) for an alias
|
||||
//will need to observe this one, or just remove it from the list, going to isolate tait lc for now
|
||||
@@ -310,8 +408,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
|
||||
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && is_kenwood_sc == 0)
|
||||
//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)
|
||||
{
|
||||
if (type == 1) fprintf (stderr, "%s \n", KYEL);
|
||||
if (type == 2) fprintf (stderr, "%s \n", KYEL);
|
||||
@@ -444,6 +542,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
sprintf (state->generic_talker_alias[0], "%s", "");
|
||||
// sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s", "BUMBLEBEETUNA");
|
||||
memset (state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
|
||||
//Hytera OTA Encryption
|
||||
memset(state->hota_lc_ctx[0], 0, sizeof(state->hota_lc_ctx[0]));
|
||||
state->hota_ctx_ok[0] = 0;
|
||||
state->hota_sbrc_idx1[0] = -1;
|
||||
state->hota_sbrc_idx2[0] = -1;
|
||||
}
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
@@ -464,6 +568,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
sprintf (state->generic_talker_alias[1], "%s", "");
|
||||
// sprintf (state->event_history_s[1].Event_History_Items[0].alias, "%s", "BUMBLEBEETUNA");
|
||||
memset (state->dmr_pdu_sf[1], 0, sizeof(state->dmr_pdu_sf[1]));
|
||||
|
||||
//Hytera OTA Encryption
|
||||
memset(state->hota_lc_ctx[1], 0, sizeof(state->hota_lc_ctx[1]));
|
||||
state->hota_ctx_ok[1] = 0;
|
||||
state->hota_sbrc_idx1[1] = -1;
|
||||
state->hota_sbrc_idx2[1] = -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -491,15 +601,15 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
if (opts->payload == 1 && is_xpt == 1 && flco == 0x3) fprintf(stderr, "HASH=%d ", tg_hash);
|
||||
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
|
||||
|
||||
//0x04 and 0x05 on a TLC seem to indicate a Cap + Private Call Terminator (perhaps one for each MS)
|
||||
//0x07 on a VLC seems to indicate a Cap+ Private Call Header
|
||||
//0x23 on the Embedded Voice Burst Sync seems to indicate a Cap+ or Cap+ TXI Private Call in progress
|
||||
//0x20 on the Embedded Voice Burst Sync seems to indicate a Moto (non-specific) Group Call in progress
|
||||
//its possible that both EMB FID 0x10 FLCO 0x20 and 0x23 are just Moto but non-specific (observed 0x20 on Tier 2)
|
||||
//0x04 and 0x05 on a TLC seem to indicate a Cap + Private Call Terminator <--need to find this again and re-evaluate
|
||||
//0x04 on a VLC seems to indicate a Cap+ Group Call Header <--single bit flip from normal group
|
||||
//0x07 on a VLC seems to indicate a Cap+ Private Call Header <--single bit flip from normal private
|
||||
//0x20 on the Embedded Voice Burst Sync seems to indicate a Moto w/ FID 0x10 Group Call in progress (RAS)
|
||||
//0x23 on the Embedded Voice Burst Sync seems to indicate a Moto w/ FID 0x10 Private Call in progress (RAS)
|
||||
|
||||
if (fid == 0x68) sprintf (state->call_string[slot], " Hytera ");
|
||||
|
||||
else if (flco == 0x4 || flco == 0x5 || flco == 0x7 || flco == 0x23) //Cap+ Things
|
||||
else if (flco == 0x4 || flco == 0x5 || flco == 0x7) //Cap+ Things (double check 0x05)
|
||||
{
|
||||
// sprintf (state->call_string[slot], " Cap+");
|
||||
sprintf (state->call_string[slot], "%s","");
|
||||
@@ -519,13 +629,13 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
state->gi[slot] = 1;
|
||||
}
|
||||
}
|
||||
else if (flco == 0x3) //UU_V_Ch_Usr
|
||||
else if (flco == 0x3 || flco == 0x23) //UU_V_Ch_Usr
|
||||
{
|
||||
sprintf (state->call_string[slot], " Private ");
|
||||
fprintf (stderr, "Private ");
|
||||
state->gi[slot] = 1;
|
||||
}
|
||||
else //Grp_V_Ch_Usr -- still valid on hytera VLC
|
||||
else //Grp_V_Ch_Usr
|
||||
{
|
||||
sprintf (state->call_string[slot], " Group ");
|
||||
fprintf (stderr, "Group ");
|
||||
@@ -551,7 +661,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[8]; char gn[50];
|
||||
uint32_t t = 0; char gm[3]; char gn[100];
|
||||
|
||||
//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++)
|
||||
@@ -654,6 +764,7 @@ 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)
|
||||
@@ -1181,7 +1292,19 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
||||
|
||||
}
|
||||
|
||||
else if (slco == 0xF)
|
||||
else if (slco == 0x5)
|
||||
{
|
||||
uint32_t unk = (uint32_t)ConvertBitIntoBytes(&slco_bits[4], 24);
|
||||
fprintf (stderr, " SLCO Caltta: %06X;", unk);
|
||||
}
|
||||
|
||||
else if (slco == 0xE)
|
||||
{
|
||||
uint32_t update_tgt = (uint32_t)ConvertBitIntoBytes(&slco_bits[4], 24);
|
||||
fprintf (stderr, " SLCO Cap Max Activity Update; TGT: %d;", update_tgt);
|
||||
}
|
||||
|
||||
else if (slco == 0xF) //This opcode has also been observed on Cap Max Payload Channels
|
||||
{
|
||||
fprintf (stderr, " SLCO Capacity Plus Site: %d - Rest LSN: %d - RS: %02X", capsite, restchannel, cap_reserved);
|
||||
|
||||
|
||||
278
src/dmr_le.c
278
src/dmr_le.c
@@ -8,29 +8,15 @@
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
static unsigned long long int next_iv_32[2] = {0,0};
|
||||
static unsigned long long int next_iv_64[2] = {0,0};
|
||||
|
||||
//gather ambe_fr mi fragments for processing
|
||||
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, uint8_t ambe_fr[4][24], uint8_t ambe_fr2[4][24], uint8_t ambe_fr3[4][24])
|
||||
{
|
||||
|
||||
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);
|
||||
@@ -103,7 +89,96 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
if (g[0] && g[1] && g[2])
|
||||
{
|
||||
if (slot == 0 && state->payload_algid != 0)
|
||||
|
||||
if (mi_crc_ok == 1 && state->hota_enc_check == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " Slot %d DMRA EP Potential;", slot+1);
|
||||
|
||||
unsigned long long int temp_iv = mi_final;
|
||||
|
||||
//Reverse Potential IV for both 32-bit and 128-bit to show the IV
|
||||
//that may have been carried by the PI header or last superframe
|
||||
uint8_t iv_a[16]; memset(iv_a, 0, sizeof(iv_a));
|
||||
uint8_t iv_d[16]; memset(iv_d, 0, sizeof(iv_d));
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
iv_a[i] = (mi_final >> (24-(i*8))) & 0xFF;
|
||||
iv_d[i] = (mi_final >> (24-(i*8))) & 0xFF;
|
||||
}
|
||||
fprintf (stderr, "\n ");
|
||||
reverse_lfsr_32a_to_len(iv_a, 32);
|
||||
fprintf (stderr, " ");
|
||||
reverse_lfsr_32d_to_len(iv_d, 32);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
if (next_iv_64[slot] != 0 && mi_final == next_iv_64[slot])
|
||||
{
|
||||
//probably safe to assume AES-256 if its DMRA specific
|
||||
fprintf (stderr, " DMRA AES 128-bit IV detected;");
|
||||
state->forced_alg_id = 0x25;
|
||||
if (slot == 0 && state->payload_mi == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
state->payload_mi = mi_final;
|
||||
state->dmr_so = 0x40;
|
||||
LFSR128d(state);
|
||||
}
|
||||
|
||||
else if (slot == 1 && state->payload_miR == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
state->payload_miR = mi_final;
|
||||
state->dmr_soR = 0x40;
|
||||
LFSR128d(state);
|
||||
}
|
||||
|
||||
}
|
||||
if (next_iv_32[slot] != 0 && mi_final == next_iv_32[slot])
|
||||
{
|
||||
//only DMRA RC4 uses this tap configuration
|
||||
fprintf (stderr, " DMRA RC4 32-bit IV detected;");
|
||||
state->forced_alg_id = 0x21;
|
||||
if (slot == 0 && state->payload_mi == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
state->payload_mi = mi_final;
|
||||
state->dmr_so = 0x40;
|
||||
LFSR(state);
|
||||
}
|
||||
|
||||
else if (slot == 1 && state->payload_miR == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
state->payload_miR = mi_final;
|
||||
state->dmr_soR = 0x40;
|
||||
LFSR(state);
|
||||
}
|
||||
}
|
||||
|
||||
//AES taps to next embedded value
|
||||
for(int cnt = 0; cnt < 32; cnt++)
|
||||
{
|
||||
unsigned long long int bit = ( (temp_iv >> 31) ^ (temp_iv >> 21) ^ (temp_iv >> 1) ^ (temp_iv >> 0) ) & 0x1;
|
||||
temp_iv = (temp_iv << 1) | bit;
|
||||
}
|
||||
next_iv_64[slot] = temp_iv & 0xFFFFFFFF;
|
||||
|
||||
//RC4 taps to next embedded value
|
||||
temp_iv = mi_final;
|
||||
for(int cnt = 0; cnt < 32; cnt++)
|
||||
{
|
||||
unsigned long long int bit = ( (temp_iv >> 31) ^ (temp_iv >> 3) ^ (temp_iv >> 1) ) & 0x1;
|
||||
temp_iv = (temp_iv << 1) | bit;
|
||||
}
|
||||
next_iv_32[slot] = temp_iv & 0xFFFFFFFF;
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
if (slot == 0 && state->payload_algid != 0 && state->payload_algid != 0x26)
|
||||
{
|
||||
if (state->payload_mi != mi_final)
|
||||
{
|
||||
@@ -133,7 +208,7 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
}
|
||||
if (slot == 1 && state->payload_algidR != 0)
|
||||
if (slot == 1 && state->payload_algidR != 0 && state->payload_algidR != 0x26)
|
||||
{
|
||||
if (state->payload_miR != mi_final)
|
||||
{
|
||||
@@ -236,6 +311,40 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
if ((state->payload_algid & 0xF0) == 0x30) //any kirisun 0x3X alg
|
||||
{
|
||||
uint8_t alg = state->payload_algid;
|
||||
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;", alg, state->payload_keyid);
|
||||
fprintf (stderr, " MI(32): %08llX;", state->payload_mi);
|
||||
fprintf (stderr, " Kirisun ");
|
||||
if (alg == 0x31)
|
||||
fprintf (stderr, "Reinforced ");
|
||||
else if (alg == 0x32)
|
||||
fprintf (stderr, "Low ");
|
||||
else if (alg == 0x33)
|
||||
fprintf (stderr, "Middle ");
|
||||
else if (alg == 0x34)
|
||||
fprintf (stderr, "High ");
|
||||
else if (alg == 0x35)
|
||||
fprintf (stderr, "Enhanced ");
|
||||
else if (alg == 0x36)
|
||||
fprintf (stderr, "Advanced ");
|
||||
else if (alg == 0x37)
|
||||
fprintf (stderr, "Universal ");
|
||||
else if (alg == 0x38)
|
||||
fprintf (stderr, "Strengthened ");
|
||||
else if (alg == 0x39)
|
||||
fprintf (stderr, "Top ");
|
||||
else if (alg == 0x3A)
|
||||
fprintf (stderr, "Ultra ");
|
||||
fprintf (stderr, "Encryption;");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
}
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
@@ -266,6 +375,40 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
if ((state->payload_algidR & 0xF0) == 0x30) //any 0x3X alg
|
||||
{
|
||||
uint8_t alg = state->payload_algidR;
|
||||
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;", alg, state->payload_keyidR);
|
||||
fprintf (stderr, " MI(32): %08llX;", state->payload_miR);
|
||||
fprintf (stderr, " Kirisun ");
|
||||
if (alg == 0x31)
|
||||
fprintf (stderr, "Reinforced ");
|
||||
else if (alg == 0x32)
|
||||
fprintf (stderr, "Low ");
|
||||
else if (alg == 0x33)
|
||||
fprintf (stderr, "Middle ");
|
||||
else if (alg == 0x34)
|
||||
fprintf (stderr, "High ");
|
||||
else if (alg == 0x35)
|
||||
fprintf (stderr, "Enhanced ");
|
||||
else if (alg == 0x36)
|
||||
fprintf (stderr, "Advanced ");
|
||||
else if (alg == 0x37)
|
||||
fprintf (stderr, "Universal ");
|
||||
else if (alg == 0x38)
|
||||
fprintf (stderr, "Strengthened ");
|
||||
else if (alg == 0x39)
|
||||
fprintf (stderr, "Top ");
|
||||
else if (alg == 0x3A)
|
||||
fprintf (stderr, "Ultra ");
|
||||
fprintf (stderr, "Encryption;");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -314,6 +457,13 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
// 1 - The embedded signalling carries RC information associated to the other logical channel
|
||||
|
||||
for(i = 0; i < 32; i++) sbrc_interleaved[i] = state->dmr_embedded_signalling[slot][5][i + 8];
|
||||
|
||||
//decrypt for Hytera OTA Encryption w/ loaded key
|
||||
if (state->hota_key_loaded && state->hota_sbrc_idx1[state->currentslot] == -1)
|
||||
hytera_ota_search_sb_rc(state, sbrc_interleaved, power);
|
||||
if (state->hota_key_loaded)
|
||||
hytera_ota_decrypt_sb_rc(state, sbrc_interleaved);
|
||||
|
||||
//power == 0 should be single burst
|
||||
if (power == 0) irr_err = BPTC_16x2_Extract_Data(sbrc_interleaved, sbrc_return, 0);
|
||||
//power == 1 should be reverse channel -- still need to check the interleave inside of BPTC
|
||||
@@ -321,6 +471,36 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
//bad emb burst, never set a valid power indicator value (probably 9)
|
||||
if (power > 1) goto SBRC_END;
|
||||
|
||||
//Hytera OTA
|
||||
if (irr_err && state->hota_key_loaded && state->hota_sbrc_idx2[state->currentslot] == -1)
|
||||
{
|
||||
state->hota_sbrc_idx1[state->currentslot] = -1;
|
||||
state->hota_sbrc_idx2[state->currentslot] = -1;
|
||||
fprintf (stderr, "%s \n", KRED);
|
||||
fprintf (stderr, " SLOT %d ", slot+1);
|
||||
fprintf (stderr, "SB_RC Missing IDX; Encrypted;");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
goto SBRC_END;
|
||||
}
|
||||
else if (irr_err && state->hota_key_loaded && state->hota_sbrc_idx2[state->currentslot] != -1)
|
||||
{
|
||||
state->hota_sbrc_idx1[state->currentslot] = -1;
|
||||
state->hota_sbrc_idx2[state->currentslot] = -1;
|
||||
fprintf (stderr, "%s \n", KRED);
|
||||
fprintf (stderr, " SLOT %d ", slot+1);
|
||||
fprintf (stderr, "SB_RC Incorrect IDX or FEC Errors; Encrypted;");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
goto SBRC_END;
|
||||
}
|
||||
else if (state->hota_enc_check)
|
||||
{
|
||||
fprintf (stderr, "%s \n", KRED);
|
||||
fprintf (stderr, " SLOT %d ", slot+1);
|
||||
fprintf (stderr, "SB_RC Encrypted;");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
goto SBRC_END;
|
||||
}
|
||||
|
||||
//arrange the return for proper order for the crc3 check
|
||||
for (i = 0; i < 8; i++) sbrc_retcrc[i] = sbrc_return[i+3];
|
||||
|
||||
@@ -390,6 +570,56 @@ 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
|
||||
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) //older firmware had sbrc_hex 0x5F1, and 0x5E1 instead
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " Slot %d", state->currentslot+1);
|
||||
fprintf (stderr, " DMR LE SB Kirisun");
|
||||
|
||||
//Newer Samples and/or updated firmware seem to do this with actual key values
|
||||
uint8_t alg = sbrc_hex >> 7;
|
||||
|
||||
if (alg != 0xB)
|
||||
{
|
||||
fprintf (stderr, " ALG: %02X;", alg+0x30);
|
||||
uint8_t kid = (sbrc_hex >> 3) & 0xF;
|
||||
fprintf (stderr, " KID: %02X;", kid);
|
||||
uint8_t unk = sbrc_hex & 0x7;
|
||||
fprintf (stderr, " LOW3: %X;", unk);
|
||||
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
if (state->payload_algid == 0 && state->dmr_so & 0x40)
|
||||
state->payload_algid = alg+0x30;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state->payload_algidR == 0 && state->dmr_soR & 0x40)
|
||||
state->payload_algidR = alg+0x30;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->dmr_le == 1)
|
||||
{
|
||||
if (irr_err != 0)
|
||||
@@ -466,7 +696,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->M == 0)
|
||||
if (state->forced_alg_id == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
@@ -476,6 +706,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
if (alg == 2) fprintf (stderr, " DES;");
|
||||
if (alg == 4) fprintf (stderr, " AES128;");
|
||||
if (alg == 5) fprintf (stderr, " AES256;");
|
||||
if (alg == 6) fprintf (stderr, " Caltta BP");
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||
if (state->payload_keyid != key)
|
||||
@@ -492,12 +723,17 @@ 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->M == 0)
|
||||
if (state->forced_alg_id == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " Slot 2");
|
||||
fprintf (stderr, " DMR LE SB ALG ID: %02X; KEY ID: %02X;", alg + 0x20, key);
|
||||
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 == 6) fprintf (stderr, " Caltta BP");
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||
if (state->payload_keyidR != key)
|
||||
|
||||
28
src/dmr_ms.c
28
src/dmr_ms.c
@@ -253,6 +253,20 @@ 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);
|
||||
}
|
||||
|
||||
if (state->vtx256_ekey != 0)
|
||||
{
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr);
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr2);
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr3);
|
||||
}
|
||||
|
||||
#ifdef PRINT_AMBE72
|
||||
ambe2_codeword_print_i(opts, ambe_fr);
|
||||
ambe2_codeword_print_i(opts, ambe_fr2);
|
||||
@@ -564,6 +578,20 @@ 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);
|
||||
}
|
||||
|
||||
if (state->vtx256_ekey != 0)
|
||||
{
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr);
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr2);
|
||||
vtx256_ambe2_interleave_inversion(ambe_fr3);
|
||||
}
|
||||
|
||||
#ifdef PRINT_AMBE72
|
||||
ambe2_codeword_print_i(opts, ambe_fr);
|
||||
ambe2_codeword_print_i(opts, ambe_fr2);
|
||||
|
||||
@@ -267,8 +267,13 @@ void dmr_udp_comp_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t
|
||||
slot = 1;
|
||||
|
||||
char comp_string[500]; memset (comp_string, 0, sizeof(comp_string));
|
||||
sprintf (comp_string, "IPC: %d; OP: %d; SRC: %d:%d (%s):(%s); DST: %d:%d (%s):(%s); ", ipid, opcode, said, spid, addrstring[0], portstring[0], daid, dpid, addrstring[1], portstring[1]);
|
||||
watchdog_event_datacall (opts, state, said, daid, comp_string, slot);
|
||||
|
||||
//use data header source and target instead of idx values
|
||||
uint32_t source = state->dmr_lrrp_source[slot];
|
||||
uint32_t target = state->dmr_lrrp_target[slot];
|
||||
sprintf (comp_string, "IPC Call; SRC: %d; TGT: %d; %s", source, target, portstring);
|
||||
// sprintf (comp_string, "IPC: %d; OP: %d; SRC: %d:%d (%s):(%s); DST: %d:%d (%s):(%s); ", ipid, opcode, said, spid, addrstring[0], portstring[0], daid, dpid, addrstring[1], portstring[1]);
|
||||
watchdog_event_datacall (opts, state, source, target, comp_string, slot);
|
||||
|
||||
}
|
||||
|
||||
@@ -482,6 +487,29 @@ 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)
|
||||
{
|
||||
|
||||
fprintf (stderr, "VTX STD TMS; ");
|
||||
|
||||
int tmslen = input[28];
|
||||
|
||||
//+29, get 8 bytes for unknown "header" information
|
||||
//unknown elements 0002000000000000, could include type (utf-16, utf-8, is07?)
|
||||
//the sample has a len of 14 utf-16 chars, corresponding to the found 0x0E value
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "Len: %03d; ", tmslen);
|
||||
fprintf (stderr, "??: ");
|
||||
for (int i = 0; i < 8; i++)
|
||||
fprintf (stderr, "%02X", input[i+29]);
|
||||
fprintf (stderr, "; Text: ");
|
||||
}
|
||||
|
||||
sprintf (state->dmr_lrrp_gps[slot], "VTX TMS SRC: %d; DST: %d; ", src24, dst24);
|
||||
utf16_to_text(state, 1, tmslen*2, input+37);
|
||||
}
|
||||
else if (port1 == 5017 && port2 == 5017)
|
||||
{
|
||||
//sanity check
|
||||
@@ -493,6 +521,12 @@ 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; ",
|
||||
@@ -967,8 +1001,9 @@ 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 * timestr = getTimeC();
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
FILE * pFile; //file pointer
|
||||
|
||||
155
src/dmr_pi.c
155
src/dmr_pi.c
@@ -12,7 +12,6 @@
|
||||
|
||||
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];
|
||||
|
||||
@@ -26,6 +25,74 @@ 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
|
||||
{
|
||||
//NOTE: Some Kirisun Radios seem to use byte 2 as the SVC_OPT, others seem to use it like a KID value
|
||||
// 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
|
||||
//NOTE: Original samples had this as 0x40 no matter which "key" was selected, new samples from different radio show a KID value
|
||||
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 on older FW
|
||||
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)
|
||||
|
||||
//Only set real KID if the byte value isn't 0x40
|
||||
if (kid == 0x40)
|
||||
kid = hash;
|
||||
|
||||
//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 = 0x40;
|
||||
state->payload_algid = alg;
|
||||
state->payload_keyid = kid;
|
||||
state->payload_mi = mi;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->dmr_soR = 0x40;
|
||||
state->payload_algidR = alg;
|
||||
state->payload_keyidR = kid;
|
||||
state->payload_miR = mi;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
fprintf (stderr, "\n Slot %d", state->currentslot+1);
|
||||
fprintf (stderr, " DMR PI H- ALG ID: %02X; TGT: %d; KEY ID: %02X; MI(32): %08X;", alg, target, kid, mi);
|
||||
|
||||
fprintf (stderr, " Kirisun ");
|
||||
if (alg == 0x31)
|
||||
fprintf (stderr, "Reinforced ");
|
||||
else if (alg == 0x32)
|
||||
fprintf (stderr, "Low ");
|
||||
else if (alg == 0x33)
|
||||
fprintf (stderr, "Middle ");
|
||||
else if (alg == 0x34)
|
||||
fprintf (stderr, "High ");
|
||||
else if (alg == 0x35)
|
||||
fprintf (stderr, "Enhanced ");
|
||||
else if (alg == 0x36)
|
||||
fprintf (stderr, "Advanced ");
|
||||
else if (alg == 0x37)
|
||||
fprintf (stderr, "Universal ");
|
||||
else if (alg == 0x38)
|
||||
fprintf (stderr, "Strengthened ");
|
||||
else if (alg == 0x39)
|
||||
fprintf (stderr, "Top ");
|
||||
else if (alg == 0x3A)
|
||||
fprintf (stderr, "Ultra ");
|
||||
fprintf (stderr, "Encryption;");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//flag late entry SB for Kirisun
|
||||
opts->dmr_le = 3;
|
||||
|
||||
}
|
||||
|
||||
if (MFID == 0x68) //Hytera Enhanced
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
@@ -96,7 +163,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
state->payload_algid = PI_BYTE[0];
|
||||
state->payload_keyid = PI_BYTE[2];
|
||||
state->payload_mi = ((unsigned long long int)PI_BYTE[3] << 24ULL) | ((unsigned long long int)PI_BYTE[4] << 16ULL) | ((unsigned long long int)PI_BYTE[5] << 8ULL) | ((unsigned long long int)PI_BYTE[6] << 0ULL);
|
||||
if (state->payload_algid < 0x26)
|
||||
if (state->payload_algid < 0x28)
|
||||
{
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
fprintf (stderr, "\n Slot 1");
|
||||
@@ -132,6 +199,13 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
state->payload_algid = 0x25;
|
||||
}
|
||||
|
||||
else if ((state->payload_algid & 0x07) == 0x06)
|
||||
{
|
||||
fprintf (stderr, " Caltta BP;");
|
||||
state->payload_algid = 0x26;
|
||||
state->payload_mi = 0;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
|
||||
//expand the 32-bit MI to a 64-bit DES IV
|
||||
@@ -149,7 +223,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
}
|
||||
}
|
||||
|
||||
if (state->payload_algid >= 0x26)
|
||||
if (state->payload_algid >= 0x28)
|
||||
{
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
@@ -199,6 +273,12 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
state->payload_algidR = 0x25;
|
||||
}
|
||||
|
||||
else if ((state->payload_algidR & 0x07) == 0x06)
|
||||
{
|
||||
fprintf (stderr, " Caltta BP;");
|
||||
state->payload_algidR = 0x26;
|
||||
state->payload_miR = 0;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
|
||||
@@ -217,7 +297,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
}
|
||||
}
|
||||
|
||||
if (state->payload_algidR >= 0x26)
|
||||
if (state->payload_algidR >= 0x28)
|
||||
{
|
||||
state->payload_algidR = 0;
|
||||
state->payload_keyidR = 0;
|
||||
@@ -324,6 +404,33 @@ void LFSR64(dsd_state * state)
|
||||
}
|
||||
}
|
||||
|
||||
//if supplied IV is only 32-bit in value, then expand it to a 128-bit IV using these LFSR taps (packed array)
|
||||
void lfsr_32_to_128(uint8_t * iv)
|
||||
{
|
||||
uint64_t lfsr = 0, bit = 0;
|
||||
|
||||
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
|
||||
|
||||
uint8_t cnt = 0, x = 32;
|
||||
|
||||
for(cnt = 0;cnt < 96; cnt++)
|
||||
{
|
||||
//32,22,2,1 (per Xilinx XAPP 052) Table 3: Taps for Maximum-Length LFSR Counters
|
||||
bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
|
||||
lfsr = (lfsr << 1) | bit;
|
||||
|
||||
//continue packing iv
|
||||
iv[x/8] = (iv[x/8] << 1) + bit;
|
||||
|
||||
x++;
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n IV(128): ");
|
||||
for (x = 0; x < 16; x++)
|
||||
fprintf (stderr, "%02X", iv[x]);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
}
|
||||
|
||||
//Expand a 32-bit MI into a 128-bit IV for AES
|
||||
void LFSR128d(dsd_state * state)
|
||||
@@ -423,13 +530,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%5];
|
||||
if (bit) mi[i] ^= taps[i%len];
|
||||
mi[i] |= bit;
|
||||
|
||||
}
|
||||
|
||||
unsigned long long int mi_value = 0;
|
||||
for (uint8_t i = 0; i < 5; i++)
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
{
|
||||
mi_value <<= 8;
|
||||
mi_value |= mi[i];
|
||||
@@ -470,4 +577,38 @@ 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;
|
||||
|
||||
}
|
||||
|
||||
@@ -438,3 +438,43 @@ uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData)
|
||||
/* Return the CRC */
|
||||
return CRC;
|
||||
} /* End ComputeCrc32Bit() */
|
||||
|
||||
uint32_t ComputeCrc32BitV(uint8_t * DMRData, uint32_t NbData)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t CRC = 0x00000000; /* Initialization value = 0x00000000 */
|
||||
/* Polynomial x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
|
||||
* Normal = 0x04C11DB7
|
||||
* Reversed = 0xEDB88320
|
||||
* Reciprocal = 0xDB710641
|
||||
* Reversed reciprocal = 0x82608EDB */
|
||||
uint32_t Polynome = 0x04C11DB7;
|
||||
for(i = 0; i < NbData; i++)
|
||||
{
|
||||
if(((CRC >> 31) & 1) ^ (DMRData[i] & 1))
|
||||
{
|
||||
CRC = (CRC << 1) ^ Polynome;
|
||||
}
|
||||
else
|
||||
{
|
||||
CRC <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Alternate Return Configuration for Vertex
|
||||
//Encrypted PDUs that have already been reordered once before
|
||||
//CRC32 CMP: 1622377A EXT: 377A1622
|
||||
uint32_t a, b, c, d;
|
||||
a = b = c = d = 0;
|
||||
|
||||
a = ((CRC >> 0) & 0xFF) << 16;
|
||||
b = ((CRC >> 8) & 0xFF) << 24;
|
||||
c = ((CRC >> 16) & 0xFF) << 0;
|
||||
d = ((CRC >> 24) & 0xFF) << 8;
|
||||
|
||||
CRC = a + b + c + d;
|
||||
|
||||
/* Return the CRC */
|
||||
return CRC;
|
||||
} /* End ComputeCrc32BitV() */
|
||||
|
||||
|
||||
@@ -581,11 +581,15 @@ 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, "%c", alias[i]);
|
||||
|
||||
// fprintf (stderr, "%X,", alias[i]); //debug
|
||||
|
||||
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
|
||||
@@ -794,22 +798,18 @@ 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, " ");
|
||||
|
||||
if (character == 0)
|
||||
strcat (alias_string, " ");
|
||||
else if (character >= 0x20 && character <= 0xFE)
|
||||
strcat (alias_string, ch);
|
||||
else
|
||||
strcat (alias_string, "*");
|
||||
//Encode to UTF-8
|
||||
ptr += utf8_encode(character, alias_string+ptr);
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " [%04X], ", character);
|
||||
|
||||
@@ -137,9 +137,11 @@ 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);
|
||||
// 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 (err != 0)
|
||||
{
|
||||
@@ -187,6 +189,14 @@ 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;
|
||||
@@ -205,6 +215,7 @@ void openOSSOutput (dsd_opts * opts)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
fmt = 0;
|
||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
|
||||
{
|
||||
@@ -301,6 +312,7 @@ void openOSSOutput (dsd_opts * opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
processAudio (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
@@ -92,6 +92,10 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
else if (state->vtx_key_loaded == 1)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22)
|
||||
{
|
||||
@@ -108,6 +112,21 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[0] == 1)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid == 0x26)
|
||||
{
|
||||
if (state->straight_ks == 0x26)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (encR)
|
||||
@@ -118,6 +137,10 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
encR = 0;
|
||||
}
|
||||
else if (state->vtx_key_loaded == 1)
|
||||
{
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22)
|
||||
{
|
||||
@@ -134,6 +157,21 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[1] == 1)
|
||||
{
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR == 0x26)
|
||||
{
|
||||
if (state->straight_ks == 0x26)
|
||||
{
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//debug disable enc check for AES testing
|
||||
@@ -723,6 +761,21 @@ void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state)
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[0] == 1)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR == 0x26)
|
||||
{
|
||||
if (state->straight_ks == 0x26)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//WIP: Mute if on B list (or not W list)
|
||||
@@ -1123,6 +1176,10 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
else if (state->vtx_key_loaded == 1)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22)
|
||||
{
|
||||
@@ -1139,6 +1196,21 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[0] == 1)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algid == 0x26)
|
||||
{
|
||||
if (state->straight_ks == 0x26)
|
||||
{
|
||||
encL = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (encR)
|
||||
@@ -1149,6 +1221,10 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
encR = 0;
|
||||
}
|
||||
else if (state->vtx_key_loaded == 1)
|
||||
{
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22)
|
||||
{
|
||||
@@ -1165,6 +1241,21 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
|
||||
{
|
||||
//going to need a better check for this later on, or seperated keys or something
|
||||
if (state->aes_key_loaded[1] == 1)
|
||||
{
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
else if (state->payload_algidR == 0x26)
|
||||
{
|
||||
if (state->straight_ks == 0x26)
|
||||
{
|
||||
encR = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//debug for AES
|
||||
@@ -2194,6 +2285,8 @@ 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
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
@@ -121,22 +122,46 @@ use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
|
||||
UNUSED(symbol);
|
||||
|
||||
int i;
|
||||
int sbuf2[128];
|
||||
int lmin, lmax, lsum;
|
||||
int lmn[2] = {INT_MAX, INT_MAX};
|
||||
int lmx[2] = {INT_MIN, INT_MIN};
|
||||
|
||||
// single-pass tracking of the 2 smallest and 2 largest values in the symbol window
|
||||
for (i = 0; i < opts->ssize; i++)
|
||||
{
|
||||
sbuf2[i] = state->sbuf[i];
|
||||
int x = state->sbuf[i];
|
||||
if (x < lmn[1])
|
||||
{
|
||||
if (x < lmn[0])
|
||||
{
|
||||
lmn[1] = lmn[0];
|
||||
lmn[0] = x;
|
||||
}
|
||||
else
|
||||
{
|
||||
lmn[1] = x;
|
||||
}
|
||||
}
|
||||
if (x > lmx[1])
|
||||
{
|
||||
if (x > lmx[0])
|
||||
{
|
||||
lmx[1] = lmx[0];
|
||||
lmx[0] = x;
|
||||
}
|
||||
else
|
||||
{
|
||||
lmx[1] = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qsort (sbuf2, opts->ssize, sizeof (int), comp);
|
||||
|
||||
// continuous update of min/max in rf_mod=1 (QPSK) mode
|
||||
// in c4fm min/max must only be updated during sync
|
||||
if (state->rf_mod == 1)
|
||||
{
|
||||
lmin = (sbuf2[0] + sbuf2[1]) / 2;
|
||||
lmax = (sbuf2[(opts->ssize - 1)] + sbuf2[(opts->ssize - 2)]) / 2;
|
||||
lmin = (lmn[0] + lmn[1]) / 2;
|
||||
lmax = (lmx[1] + lmx[0]) / 2;
|
||||
state->minbuf[state->midx] = lmin;
|
||||
state->maxbuf[state->midx] = lmax;
|
||||
if (state->midx == (opts->msize - 1))
|
||||
@@ -178,7 +203,7 @@ use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
|
||||
|
||||
if (opts->datascope == 1)
|
||||
{
|
||||
print_datascope(opts, state, sbuf2);
|
||||
print_datascope(opts, state, state->sbuf);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -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, event_struct);
|
||||
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 = 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, event_struct);
|
||||
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 = 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
|
||||
{
|
||||
|
||||
627
src/dsd_file.c
627
src/dsd_file.c
@@ -18,6 +18,7 @@
|
||||
#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)
|
||||
{
|
||||
@@ -501,7 +502,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, Event_History_I * event_struct)
|
||||
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, char * custom_tag, Event_History_I * event_struct)
|
||||
{
|
||||
sf_close(wav_file);
|
||||
|
||||
@@ -537,11 +538,46 @@ 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_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tgt_str, src_str);
|
||||
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);
|
||||
else //is a numerical value
|
||||
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);
|
||||
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);
|
||||
|
||||
if (timestr != NULL)
|
||||
{
|
||||
@@ -841,6 +877,76 @@ void unpack_ambe (uint8_t * input, char * ambe)
|
||||
ambe[48] = input[6] >> 7;
|
||||
}
|
||||
|
||||
uint64_t reverse_lfsr_32a_to_len(uint8_t * iv, int16_t len)
|
||||
{
|
||||
|
||||
uint64_t lfsr = 0, bit1 = 0, bit2 = 0;
|
||||
|
||||
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
|
||||
|
||||
memset (iv, 0, 8*sizeof(uint8_t));
|
||||
|
||||
for(int16_t cnt = 0; cnt < len; cnt++)
|
||||
{
|
||||
//32,22,2,1 (per Xilinx XAPP 052) Table 3: Taps for Maximum-Length LFSR Counters
|
||||
|
||||
//taps at the +1 position on all but MSB, then check the LSB and configure bit as required
|
||||
bit1 = ( (lfsr >> 22) ^ (lfsr >> 2) ^ (lfsr >> 1) ) & 0x1;
|
||||
bit2 = lfsr & 1;
|
||||
if (bit1 == bit2)
|
||||
bit2 = 0;
|
||||
else bit2 = 1;
|
||||
|
||||
//just run this in reverse of normal LFSR
|
||||
lfsr = (lfsr >> 1) | (bit2 << 31);
|
||||
}
|
||||
|
||||
for (int16_t i = 0; i < 4; i++)
|
||||
iv[i] = (lfsr >> (24-(i*8))) & 0xFF;
|
||||
|
||||
fprintf (stderr, "REV LFSR AES(%02d): ", len);
|
||||
for (int16_t i = 0; i < 4; i++)
|
||||
fprintf (stderr, "%02X", iv[i]);
|
||||
|
||||
return bit2;
|
||||
|
||||
}
|
||||
|
||||
uint64_t reverse_lfsr_32d_to_len(uint8_t * iv, int16_t len)
|
||||
{
|
||||
|
||||
uint64_t lfsr = 0, bit1 = 0, bit2 = 0;
|
||||
|
||||
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
|
||||
|
||||
memset (iv, 0, 8*sizeof(uint8_t));
|
||||
|
||||
for(int16_t cnt = 0; cnt < len; cnt++)
|
||||
{
|
||||
//magical taps that somebody must have dreamt up
|
||||
|
||||
//taps at the +1 position on all but MSB, then check the LSB and configure bit as required
|
||||
bit1 = ((lfsr >> 4) ^ (lfsr >> 2)) & 0x1;
|
||||
bit2 = lfsr & 1;
|
||||
if (bit1 == bit2)
|
||||
bit2 = 0;
|
||||
else bit2 = 1;
|
||||
|
||||
//just run this in reverse of normal LFSR
|
||||
lfsr = (lfsr >> 1) | (bit2 << 31);
|
||||
}
|
||||
|
||||
for (int16_t i = 0; i < 4; i++)
|
||||
iv[i] = (lfsr >> (24-(i*8))) & 0xFF;
|
||||
|
||||
fprintf (stderr, "REV LFSR RC4(%02d): ", len);
|
||||
for (int16_t i = 0; i < 4; i++)
|
||||
fprintf (stderr, "%02X", iv[i]);
|
||||
|
||||
return bit2;
|
||||
|
||||
}
|
||||
|
||||
//recover previous IV for SDRTrunk .mbe files when P25p1
|
||||
uint64_t reverse_lfsr_64_to_len(dsd_opts * opts, uint8_t * iv, int16_t len)
|
||||
{
|
||||
@@ -928,9 +1034,12 @@ 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;
|
||||
@@ -976,6 +1085,28 @@ 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 = (uint8_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;
|
||||
|
||||
}
|
||||
|
||||
char ambe_d[49]; memset(ambe_d, 0, sizeof(ambe_d));
|
||||
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||
state->errs2 = state->errs;
|
||||
@@ -1052,7 +1183,6 @@ 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;
|
||||
@@ -1199,6 +1329,16 @@ 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);
|
||||
@@ -1210,6 +1350,12 @@ 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);
|
||||
|
||||
@@ -1293,6 +1439,18 @@ 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)
|
||||
{
|
||||
@@ -1303,6 +1461,57 @@ 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
|
||||
@@ -1336,6 +1545,29 @@ 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);
|
||||
|
||||
@@ -1344,6 +1576,127 @@ 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;
|
||||
|
||||
}
|
||||
|
||||
else if ((alg_id == 0x24 || alg_id == 0x25) && state->payload_mi != 0)
|
||||
{
|
||||
|
||||
uint8_t ks_bytes[375];
|
||||
memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
|
||||
uint8_t aes_key[32];
|
||||
memset(aes_key, 0, sizeof(aes_key));
|
||||
|
||||
uint8_t empty[32];
|
||||
memset(empty, 0, sizeof(empty));
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
//check to see if a key is actually loaded
|
||||
if (memcmp (aes_key, empty, sizeof(aes_key)) != 0)
|
||||
{
|
||||
|
||||
if (alg_id == 0x24) //128, or 256
|
||||
aes_ofb_keystream_output(state->aes_iv, aes_key, ks_bytes, 0, 16); //16*16=256
|
||||
else aes_ofb_keystream_output(state->aes_iv, aes_key, ks_bytes, 2, 16); //16*16=256
|
||||
|
||||
unpack_byte_array_into_bit_array(ks_bytes+16, ks, 256-16); //unpack starting after discards
|
||||
|
||||
ks_available = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Forced Application of DMRA DES Mode (untested on .mbe real samples)
|
||||
if (alg_id == 0x22 && state->R != 0 && state->payload_mi != 0)
|
||||
{
|
||||
//expand IV from 32 to 64 (late entry iv should fix this)
|
||||
// if (alg_id == 0x22)
|
||||
// LFSR64(state);
|
||||
|
||||
uint8_t ks_bytes[375];
|
||||
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+8, ks, 256-8);
|
||||
|
||||
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
|
||||
@@ -1400,6 +1753,36 @@ 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);
|
||||
@@ -1430,7 +1813,11 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//debug set value
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n IV: %016llX;", iv_hex); //not really needed if loaded into array
|
||||
{
|
||||
if (iv_hex > 0xFFFFFFFF)
|
||||
fprintf (stderr, "\n IV: %016llX;", iv_hex);
|
||||
else fprintf (stderr, "\n IV: %08llX;", iv_hex);
|
||||
}
|
||||
|
||||
//debug print current str_buffer
|
||||
// fprintf (stderr, "\n Encryption MI/IV: %s", str_buffer);
|
||||
@@ -1445,9 +1832,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[15]; memset(kiv, 0, sizeof(kiv));
|
||||
uint8_t kiv[32]; memset(kiv, 0, sizeof(kiv));
|
||||
|
||||
//Test: Setup a simple RC4 for now (working)
|
||||
//RC4 (v1 tested and working on P25, v2 should be okay now)
|
||||
if ( (alg_id == 0xAA || alg_id == 0x21) && state->R != 0 )
|
||||
{
|
||||
|
||||
@@ -1480,19 +1867,163 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
ks_available = 1;
|
||||
|
||||
}
|
||||
|
||||
} //end test
|
||||
//P25 and DMR DES (v1 tested and working on P1, v2 should be okay now), DMR Untested but should be okay
|
||||
else if ((alg_id == 0x22 || alg_id == 0x81) && state->R != 0)
|
||||
{
|
||||
|
||||
//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.
|
||||
//expand IV from 32 to 64
|
||||
if (alg_id == 0x22)
|
||||
LFSR64(state);
|
||||
|
||||
//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)
|
||||
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256
|
||||
|
||||
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 and DMR AES (v1 and v2 tested working on P1), working on DMR
|
||||
else if ( (alg_id == 0x24 || alg_id == 0x25 || 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
|
||||
if (alg_id == 0x24 || alg_id == 0x25)
|
||||
lfsr_32_to_128(aes_iv);
|
||||
else lfsr_64_to_128(aes_iv);
|
||||
|
||||
if (alg_id == 0x89 || alg_id == 0x24) //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
|
||||
|
||||
//reset ks_idx to 0
|
||||
ks_idx = 0;
|
||||
@@ -1516,6 +2047,9 @@ 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);
|
||||
@@ -1523,7 +2057,10 @@ 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)
|
||||
@@ -1544,7 +2081,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " # %02d; KS_IDX_I: %04d;", imbe_counter, ks_idx_i);
|
||||
// fprintf (stderr, "\n IMBE# %02d; KS_IDX: %d; KS_IDX_I: %d; ", imbe_counter, ks_idx, ks_idx_i);
|
||||
|
||||
//debug
|
||||
// if (is_enc == 1 && ks_available == 0)
|
||||
@@ -1553,7 +2090,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
|
||||
// fprintf (stderr, " Enc Play;");
|
||||
|
||||
}
|
||||
else if (protocol == 2) //P25p2 AMBE
|
||||
else if (protocol == 2) //P25p2 or DMR AMBE+2
|
||||
{
|
||||
//debug print current str_buffer
|
||||
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
|
||||
@@ -1567,6 +2104,56 @@ 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)
|
||||
{
|
||||
//run LFSR evaluate stored codewords for matching IV or replace if not match or not set
|
||||
if (state->forced_alg_id == 0x21 && state->payload_mi != 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
LFSR(state);
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
dmr_late_entry_mi (opts, state);
|
||||
|
||||
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, 4);
|
||||
return dsd_input_filter(sample, 2);
|
||||
}
|
||||
|
||||
short
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#include "p25p1_check_nid.h"
|
||||
|
||||
void
|
||||
printFrameInfo (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
@@ -262,21 +260,10 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
processYSF(opts, state);
|
||||
return;
|
||||
}
|
||||
//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) )
|
||||
//M17 STR
|
||||
else if (state->synctype == 16)
|
||||
{
|
||||
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);
|
||||
processM17STR(opts, state);
|
||||
return;
|
||||
}
|
||||
//P25 P2
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
|
||||
void
|
||||
@@ -162,10 +163,9 @@ 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?
|
||||
char synctest_buf[10240 + 48]; //history for sync matching; look-back window up to 48 bytes
|
||||
int lmin, lmax, lidx;
|
||||
|
||||
//assign t_max value based on decoding type expected (all non-auto decodes first)
|
||||
@@ -198,25 +198,23 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else t_max = 24; //24 for everything else
|
||||
|
||||
int lbuf[48], lbuf2[48]; //if we use t_max in these arrays, and t >= t_max in condition below, then it can overflow those checks in there if t exceeds t_max
|
||||
int lbuf[48]; //if we use t_max in these arrays, and t >= t_max in condition below, then it can overflow those checks in there if t exceeds t_max
|
||||
int lsum;
|
||||
//init the lbuf
|
||||
memset (lbuf, 0, sizeof(lbuf));
|
||||
memset (lbuf2, 0, sizeof(lbuf2));
|
||||
|
||||
// detect frame sync
|
||||
t = 0;
|
||||
synctest10[10] = 0;
|
||||
synctest[24] = 0;
|
||||
synctest8[8] = 0; //M17, wasn't initialized or terminated (source of much pain and frustration in Cygwin)
|
||||
synctest8[8] = 0;
|
||||
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;
|
||||
modulation[7] = 0; //not initialized or terminated (unsure if this would be an issue or not)
|
||||
synctest_pos = 0;
|
||||
synctest_p = synctest_buf + 10;
|
||||
synctest_p = synctest_buf + 48;
|
||||
sync = 0;
|
||||
lmin = 0;
|
||||
lmax = 0;
|
||||
@@ -376,13 +374,38 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
*synctest_p = dibit;
|
||||
if (t >= t_max) //works excelent now with short sync patterns, and no issues with large ones!
|
||||
{
|
||||
int lmn[5] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX};
|
||||
int lmx[5] = {INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN};
|
||||
int j, x;
|
||||
for (i = 0; i < t_max; i++) //24
|
||||
{
|
||||
lbuf2[i] = lbuf[i];
|
||||
x = lbuf[i];
|
||||
if (x < lmn[4])
|
||||
{
|
||||
for (j = 4; j > 0 && x < lmn[j - 1]; j--)
|
||||
lmn[j] = lmn[j - 1];
|
||||
lmn[j] = x;
|
||||
}
|
||||
if (x > lmx[4])
|
||||
{
|
||||
for (j = 4; j > 0 && x > lmx[j - 1]; j--)
|
||||
lmx[j] = lmx[j - 1];
|
||||
lmx[j] = x;
|
||||
}
|
||||
}
|
||||
qsort (lbuf2, t_max, sizeof (int), comp);
|
||||
lmin = (lbuf2[1] + lbuf2[2] + lbuf2[3]) / 3;
|
||||
lmax = (lbuf2[t_max - 3] + lbuf2[t_max - 2] + lbuf2[t_max - 1]) / 3;
|
||||
|
||||
//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 = (lmn[0] + lmn[1] + lmn[2]) / 3;
|
||||
lmax = (lmx[2] + lmx[1] + lmx[0]) / 3;
|
||||
}
|
||||
//on longer sync patterns, we can get more towards the middle and eliminate outlier values
|
||||
else
|
||||
{
|
||||
lmin = (lmn[2] + lmn[3] + lmn[4]) / 3;
|
||||
lmax = (lmx[4] + lmx[3] + lmx[2]) / 3;
|
||||
}
|
||||
|
||||
if (state->rf_mod == 1)
|
||||
{
|
||||
@@ -419,8 +442,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
|
||||
//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 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 (opts->frame_nxdn48 == 1 || opts->frame_nxdn96 == 1 || opts->frame_dpmr == 1 || opts->frame_m17 == 1)
|
||||
{
|
||||
@@ -566,139 +589,24 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
//end YSF sync
|
||||
|
||||
//M17 Sync -- Just STR and LSF for now
|
||||
strncpy(synctest16, (synctest_p - 15), 16);
|
||||
//M17 Sync -- Just STR
|
||||
strncpy(synctest8, (synctest_p - 7), 8);
|
||||
if(opts->frame_m17 == 1)
|
||||
{
|
||||
//preambles will skip dibits in an attempt to prime the
|
||||
//demodulator but not attempt any decoding
|
||||
if (strcmp(synctest8, M17_PRE) == 0)
|
||||
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)
|
||||
{
|
||||
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");
|
||||
return (16);
|
||||
}
|
||||
state->lastsynctype = 16;
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
//end M17
|
||||
@@ -1257,8 +1165,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//NXDN
|
||||
#ifdef NXDN_OLD_SYNC
|
||||
//NXDN (previous sync, now a cmake option -DNOS=ON ..)
|
||||
else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
|
||||
{
|
||||
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
|
||||
@@ -1272,11 +1180,14 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
)
|
||||
{
|
||||
|
||||
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->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (28);
|
||||
}
|
||||
@@ -1294,9 +1205,72 @@ 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);
|
||||
@@ -1304,7 +1278,9 @@ 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
|
||||
@@ -1412,7 +1388,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// buffer reset
|
||||
synctest_pos = 0;
|
||||
synctest_p = synctest_buf;
|
||||
synctest_p = synctest_buf + 48;
|
||||
noCarrier (opts, state);
|
||||
|
||||
}
|
||||
|
||||
595
src/dsd_gps.c
595
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 = getDate();
|
||||
char * timestr = getTime();
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
|
||||
//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 = getDate();
|
||||
char * timestr = getTime();
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
|
||||
int s = (int)fkph; //rounded interger format for the log report
|
||||
int a = 0;
|
||||
@@ -281,269 +281,146 @@ void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint
|
||||
|
||||
}
|
||||
|
||||
//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)
|
||||
//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)
|
||||
{
|
||||
|
||||
//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
|
||||
//I really wonder about the engineers who laid out this message
|
||||
|
||||
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
|
||||
//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.
|
||||
|
||||
//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 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);
|
||||
|
||||
//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 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);
|
||||
|
||||
//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 8,9
|
||||
uint32_t time_secs_low = (uint32_t)ConvertBitIntoBytes(input+64, 16);
|
||||
|
||||
//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, 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
|
||||
|
||||
//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;
|
||||
//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);
|
||||
|
||||
//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;
|
||||
//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)nmea_speed * 0.514444; UNUSED(fmps);
|
||||
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
|
||||
fkph = (float)nmea_speed * 1.852f; UNUSED(fkph);
|
||||
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);
|
||||
|
||||
//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) );
|
||||
//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;
|
||||
|
||||
//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);
|
||||
char deg_glyph[4];
|
||||
sprintf (deg_glyph, "%s", "°");
|
||||
|
||||
//print results
|
||||
fprintf (stderr, "\n");
|
||||
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
|
||||
if (phase == 2)
|
||||
{
|
||||
if (nmea_speed > 126)
|
||||
fprintf (stderr, " SPD > 126 knots or %f MPH;", fmph); //using MPH here, its Harris, they're in the U.S.
|
||||
fprintf (stderr, " LCH: %d;", slot);
|
||||
if (src != 0)
|
||||
fprintf (stderr, " SRC: %08d;", src);
|
||||
else
|
||||
fprintf (stderr, " SPD: %d knots; %f MPH;", nmea_speed, fmph);
|
||||
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;");
|
||||
}
|
||||
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)", latitude, deg_glyph, longitude, deg_glyph);
|
||||
sprintf (state->dmr_embedded_gps[slot], "(%f%s, %f%s)", lat_DDMMmm, deg_glyph, lon_DDMMmm, 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
|
||||
FILE * pFile; //file pointer
|
||||
if (opts->lrrp_file_output == 1)
|
||||
if (opts->lrrp_file_output == 1 && src != 0 && gps_quality != 3)
|
||||
{
|
||||
|
||||
char * datestr = getDate();
|
||||
char * timestr = getTime();
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
|
||||
//rounded interger formats for the log report
|
||||
int s = (int)fkph;
|
||||
int a = nmea_cog;
|
||||
|
||||
//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 ); //could switch to UTC time on PDU if desired
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//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();
|
||||
int a = (int)course_deg;
|
||||
|
||||
//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", lat_dec);
|
||||
fprintf (pFile, "%.6lf\t", lon_dec);
|
||||
fprintf (pFile, "%.6lf\t", lat_DDMMmm);
|
||||
fprintf (pFile, "%.6lf\t", lon_DDMMmm);
|
||||
fprintf (pFile, "%d\t ", s);
|
||||
fprintf (pFile, "%d\t ", a);
|
||||
fprintf (pFile, "\n");
|
||||
@@ -562,9 +439,14 @@ void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input)
|
||||
|
||||
}
|
||||
|
||||
//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
|
||||
//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));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
//externalize embedded GPS - Confirmed working now on NE, NW, SE, and SW coordinates
|
||||
@@ -646,7 +528,7 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||
if (slot == 1) src = state->lasttgR;
|
||||
|
||||
//save to event history string
|
||||
if (state->event_history_s[slot].Event_History_Items[0].source_id == src)
|
||||
// if (state->event_history_s[slot].Event_History_Items[0].source_id == src)
|
||||
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
|
||||
@@ -654,8 +536,8 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
|
||||
char * datestr = getDate();
|
||||
char * timestr = getTime();
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
@@ -762,8 +644,9 @@ 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 = getDate();
|
||||
char * timestr = getTime();
|
||||
|
||||
char * datestr = getDateS();
|
||||
char * timestr = getTimeC();
|
||||
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
@@ -827,6 +710,7 @@ 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
|
||||
@@ -835,3 +719,240 @@ 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,26 +7,31 @@ 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;
|
||||
long int group_number = 0; //local group number for array index value
|
||||
UNUSED(group_number);
|
||||
int i = 0;
|
||||
while (fgets(buffer, BSIZE, fp)) {
|
||||
|
||||
unsigned 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)
|
||||
{
|
||||
@@ -34,23 +39,38 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
|
||||
state->group_array[i].groupNumber = atol(field);
|
||||
fprintf (stderr, "%ld, ", state->group_array[i].groupNumber);
|
||||
}
|
||||
if (field_count == 1)
|
||||
else if (field_count == 1)
|
||||
{
|
||||
strcpy(state->group_array[i].groupMode, field);
|
||||
// strcpy(state->group_array[i].groupMode, field);
|
||||
strncpy(state->group_array[i].groupMode, field, 2); //only copy two letters max A, B, D, DE
|
||||
fprintf (stderr, "%s, ", state->group_array[i].groupMode);
|
||||
}
|
||||
if (field_count == 2)
|
||||
else if (field_count == 2)
|
||||
{
|
||||
strcpy(state->group_array[i].groupName, field);
|
||||
// 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';
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", state->group_array[i].groupName);
|
||||
}
|
||||
|
||||
field = strtok(NULL, ",");
|
||||
field_count++;
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
i++;
|
||||
state->group_tally++;
|
||||
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
|
||||
702
src/dsd_main.c
702
src/dsd_main.c
File diff suppressed because it is too large
Load Diff
560
src/dsd_mbe.c
560
src/dsd_mbe.c
@@ -18,6 +18,8 @@
|
||||
#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
|
||||
@@ -91,7 +93,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)
|
||||
if (opts->wav_out_f != NULL && (opts->dmr_stereo_wav == 1 || opts->static_wav_file == 1))
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
@@ -139,7 +141,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)
|
||||
if (opts->wav_out_f != NULL && (opts->dmr_stereo_wav == 1 || opts->static_wav_file == 1))
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
@@ -173,6 +175,30 @@ 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);
|
||||
@@ -278,9 +304,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
if (state->payload_algid == 0x9F) //DES-XL
|
||||
des_multi_keystream_output (state->payload_miP, state->R, state->ks_octetL, 2, state->xl_is_hdu); //hard coded bit count value, xl_is_hdu determines lfsr run values
|
||||
if (state->payload_algid == 0x84) //AES256
|
||||
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 2, 14); //14 + 1 discard round
|
||||
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 2, 16); //15 + 1 discard round
|
||||
if (state->payload_algid == 0x89) //AES128
|
||||
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 0, 14); //14 + 1 discard round
|
||||
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 0, 16); //15 + 1 discard round
|
||||
|
||||
}
|
||||
|
||||
@@ -448,7 +474,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->M == 1 && state->R > 0) )
|
||||
(state->forced_alg_id == 1 && state->R > 0) )
|
||||
{
|
||||
|
||||
if (state->payload_miN == 0)
|
||||
@@ -509,7 +535,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+8, state->ks_bitstreamL, 15*8);
|
||||
unpack_byte_array_into_bit_array(state->ks_octetL+16, state->ks_bitstreamL, 14*16);
|
||||
|
||||
//reset flag to 0
|
||||
state->nxdn_new_iv = 0;
|
||||
@@ -520,7 +546,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 creation is currently inside of the NXDN_decode_VCALL_IV function
|
||||
//Keystream Application (no +7 discard like DMRA)
|
||||
for (i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= state->ks_bitstreamL[state->bit_counterL++];
|
||||
|
||||
@@ -556,7 +582,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->M == 0 && state->payload_algid == 0)
|
||||
if (state->forced_alg_id == 0 && state->payload_algid == 0)
|
||||
{
|
||||
//see if we need to hash a value larger than 16-bits
|
||||
hash = state->lasttg & 0xFFFFFF;
|
||||
@@ -573,7 +599,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
if (state->rkey_array[hash] != 0)
|
||||
{
|
||||
#ifdef VERTEX_KEY40_BY_TG
|
||||
state->vtx40_key = state->rkey_array[hash] & 0xFFFFFFFFFF;
|
||||
vertex_40_keystream_creation(state, state->vtx40_key);
|
||||
#elif VERTEX_KEY256_BY_TG
|
||||
//TODO: This
|
||||
#else
|
||||
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
|
||||
#endif
|
||||
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
|
||||
opts->dmr_mute_encL = 0;
|
||||
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
|
||||
@@ -582,7 +615,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->M == 1) )
|
||||
(state->K > 0 && state->forced_alg_id == 1) )
|
||||
{
|
||||
k = BPK[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
@@ -593,67 +626,101 @@ 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->M == 1) )
|
||||
if ( (state->vtx_key_loaded != 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||
(state->vtx_key_loaded != 0 && state->forced_alg_id == 1) )
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
|
||||
unsigned long long int k1 = state->K1;
|
||||
unsigned long long int k2 = state->K2;
|
||||
unsigned long long int k3 = state->K3;
|
||||
unsigned long long int k4 = state->K4;
|
||||
|
||||
int T_Key[256] = {0};
|
||||
int pN[882] = {0};
|
||||
|
||||
int len = 0;
|
||||
|
||||
if (k2 == 0)
|
||||
{
|
||||
len = 39;
|
||||
k1 = k1 << 24;
|
||||
}
|
||||
if (k2 != 0)
|
||||
{
|
||||
len = 127;
|
||||
}
|
||||
if (k4 != 0)
|
||||
{
|
||||
len = 255;
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
|
||||
}
|
||||
|
||||
for (i = 0; i < 882; i++)
|
||||
{
|
||||
pN[i] = T_Key[pos];
|
||||
pos++;
|
||||
if (pos > len)
|
||||
if (memcmp(ambe_d, ambe_silence, 49) != 0)
|
||||
{
|
||||
pos = 0;
|
||||
for (int i = 0; i < 49; i++)
|
||||
{
|
||||
if (i < 8 || i > 11)
|
||||
ambe_d[i] ^= (char)state->static_ks_bits[0][i];
|
||||
}
|
||||
}
|
||||
opts->dmr_mute_encL = 0; //shim to unmute
|
||||
}
|
||||
|
||||
//sanity check
|
||||
if (state->DMRvcL > 17) //18
|
||||
//Hytera BP //TODO: Clean this mess up
|
||||
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->DMRvcL = 17; //18
|
||||
|
||||
int pos = 0;
|
||||
|
||||
unsigned long long int k1 = state->K1;
|
||||
unsigned long long int k2 = state->K2;
|
||||
unsigned long long int k3 = state->K3;
|
||||
unsigned long long int k4 = state->K4;
|
||||
|
||||
int T_Key[256] = {0};
|
||||
int pN[882] = {0};
|
||||
|
||||
int len = 0;
|
||||
|
||||
if (k2 == 0)
|
||||
{
|
||||
len = 39;
|
||||
k1 = k1 << 24;
|
||||
}
|
||||
if (k2 != 0)
|
||||
{
|
||||
len = 127;
|
||||
}
|
||||
if (k4 != 0)
|
||||
{
|
||||
len = 255;
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
|
||||
}
|
||||
|
||||
for (i = 0; i < 882; i++)
|
||||
{
|
||||
pN[i] = T_Key[pos];
|
||||
pos++;
|
||||
if (pos > len)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//sanity check
|
||||
if (state->DMRvcL > 17) //18
|
||||
{
|
||||
state->DMRvcL = 17; //18
|
||||
}
|
||||
|
||||
pos = state->DMRvcL * 49;
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
pos += 49;
|
||||
else
|
||||
{
|
||||
for(i = 0; i < 49; i++)
|
||||
{
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
state->DMRvcL++;
|
||||
}
|
||||
|
||||
pos = state->DMRvcL * 49;
|
||||
for(i = 0; i < 49; i++)
|
||||
//Caltta BP
|
||||
if ( (state->straight_ks == 0x26 && state->dmr_so & 0x40 && state->payload_algid == 0x26) || //&& state->payload_keyid != 0
|
||||
(state->straight_ks == 0x26 && state->forced_alg_id == 0x26) )
|
||||
{
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
state->DMRvcL++;
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 56;
|
||||
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]++)%state->straight_mod] & 1); //Yikes!
|
||||
state->static_ks_counter[state->currentslot] += 7;
|
||||
}
|
||||
}
|
||||
|
||||
//DMR and P25p2 DES-OFB 56 Handling, Slot 1, VCH 0 -- consider moving into the AES handler
|
||||
@@ -712,6 +779,8 @@ 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 >= 0x31 && \
|
||||
state->payload_algid <= 0x3A && state->aes_key_loaded[0] == 1 ) || //KIRI Any
|
||||
(state->currentslot == 0 && state->payload_algid == 0x02 && state->R != 0 ) ) //HYT ENHANCED
|
||||
{
|
||||
|
||||
@@ -744,9 +813,13 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
if(state->payload_algid == 0x02)
|
||||
{
|
||||
n = 0;
|
||||
hytera_enhanced_rc4_setup(opts, state, state->R, state->payload_mi);
|
||||
hytera_enhanced_rc4_setup(opts, state, state->R, state->payload_mi, 135);
|
||||
}
|
||||
if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
|
||||
{
|
||||
n = 0;
|
||||
kirisun_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
|
||||
{
|
||||
@@ -759,18 +832,17 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
}
|
||||
|
||||
//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++)
|
||||
//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
|
||||
{
|
||||
for (j = 0; j < 8; j++)
|
||||
ambe_d[z++] ^= state->ks_bitstreamL[state->bit_counterL++];
|
||||
for (i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= 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;
|
||||
@@ -808,15 +880,24 @@ 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));
|
||||
|
||||
state->dropL += 7;
|
||||
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));
|
||||
|
||||
//unpack deciphered plain array back into ambe_d bit array
|
||||
memset (ambe_d, 0, 49*sizeof(char));
|
||||
unpack_ambe(plain, ambe_d);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -857,49 +938,118 @@ 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)
|
||||
{
|
||||
|
||||
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];
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
//DMR TYT EP, Either Slot (static single key'd enforced KS)
|
||||
if (state->tyt_ep == 1)
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 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);
|
||||
}
|
||||
}
|
||||
|
||||
//DMR Kenwood Scrambler, Either Slot (static single key'd enforced KS) //should probably break this up, but this is a test for now
|
||||
//DMR Kenwood Scrambler, Either Slot (static single key'd enforced KS)
|
||||
if (state->ken_sc == 1)
|
||||
{
|
||||
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!
|
||||
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!
|
||||
}
|
||||
}
|
||||
|
||||
//DMR Anytone BP, Either Slot (static single key'd enforced KS)
|
||||
if (state->any_bp == 1)
|
||||
{
|
||||
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!
|
||||
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!
|
||||
}
|
||||
}
|
||||
|
||||
//Generic Straight Static Keystream
|
||||
if (state->straight_ks == 1)
|
||||
{
|
||||
//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!
|
||||
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!
|
||||
//apply optional discard (i.e., caltta bp)
|
||||
if (state->straight_discard != 0)
|
||||
state->static_ks_counter[state->currentslot] += state->straight_discard;
|
||||
}
|
||||
}
|
||||
|
||||
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
||||
@@ -932,7 +1082,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->M == 0 && state->payload_algidR == 0)
|
||||
if (state->forced_alg_id == 0 && state->payload_algidR == 0)
|
||||
{
|
||||
//see if we need to hash a value larger than 16-bits
|
||||
hash = state->lasttgR & 0xFFFFFF;
|
||||
@@ -949,7 +1099,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
if (state->rkey_array[hash] != 0)
|
||||
{
|
||||
#ifdef VERTEX_KEY40_BY_TG
|
||||
state->vtx40_key = state->rkey_array[hash] & 0xFFFFFFFFFF;
|
||||
vertex_40_keystream_creation(state, state->vtx40_key);
|
||||
#elif VERTEX_KEY256_BY_TG
|
||||
//TODO: This
|
||||
#else
|
||||
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
|
||||
#endif
|
||||
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
|
||||
opts->dmr_mute_encR = 0;
|
||||
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
|
||||
@@ -958,7 +1115,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->M == 1) )
|
||||
(state->K > 0 && state->forced_alg_id == 1) )
|
||||
{
|
||||
k = BPK[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
@@ -969,8 +1126,23 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
}
|
||||
|
||||
if ( (state->vtx_key_loaded != 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
||||
(state->vtx_key_loaded != 0 && state->forced_alg_id == 1) )
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) != 0)
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
{
|
||||
if (i < 8 || i > 11)
|
||||
ambe_d[i] ^= (char)state->static_ks_bits[1][i];
|
||||
}
|
||||
}
|
||||
opts->dmr_mute_encR = 0; //shim to unmute
|
||||
}
|
||||
|
||||
//Hytera BP //TODO: Clean this mess up
|
||||
if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
|
||||
(state->K1 > 0 && state->M == 1))
|
||||
(state->K1 > 0 && state->forced_alg_id == 1))
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
@@ -1024,14 +1196,33 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
pos = state->DMRvcR * 49;
|
||||
for(i = 0; i < 49; i++)
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
pos += 49;
|
||||
else
|
||||
{
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
for(i = 0; i < 49; i++)
|
||||
{
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
state->DMRvcR++;
|
||||
}
|
||||
|
||||
//Caltta BP
|
||||
if ( (state->straight_ks == 0x26 && state->dmr_soR & 0x40 && state->payload_algidR == 0x26) || //&& state->payload_keyidR != 0
|
||||
(state->straight_ks == 0x26 && state->forced_alg_id == 0x26) )
|
||||
{
|
||||
if (memcmp(ambe_d, ambe_silence, 49) == 0)
|
||||
state->static_ks_counter[state->currentslot] += 56;
|
||||
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]++)%state->straight_mod] & 1); //Yikes!
|
||||
state->static_ks_counter[state->currentslot] += 7;
|
||||
}
|
||||
}
|
||||
|
||||
//DMR and P25p2 DES-OFB 56 Handling, Slot 2, VCH 1 -- Consider moving into AES handler
|
||||
if ( (state->currentslot == 1 && state->payload_algidR == 0x22 && state->RR != 0) ||
|
||||
(state->currentslot == 1 && state->payload_algidR == 0x81 && state->RR != 0) )
|
||||
@@ -1088,6 +1279,8 @@ 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 >= 0x31 && \
|
||||
state->payload_algidR <= 0x3A && state->aes_key_loaded[1] == 1 ) || //KIRI Any
|
||||
(state->currentslot == 1 && state->payload_algidR == 0x02 && state->RR != 0 ) ) //HYT ENHANCED
|
||||
{
|
||||
|
||||
@@ -1120,9 +1313,13 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
if(state->payload_algidR == 0x02)
|
||||
{
|
||||
n = 0;
|
||||
hytera_enhanced_rc4_setup(opts, state, state->RR, state->payload_miR);
|
||||
hytera_enhanced_rc4_setup(opts, state, state->RR, state->payload_miR, 135);
|
||||
}
|
||||
if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
|
||||
{
|
||||
n = 0;
|
||||
kirisun_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
|
||||
{
|
||||
@@ -1135,18 +1332,17 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
}
|
||||
|
||||
//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++)
|
||||
//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
|
||||
{
|
||||
for (j = 0; j < 8; j++)
|
||||
ambe_d[z++] ^= state->ks_bitstreamR[state->bit_counterR++];
|
||||
for (i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= 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;
|
||||
@@ -1184,14 +1380,22 @@ 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->errsR < 3)
|
||||
rc4_voice_decrypt(state->dropR, 9, 7, rckey, cipher, plain);
|
||||
else memcpy (plain, cipher, sizeof(plain));
|
||||
state->dropR += 7;
|
||||
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;
|
||||
|
||||
//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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1233,48 +1437,118 @@ 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)
|
||||
{
|
||||
|
||||
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];
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//DMR TYT EP, Either Slot (static single key'd enforced KS)
|
||||
if (state->tyt_ep == 1)
|
||||
{
|
||||
for (int i = 0; i < 49; i++)
|
||||
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 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);
|
||||
}
|
||||
}
|
||||
|
||||
//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)
|
||||
{
|
||||
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!
|
||||
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!
|
||||
}
|
||||
}
|
||||
|
||||
//DMR Anytone BP, Either Slot (static single key'd enforced KS)
|
||||
if (state->any_bp == 1)
|
||||
{
|
||||
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!
|
||||
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!
|
||||
}
|
||||
}
|
||||
|
||||
//Generic Straight Static Keystream
|
||||
if (state->straight_ks == 1)
|
||||
{
|
||||
//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!
|
||||
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!
|
||||
//apply optional discard (i.e., caltta bp)
|
||||
if (state->straight_discard != 0)
|
||||
state->static_ks_counter[state->currentslot] += state->straight_discard;
|
||||
}
|
||||
}
|
||||
|
||||
mbe_processAmbe2450Dataf (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR,
|
||||
@@ -1330,6 +1604,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
if (state->ken_sc == 1)
|
||||
state->dmr_encL = 0;
|
||||
|
||||
if (state->payload_algid == 0x26 && state->straight_ks == 0x26)
|
||||
state->dmr_encL = 0;
|
||||
|
||||
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
|
||||
if (opts->reverse_mute == 1)
|
||||
{
|
||||
@@ -1410,6 +1687,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
if (state->ken_sc == 1)
|
||||
state->dmr_encR = 0;
|
||||
|
||||
if (state->payload_algidR == 0x26 && state->straight_ks == 0x26)
|
||||
state->dmr_encR = 0;
|
||||
|
||||
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
|
||||
if (opts->reverse_mute == 1)
|
||||
{
|
||||
|
||||
343
src/dsd_misc.c
343
src/dsd_misc.c
@@ -8,322 +8,6 @@
|
||||
|
||||
#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
|
||||
@@ -337,7 +21,6 @@ uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
|
||||
// #define HFC 50
|
||||
// #define LFC 100
|
||||
|
||||
|
||||
void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
|
||||
{
|
||||
|
||||
@@ -351,7 +34,6 @@ void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
|
||||
|
||||
}
|
||||
|
||||
|
||||
float LPFilter_Update(LPFilter *filter, float v_in)
|
||||
{
|
||||
|
||||
@@ -382,7 +64,6 @@ void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS)
|
||||
|
||||
}
|
||||
|
||||
|
||||
float HPFilter_Update(HPFilter *filter, float v_in)
|
||||
{
|
||||
|
||||
@@ -592,27 +273,3 @@ 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,10 +34,7 @@ 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
|
||||
{
|
||||
if (opts->m17encoder == 0) //don't allow menu if using M17 encoder
|
||||
ncursesMenu (opts, state);
|
||||
}
|
||||
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
|
||||
@@ -214,14 +211,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->M == 1 || state->M == 0x21) state->M = 0;
|
||||
else state->M = 1;
|
||||
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0;
|
||||
else state->forced_alg_id = 1;
|
||||
}
|
||||
|
||||
if (c == 54) // '6' key, toggle force rc4 key over missing pi header/late entry
|
||||
{
|
||||
if (state->M == 1 || state->M == 0x21) state->M = 0;
|
||||
else state->M = 0x21;
|
||||
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0;
|
||||
else state->forced_alg_id = 0x21;
|
||||
}
|
||||
|
||||
if (c == 105) //'i' key, toggle signal inversion on inverted types
|
||||
@@ -233,7 +230,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_m17 = 1;
|
||||
opts->inverted_nxdn = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -241,7 +238,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_m17 = 0;
|
||||
opts->inverted_nxdn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,8 +379,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, &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_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_file[0] = 0; //Bugfix for decoded wav file display after disabling
|
||||
opts->wav_out_fileR[0] = 0;
|
||||
opts->dmr_stereo_wav = 0;
|
||||
@@ -597,7 +594,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 == 103) //'g' key, toggle tune group calls
|
||||
if (opts->p25_trunk == 1 && c == 84) //'T' 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;
|
||||
@@ -888,16 +885,6 @@ 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;
|
||||
@@ -931,6 +918,26 @@ 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,11 +130,13 @@ 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
|
||||
{
|
||||
@@ -417,7 +419,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 Power Squelch Level (NXDN/dPMR/Analog/Raw only): ");
|
||||
mvwprintw(entry_win, 2, 2, " RTL RMS Squelch Level (NXDN/dPMR/Analog/Raw only): ");
|
||||
mvwprintw(entry_win, 3, 3, " ");
|
||||
echo();
|
||||
refresh();
|
||||
@@ -463,7 +465,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 PWR Squelch: %d", opts->rtl_squelch_level);
|
||||
mvwprintw(entry_win, 11, 2, " RTL RMS 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, " ");
|
||||
@@ -692,8 +694,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, &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_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_file[0] = 0; //Bugfix for decoded wav file display after disabling
|
||||
opts->wav_out_fileR[0] = 0;
|
||||
opts->dmr_stereo_wav = 0;
|
||||
@@ -921,11 +923,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->M == 1 || state->M == 0x21)
|
||||
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21)
|
||||
{
|
||||
state->M = 0;
|
||||
state->forced_alg_id = 0;
|
||||
}
|
||||
else state->M = 1;
|
||||
else state->forced_alg_id = 1;
|
||||
}
|
||||
|
||||
if (option == 5)
|
||||
@@ -951,11 +953,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->M == 1 || state->M == 0x21)
|
||||
// if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21)
|
||||
// {
|
||||
// state->M = 0;
|
||||
// state->forced_alg_id = 0;
|
||||
// }
|
||||
// else state->M = 0x21;
|
||||
// else state->forced_alg_id = 0x21;
|
||||
// }
|
||||
|
||||
//load AES keys
|
||||
@@ -1415,7 +1417,6 @@ 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
|
||||
{
|
||||
@@ -1423,7 +1424,6 @@ 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,10 +1587,12 @@ 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,9 +223,6 @@ 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);
|
||||
@@ -247,15 +244,17 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
if (opts->audio_in_type == 3)
|
||||
{
|
||||
printw ("| RTL: %d;", opts->rtl_dev_index);
|
||||
if (opts->rtl_gain_value == 0)
|
||||
if (opts->rtltcp_enabled == 1)
|
||||
printw ("| RTL_TCP: %s:%d;", opts->rtltcp_hostname, opts->rtltcp_portno);
|
||||
else printw ("| RTL: %d;", opts->rtl_dev_index);
|
||||
if (opts->rtl_gain_actual == -100)
|
||||
printw (" G: AGC;");
|
||||
else
|
||||
printw (" G: %idB;", opts->rtl_gain_value);
|
||||
printw (" G: %i/%i;", opts->rtl_gain_value, opts->rtl_gain_actual);
|
||||
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 (" PWR: %04li;", opts->rtl_pwr);
|
||||
printw (" RMS: %04li;", opts->rtl_rms);
|
||||
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);
|
||||
@@ -282,7 +281,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 ("PWR: %04ld; ", opts->rtl_pwr);
|
||||
if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms);
|
||||
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 (" |");
|
||||
@@ -302,7 +301,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 PWR: %04ld; G: %02.0f%% (/|*) ", opts->rtl_pwr, opts->audio_gainA);
|
||||
printw ("\n| Analog Monitor RMS: %04ld; G: %02.0f%% (/|*) ", opts->rtl_rms, 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: | |");
|
||||
@@ -321,14 +320,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 PWR: %04ld ", opts->rtl_pwr);
|
||||
printw (" - Monitor RMS: %04ld ", opts->rtl_rms);
|
||||
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 ("PWR: %04ld; ", opts->rtl_pwr);
|
||||
if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms);
|
||||
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 (" |");
|
||||
@@ -336,25 +335,6 @@ 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);
|
||||
@@ -385,7 +365,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
printw ("| Trunking -");
|
||||
if (opts->trunk_tune_group_calls == 0) attron(COLOR_PAIR(2));
|
||||
printw (" Group(g)");
|
||||
printw (" Group(T)");
|
||||
attron(COLOR_PAIR(4));
|
||||
if (opts->trunk_tune_private_calls == 0) attron(COLOR_PAIR(2));
|
||||
printw (" Private(u)");
|
||||
@@ -404,8 +384,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(g)");
|
||||
else printw (" GROUP(g)");
|
||||
if (opts->trunk_tune_group_calls == 0) printw (" group(T)");
|
||||
else printw (" GROUP(T)");
|
||||
if (opts->trunk_tune_private_calls == 0) printw (" private(u)");
|
||||
else printw (" PRIVATE(u)");
|
||||
if (opts->trunk_tune_data_calls == 0) printw (" data(d)");
|
||||
@@ -517,18 +497,18 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
#endif
|
||||
// if (opts->aggressive_framesync == 0) printw ("| Selective CRC ERR Bypass Enabled (RAS) \n");
|
||||
if (state->M == 1)
|
||||
if (state->forced_alg_id == 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->M == 0x21)
|
||||
if (state->forced_alg_id == 0x21)
|
||||
{
|
||||
if (state->R != 0) printw ("| Forcing Key Priority -- RC4 Key: %010llX \n", state->R);
|
||||
}
|
||||
if (state->M == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H);
|
||||
if (state->forced_alg_id == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H);
|
||||
|
||||
if (opts->scanner_mode == 1)
|
||||
{
|
||||
@@ -561,7 +541,6 @@ 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
|
||||
@@ -654,20 +633,22 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw ("\n");
|
||||
printw ("| ");
|
||||
|
||||
|
||||
//fill in any extra info, like Meta (IV, etc)
|
||||
if (state->m17_enc == 1)
|
||||
{
|
||||
printw (" Scrambler - Type: %d", state->m17_enc_st);
|
||||
attron(COLOR_PAIR(1));
|
||||
printw ("Scrambler - Type: %d", state->m17_enc_st);
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
|
||||
if (state->m17_enc == 2)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw ("AES-CTR - IV: ");
|
||||
//display packed meta as IV
|
||||
printw ("AES-CTR IV: ");
|
||||
//display AES IV
|
||||
for (i = 0; i < 16; i++)
|
||||
printw ("%02X", state->m17_meta[i]);
|
||||
printw ("%02X", state->m17_aes_iv[i]);
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
|
||||
if (state->m17_enc == 3)
|
||||
@@ -676,6 +657,30 @@ 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");
|
||||
|
||||
}
|
||||
|
||||
@@ -918,10 +923,16 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000);
|
||||
}
|
||||
|
||||
if (state->dmr_is_ras == 1)
|
||||
printw ("{RAS}");
|
||||
|
||||
}
|
||||
else if (lls == 32 || lls == 33 || lls == 34)
|
||||
{
|
||||
printw ("DMR MS - DCC: %02i; ", state->dmr_color_code);
|
||||
|
||||
if (state->dmr_is_ras == 1)
|
||||
printw ("{RAS}");
|
||||
}
|
||||
else if (lls == 0 || lls == 1) //P1
|
||||
{
|
||||
@@ -1101,6 +1112,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algid == 0x26)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Caltta BP ");
|
||||
// if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); } //TODO: This
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algid == 0x02)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
@@ -1109,6 +1127,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw(" Key: %010llX", state->R);
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Kirisun Encryption ");
|
||||
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
|
||||
printw ("\n");
|
||||
|
||||
@@ -1276,6 +1301,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algidR == 0x26)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Caltta BP ");
|
||||
// if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); } //TODO: This
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algidR == 0x02)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
@@ -1284,6 +1316,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
printw(" Key: %010llX", state->RR);
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
|
||||
{
|
||||
attron(COLOR_PAIR(1));
|
||||
printw("Kirisun Advanced ");
|
||||
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
|
||||
attron(COLOR_PAIR(3));
|
||||
}
|
||||
|
||||
printw ("\n");
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ void error(char *msg) {
|
||||
|
||||
struct sockaddr_in address;
|
||||
struct sockaddr_in addressA;
|
||||
struct sockaddr_in addressM17;
|
||||
|
||||
//
|
||||
// Connect
|
||||
@@ -324,18 +323,6 @@ 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)
|
||||
{
|
||||
@@ -353,20 +340,6 @@ 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);
|
||||
@@ -447,46 +420,6 @@ 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_pwr = rtl_return_pwr();
|
||||
opts->rtl_rms = rtl_return_rms();
|
||||
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 pwr value if not using the rtl built in version
|
||||
//get an rms value if not using the rtl built in version
|
||||
if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1)
|
||||
opts->rtl_pwr = raw_pwr(state->analog_out, 960, 1);
|
||||
opts->rtl_rms = raw_rms(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 PWR after filtering does remove the analog spike from the PWR value
|
||||
//Running RMS after filtering does remove the analog spike from the RMS value
|
||||
//but noise floor noise will still produce higher values
|
||||
// if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1)
|
||||
// opts->rtl_pwr = raw_pwr(state->analog_out, 960, 1);
|
||||
// opts->rtl_rms = raw_rms(state->analog_out, 960, 1);
|
||||
|
||||
//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
|
||||
//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
|
||||
{
|
||||
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; //PWR has a 5 count (5 * 180ms) now before cutting off;
|
||||
int count = 5; //RMS 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 pwr = opts->rtl_squelch_level + 1; //one more for the initial loop phase
|
||||
long int rms = 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 pwr will only work properly (for now) with squelch enabled in SDR++ or other
|
||||
pwr = raw_pwr(analog3, 960, 1);
|
||||
//this rms will only work properly (for now) with squelch enabled in SDR++ or other
|
||||
rms = raw_rms(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 pwr will only work properly (for now) with squelch enabled in SDR++
|
||||
pwr = raw_pwr(analog3, 960, 1);
|
||||
//this rms will only work properly (for now) with squelch enabled in SDR++
|
||||
rms = raw_rms(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 pwr value works properly without needing a 'hard' squelch value
|
||||
pwr = rtl_return_pwr();
|
||||
//the rtl rms value works properly without needing a 'hard' squelch value
|
||||
rms = rtl_return_rms();
|
||||
}
|
||||
#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_pwr for TCP/VS here, but the analog spike on EDACS (STM)
|
||||
//NOTE: Ideally, we would run raw_rms 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)
|
||||
// pwr = raw_pwr(analog3, 960, 1);
|
||||
// rms = raw_rms(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_pwr = pwr;
|
||||
opts->rtl_rms = rms;
|
||||
|
||||
|
||||
printFrameSync (opts, state, " EDACS", 0, "A");
|
||||
|
||||
if (pwr < sql) count--;
|
||||
if (rms < sql) count--;
|
||||
else count = 5;
|
||||
|
||||
if (pwr > sql) fprintf(stderr, "%s", KGRN);
|
||||
if (rms > sql) fprintf(stderr, "%s", KGRN);
|
||||
else fprintf(stderr, "%s", KRED);
|
||||
|
||||
fprintf (stderr, " Analog PWR: %04ld SQL: %ld", pwr, sql);
|
||||
fprintf (stderr, " Analog RMS: %04ld SQL: %ld", rms, 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, &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, opts->wav_custom_tag, &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, &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, opts->wav_custom_tag, &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, &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, opts->wav_custom_tag, &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 (PWR, or a dotting sequence is detected)
|
||||
//signal level drops (RMS, 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, &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, opts->wav_custom_tag, &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 (PWR, or a dotting sequence is detected)
|
||||
//signal level drops (RMS, 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, &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, opts->wav_custom_tag, &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 (PWR, or a dotting sequence is detected)
|
||||
//signal level drops (RMS, 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, &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, opts->wav_custom_tag, &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 (PWR, or a dotting sequence is detected)
|
||||
//signal level drops (RMS, 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, &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, opts->wav_custom_tag, &state->event_history_s[0]);
|
||||
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
|
||||
}
|
||||
}
|
||||
@@ -2299,6 +2299,10 @@ 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);
|
||||
@@ -2308,7 +2312,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, &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, opts->wav_custom_tag, &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;
|
||||
int corrections = 0; UNUSED(corrections);
|
||||
for (is = 0; is < 9; is++)
|
||||
{
|
||||
syndromeI += (((rxBits[0] * QR_16_7_6_m_H[16*is + 0])
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
|
||||
2788
src/nxdn_deperm.c
2788
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,40 +1,32 @@
|
||||
//NXDN frame handler
|
||||
//Reworked portions from Osmocom OP25 rx_sync.cc
|
||||
|
||||
/* -*- c++ -*- */
|
||||
/*
|
||||
/*-------------------------------------------------------------------------------
|
||||
* nxdn_frame.c
|
||||
* NXDN Frame Handling
|
||||
*
|
||||
* Reworked portions from Osmocom OP25 rx_sync.cc
|
||||
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
* LWVMOBILE
|
||||
* 2026-03 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "nxdn_const.h"
|
||||
|
||||
// #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)
|
||||
#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)
|
||||
|
||||
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;
|
||||
uint8_t answer[32];
|
||||
uint8_t sacch_answer[32];
|
||||
uint8_t lich = 0;
|
||||
int lich_parity_received;
|
||||
int lich_parity_computed;
|
||||
int voice = 0;
|
||||
@@ -71,12 +63,21 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//nxdn bit buffer, for easy assignment handling
|
||||
int nxdn_bit_buffer[364];
|
||||
int nxdn_dibit_buffer[182];
|
||||
|
||||
#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
|
||||
|
||||
//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));
|
||||
@@ -85,15 +86,18 @@ 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_descramble (lich_dibits, 8);
|
||||
nxdn_pn95_dibit_scrambler (state, lich_dibits, 8);
|
||||
|
||||
lich = 0;
|
||||
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
|
||||
//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;
|
||||
}
|
||||
|
||||
//debug lich as a 16-bit value (with encoding "dividing")
|
||||
uint8_t lich_bits[16]; memset(lich_bits, 0, sizeof(lich_bits));
|
||||
@@ -107,18 +111,17 @@ 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;
|
||||
uint8_t lich_off_hex = 0; UNUSED(lich_off_hex);
|
||||
for (int i=0; i<8; i++)
|
||||
lich_off_hex += lich_bits[(i*2)+1];
|
||||
#ifdef NXDN_LICH_OFFBITS
|
||||
if (lich_off_hex < 7) //allow up to 1 bit error
|
||||
if (lich_off_hex < LICH_ERR_THRESHOLD)
|
||||
{
|
||||
#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
|
||||
|
||||
@@ -127,9 +130,11 @@ 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 == 0x4A || lich == 0x48 || lich == 0x46)
|
||||
if (lich == 0x08 || 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)
|
||||
{
|
||||
@@ -137,8 +142,7 @@ 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;
|
||||
goto END;
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
}
|
||||
|
||||
voice = 0;
|
||||
@@ -156,150 +160,179 @@ 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) { //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;
|
||||
switch(lich)
|
||||
{
|
||||
|
||||
//DCR Voice
|
||||
case 0x46:
|
||||
voice = 3;
|
||||
sacch2 = 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 Data or End Frame
|
||||
case 0x48:
|
||||
pich_tch = 3;
|
||||
case 0x4A:
|
||||
sacch2 = 1;
|
||||
break;
|
||||
#ifdef JPNDCR_DECODE
|
||||
//DCR Voice
|
||||
case 0x46:
|
||||
voice = 3;
|
||||
sacch2 = 1;
|
||||
break;
|
||||
|
||||
//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;
|
||||
//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
|
||||
|
||||
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;
|
||||
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)
|
||||
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);
|
||||
|
||||
//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)
|
||||
{
|
||||
@@ -336,14 +369,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
#endif
|
||||
}
|
||||
|
||||
//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)
|
||||
nxdn_pn95_dibit_scrambler (state, dbuf, 182);
|
||||
|
||||
//seperate our dbuf (dibit_buffer) into individual bit array
|
||||
for (int i = 0; i < 182; i++)
|
||||
@@ -354,9 +380,7 @@ 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++)
|
||||
@@ -367,21 +391,15 @@ 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
|
||||
@@ -465,7 +483,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->M == 1) state->nxdn_cipher_type = 0x1;
|
||||
if (state->R != 0 && state->forced_alg_id == 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
|
||||
@@ -521,7 +539,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->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
if (state->forced_alg_id == 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)
|
||||
{
|
||||
@@ -547,32 +565,38 @@ 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);
|
||||
|
||||
if (udch2) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 0);
|
||||
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
|
||||
//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 (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
|
||||
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
||||
//NXDN Conventional and Type-C
|
||||
if (sacch) nxdn_sacch(opts, state, sacch_bits);
|
||||
if (cac) nxdn_cac(opts, state, cac_bits);
|
||||
|
||||
//Seperated UDCH user data from facch2 data
|
||||
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
|
||||
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
|
||||
|
||||
//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)
|
||||
//Duplicate facch check inside decoder. store and compare for facch message, depending on passed frame value
|
||||
if (facch == 3)
|
||||
{
|
||||
if (memcmp (facch_bits_a, facch_bits_b, 144) != 0)
|
||||
nxdn_deperm_facch(opts, state, facch_bits_b);
|
||||
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);
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
//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);
|
||||
|
||||
//EHR only (TODO: Make an EFR mode? never observed)
|
||||
if (voice)
|
||||
{
|
||||
//restore MBE file open here
|
||||
@@ -580,7 +604,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->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
if (state->forced_alg_id == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
//process voice frame
|
||||
nxdn_voice (opts, state, voice, dbuf);
|
||||
}
|
||||
@@ -628,7 +652,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;
|
||||
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->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[182])
|
||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf)
|
||||
{
|
||||
int i;
|
||||
int start, stop;
|
||||
|
||||
186
src/p25_12.c
186
src/p25_12.c
@@ -1,22 +1,24 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* p25_12.c
|
||||
* P25p1 1/2 Rate Simple Trellis Decoder
|
||||
* P25p1 1/2 Rate Viterbi Trellis Decoder (Improved)
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2023-10 DSD-FME Florida Man Edition
|
||||
* 2026-02 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
|
||||
@@ -25,19 +27,21 @@ 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)
|
||||
{
|
||||
@@ -50,37 +54,13 @@ 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, j;
|
||||
int irr_err = 0;
|
||||
int i, t;
|
||||
int total_err = 0; UNUSED(total_err);
|
||||
|
||||
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++)
|
||||
@@ -88,80 +68,86 @@ 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]];
|
||||
|
||||
//debug view points
|
||||
// fprintf (stderr, "\n P =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", point[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)
|
||||
|
||||
//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;
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
{
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
|
||||
}
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
//debug view tdibits/states
|
||||
// fprintf (stderr, "\n T =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", tdibits[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]));
|
||||
|
||||
//pack tdibits into return payload bytes
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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]];
|
||||
}
|
||||
|
||||
//pack tdibits into return payload bytes (use first 48 as original)
|
||||
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];
|
||||
|
||||
//trellis point/state err tally
|
||||
// if (irr_err != 0)
|
||||
// fprintf (stderr, " P_ERR = %d", irr_err);
|
||||
|
||||
return (irr_err);
|
||||
}
|
||||
|
||||
|
||||
//debug what was the cost metric on this
|
||||
// fprintf (stderr, " R12 Min Metric = %d; ", min_metric);
|
||||
|
||||
// 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;
|
||||
// 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 == 0xFFFF) return 0;
|
||||
|
||||
//Note: Base Frequency is calculated as (Base Frequency) x (0.000005 MHz) from the IDEN_UP message.
|
||||
|
||||
@@ -310,6 +310,18 @@ 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)
|
||||
@@ -494,16 +506,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]+40, LCW_bits+16, 56*sizeof(uint8_t)); //+40 offset to match the vPDU decoder
|
||||
memcpy(state->dmr_pdu_sf[0]+16, LCW_bits+16, 56*sizeof(uint8_t)); //contents of message in this block
|
||||
}
|
||||
|
||||
else if (lc_mfid == 0xA4 && lc_opcode == 0x2B)
|
||||
{
|
||||
fprintf (stderr, " MFIDA4 (Harris) GPS Block 2");
|
||||
memcpy(state->dmr_pdu_sf[0]+40+56, LCW_bits+16, 56*sizeof(uint8_t)); //+40 +56 to offset first block
|
||||
memcpy(state->dmr_pdu_sf[0]+16+56, LCW_bits+16, 56*sizeof(uint8_t)); //+16 +56 to offset first block
|
||||
uint16_t check = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[0][0], 16);
|
||||
if (check == 0x2AA4)
|
||||
nmea_harris(opts, state, state->dmr_pdu_sf[0], (uint32_t)state->lastsrc, 0);
|
||||
harris_lptt(opts, state, state->dmr_pdu_sf[0]+16, (uint32_t)state->lastsrc, 0, 1);
|
||||
else fprintf (stderr, " Missing GPS Block 1");
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
}
|
||||
@@ -526,6 +538,15 @@ 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,17 +1,138 @@
|
||||
#include <itpp/itcomm.h>
|
||||
// BCH decoder implementation from OP25 (Copyright 2010, KA1RBI, GPL license)
|
||||
#include "dsd.h"
|
||||
|
||||
#include "p25p1_check_nid.h"
|
||||
#include <vector>
|
||||
typedef std::vector<bool> bit_vector;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
using namespace itpp;
|
||||
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;
|
||||
|
||||
// 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.
|
||||
SynError = 0; CantDecode = 0;
|
||||
|
||||
itpp::BCH bch(63, 16, 11, "6 3 3 1 1 4 1 3 6 7 2 3 5 4 5 3", true);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience class to calculate the parity of the DUID values. Keeps a table with the expected outcomes
|
||||
@@ -24,13 +145,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;
|
||||
@@ -39,9 +160,8 @@ 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)
|
||||
@@ -49,31 +169,38 @@ int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity
|
||||
int result;
|
||||
|
||||
// Fill up with the given input
|
||||
bvec input(63);
|
||||
for(unsigned int i=0; i<63; i++) {
|
||||
input[i] = bch_code[i];
|
||||
bit_vector codeword(63);
|
||||
for (unsigned int i = 0; i < 63; i++) {
|
||||
codeword[i] = bch_code[62-i];
|
||||
}
|
||||
|
||||
// Decode it
|
||||
bvec decoded, cw_isvalid;
|
||||
bool ok = bch.decode(input, decoded, cw_isvalid);
|
||||
int errors = bchDec(codeword);
|
||||
bool ok = (errors >= 0);
|
||||
|
||||
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)decoded[i];
|
||||
nac |= (int)codeword[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)decoded[12])<<1) + ((int)decoded[13]);
|
||||
unsigned char new_duid_1 = (((int)decoded[14])<<1) + ((int)decoded[15]);
|
||||
unsigned char new_duid_0 = (((int)codeword[12]) << 1) + ((int)codeword[13]);
|
||||
unsigned char new_duid_1 = (((int)codeword[14]) << 1) + ((int)codeword[15]);
|
||||
new_duid[0] = new_duid_0 + '0';
|
||||
new_duid[1] = new_duid_1 + '0';
|
||||
new_duid[2] = 0; // Null terminate
|
||||
@@ -82,12 +209,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) {
|
||||
// Ignore, not sure what to do
|
||||
//fprintf (stderr, "Error in parity detected?");
|
||||
// Parity mismatch – optional handling
|
||||
// fprintf(stderr, "Error in parity detected?\n");
|
||||
}
|
||||
|
||||
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] = dmr_34 (tsbk_dibit, r34byte_b);
|
||||
ec[j] = viterbi_r34 (tsbk_dibit, r34byte_b);
|
||||
|
||||
//shuffle 34 rate data into array
|
||||
if (j != 0) //should never happen, but just in case
|
||||
|
||||
@@ -457,8 +457,18 @@ 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);
|
||||
|
||||
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
|
||||
//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 //default catch all (debug only)
|
||||
// {
|
||||
|
||||
@@ -489,11 +489,53 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
fprintf (stderr, " MFID 90 (Moto) Acknoledge Response: ");
|
||||
fprintf (stderr, " MFID 90 (Moto) Acknowledge 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
|
||||
@@ -505,22 +547,6 @@ 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");
|
||||
@@ -537,14 +563,6 @@ 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,10 +1628,79 @@ 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) //needs MAC message len update, may work same as explicit enc regroup?
|
||||
if (MAC[1+len_a] == 0x81 && MAC[2+len_a] == 0x90)
|
||||
{
|
||||
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
|
||||
@@ -1772,9 +1841,15 @@ 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, unknown if this is an opcode, or other MFID
|
||||
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 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;; //0x11 or 17 dec sounds reasonable, but cannot verify
|
||||
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;
|
||||
|
||||
fprintf (stderr, "\n MFID %02X (Harris); Len: %d; Opcode: %02X/%02X;", mfid, len, unk1, unk2);
|
||||
|
||||
//convert bytes to bits, may move this up top
|
||||
@@ -1791,8 +1866,7 @@ 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_gps (opts, state, slot, mac_bits); //fallback
|
||||
nmea_harris (opts, state, mac_bits+0, tsrc, slot); //new
|
||||
harris_lptt (opts, state, mac_bits+40, tsrc, slot, 2);
|
||||
|
||||
//debug - just dump payload
|
||||
// for (i = 0; i < 24; i++)
|
||||
|
||||
@@ -317,6 +317,18 @@ 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)
|
||||
{
|
||||
@@ -346,6 +358,18 @@ 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
|
||||
@@ -760,6 +784,18 @@ 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)
|
||||
{
|
||||
@@ -785,6 +821,19 @@ 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
|
||||
|
||||
@@ -314,3 +314,25 @@ rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword) {
|
||||
}
|
||||
return &rs_12_9_checksum;
|
||||
}
|
||||
|
||||
//This will run a RS 12,9 encode on 9 bytes of input
|
||||
//input is an array of 12 bytes or more
|
||||
void rs_12_9_encode(uint8_t * input)
|
||||
{
|
||||
rs_12_9_codeword_t cw;
|
||||
rs_12_9_checksum_t *cs;
|
||||
|
||||
//init cw
|
||||
memset(cw.data, 0, 12*sizeof(uint8_t));
|
||||
|
||||
//Copy 9 bytes of data input
|
||||
memcpy(cw.data, input, 9);
|
||||
|
||||
//Calculate the 3 parity / checksum bytes
|
||||
cs = rs_12_9_calc_checksum(&cw);
|
||||
|
||||
//Build the final 12-byte codeword
|
||||
input[9] = cs->bytes[0];
|
||||
input[10] = cs->bytes[1];
|
||||
input[11] = cs->bytes[2];
|
||||
}
|
||||
|
||||
123
src/rs-16-13.c
Normal file
123
src/rs-16-13.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* RS(16,13) encoder
|
||||
*
|
||||
* 13 data bytes + 3 parity bytes = 16-byte codeword
|
||||
*
|
||||
* Uses the same generator polynomial and GF(256) tables as the
|
||||
* DMR RS(12,9) implementation (DMR AI Spec / dmrshark / dsd):
|
||||
*
|
||||
* g(x) = x^3 + 0x40 x^2 + 0x38 x + 0x0e
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* Constants
|
||||
* ----------------------------------------------------------------------- */
|
||||
#define RS_DATASIZE 13
|
||||
#define RS_CHECKSUMSIZE 3
|
||||
#define RS_CODEWORDSIZE (RS_DATASIZE + RS_CHECKSUMSIZE) /* 16 */
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* Galois Field tables (GF(256) – same as DMR RS(12,9))
|
||||
* ----------------------------------------------------------------------- */
|
||||
static const uint8_t galois_exp_table[256] = {
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1D, 0x3A, 0x74, 0xE8, 0xCD, 0x87, 0x13, 0x26,
|
||||
0x4C, 0x98, 0x2D, 0x5A, 0xB4, 0x75, 0xEA, 0xC9, 0x8F, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0,
|
||||
0x9D, 0x27, 0x4E, 0x9C, 0x25, 0x4A, 0x94, 0x35, 0x6A, 0xD4, 0xB5, 0x77, 0xEE, 0xC1, 0x9F, 0x23,
|
||||
0x46, 0x8C, 0x05, 0x0A, 0x14, 0x28, 0x50, 0xA0, 0x5D, 0xBA, 0x69, 0xD2, 0xB9, 0x6F, 0xDE, 0xA1,
|
||||
0x5F, 0xBE, 0x61, 0xC2, 0x99, 0x2F, 0x5E, 0xBC, 0x65, 0xCA, 0x89, 0x0F, 0x1E, 0x3C, 0x78, 0xF0,
|
||||
0xFD, 0xE7, 0xD3, 0xBB, 0x6B, 0xD6, 0xB1, 0x7F, 0xFE, 0xE1, 0xDF, 0xA3, 0x5B, 0xB6, 0x71, 0xE2,
|
||||
0xD9, 0xAF, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0D, 0x1A, 0x34, 0x68, 0xD0, 0xBD, 0x67, 0xCE,
|
||||
0x81, 0x1F, 0x3E, 0x7C, 0xF8, 0xED, 0xC7, 0x93, 0x3B, 0x76, 0xEC, 0xC5, 0x97, 0x33, 0x66, 0xCC,
|
||||
0x85, 0x17, 0x2E, 0x5C, 0xB8, 0x6D, 0xDA, 0xA9, 0x4F, 0x9E, 0x21, 0x42, 0x84, 0x15, 0x2A, 0x54,
|
||||
0xA8, 0x4D, 0x9A, 0x29, 0x52, 0xA4, 0x55, 0xAA, 0x49, 0x92, 0x39, 0x72, 0xE4, 0xD5, 0xB7, 0x73,
|
||||
0xE6, 0xD1, 0xBF, 0x63, 0xC6, 0x91, 0x3F, 0x7E, 0xFC, 0xE5, 0xD7, 0xB3, 0x7B, 0xF6, 0xF1, 0xFF,
|
||||
0xE3, 0xDB, 0xAB, 0x4B, 0x96, 0x31, 0x62, 0xC4, 0x95, 0x37, 0x6E, 0xDC, 0xA5, 0x57, 0xAE, 0x41,
|
||||
0x82, 0x19, 0x32, 0x64, 0xC8, 0x8D, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xDD, 0xA7, 0x53, 0xA6,
|
||||
0x51, 0xA2, 0x59, 0xB2, 0x79, 0xF2, 0xF9, 0xEF, 0xC3, 0x9B, 0x2B, 0x56, 0xAC, 0x45, 0x8A, 0x09,
|
||||
0x12, 0x24, 0x48, 0x90, 0x3D, 0x7A, 0xF4, 0xF5, 0xF7, 0xF3, 0xFB, 0xEB, 0xCB, 0x8B, 0x0B, 0x16,
|
||||
0x2C, 0x58, 0xB0, 0x7D, 0xFA, 0xE9, 0xCF, 0x83, 0x1B, 0x36, 0x6C, 0xD8, 0xAD, 0x47, 0x8E, 0x01
|
||||
};
|
||||
|
||||
static const uint8_t galois_log_table[256] = {
|
||||
0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75,
|
||||
4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
|
||||
5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69,
|
||||
29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166,
|
||||
6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
|
||||
54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64,
|
||||
30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61,
|
||||
202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87,
|
||||
7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24,
|
||||
227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
|
||||
55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97,
|
||||
242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162,
|
||||
31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246,
|
||||
108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90,
|
||||
203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
|
||||
79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* Galois multiplication
|
||||
* ----------------------------------------------------------------------- */
|
||||
static uint8_t galois_mult(uint8_t a, uint8_t b)
|
||||
{
|
||||
if (a == 0 || b == 0)
|
||||
return 0;
|
||||
return galois_exp_table[(galois_log_table[a] + galois_log_table[b]) % 255];
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* RS(16,13) encoder – LFSR with the DMR generator polynomial
|
||||
*
|
||||
* genpoly coefficients (same as RS(12,9)):
|
||||
* { 0x40, 0x38, 0x0e, 0x01 }
|
||||
* ----------------------------------------------------------------------- */
|
||||
void rs16_13_encode(const uint8_t data_in[RS_DATASIZE], uint8_t codeword[RS_CODEWORDSIZE])
|
||||
{
|
||||
/* Generator polynomial coefficients from DMR AI Spec */
|
||||
static const uint8_t genpoly[] = { 0x40, 0x38, 0x0e, 0x01 };
|
||||
|
||||
uint8_t parity[3] = { 0, 0, 0 };
|
||||
uint8_t i;
|
||||
uint8_t feedback;
|
||||
|
||||
for (i = 0; i < RS_DATASIZE; i++) {
|
||||
feedback = data_in[i] ^ parity[0];
|
||||
|
||||
parity[0] = parity[1] ^ galois_mult(genpoly[2], feedback);
|
||||
parity[1] = parity[2] ^ galois_mult(genpoly[1], feedback);
|
||||
parity[2] = galois_mult(genpoly[0], feedback);
|
||||
}
|
||||
|
||||
/* Build systematic codeword: data || parity */
|
||||
memcpy(codeword, data_in, RS_DATASIZE);
|
||||
codeword[13] = parity[0];
|
||||
codeword[14] = parity[1];
|
||||
codeword[15] = parity[2];
|
||||
}
|
||||
|
||||
//This will run a RS 16,13 encode on 13 bytes of input
|
||||
//input is an array of 16 bytes or more
|
||||
void rs_16_13_encode(uint8_t * input)
|
||||
{
|
||||
|
||||
uint8_t output[16];
|
||||
memset(output, 0, sizeof(output));
|
||||
|
||||
//Calculate the 3 parity / checksum bytes
|
||||
rs16_13_encode(input, output);
|
||||
|
||||
//append the encoded parity to the input
|
||||
input[13] = output[13];
|
||||
input[14] = output[14];
|
||||
input[15] = output[15];
|
||||
}
|
||||
3675
src/rtl_sdr_fm.cpp
3675
src/rtl_sdr_fm.cpp
File diff suppressed because it is too large
Load Diff
331
src/rtl_sdr_tcp.c
Normal file
331
src/rtl_sdr_tcp.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* rtl_sdr_tcp.c
|
||||
* rtl_tcp client for the RTL-SDR input: samples arrive on a TCP socket, and tuner commands are sent back on it
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <time.h>
|
||||
|
||||
//MSG_NOSIGNAL is missing before macOS 11; SO_NOSIGPIPE in rtltcp_open keeps send from raising SIGPIPE there
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
//rtlsdr_read_async's default block size, in bytes
|
||||
#define RTLTCP_BUF_LENGTH (16 * 32 * 512)
|
||||
|
||||
//rtl_tcp state: socket, tuner type, and a mutex that keeps commands from different threads from interleaving
|
||||
//NOTE: Keeping these as static global is not great. They really should be in some kind of opaque handle
|
||||
// like rtl_tcp_dev_t or something. For applications that only use one device at a time, this is fine.
|
||||
// But if we ever move to supporting multiple simultaneous RTL tuners, this will need to change.
|
||||
static int rtltcp_sock = -1;
|
||||
static int rtltcp_tuner_type = RTLSDR_TUNER_UNKNOWN; //from the server's greeting
|
||||
static pthread_mutex_t rtltcp_m = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
//limit for connecting to each address, reading the greeting, and sending a command, so a stalled server can't hang dsd-fme
|
||||
static const int RTLTCP_TIMEOUT_MS = 5000;
|
||||
|
||||
static int64_t rtltcp_now_ms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
//Poll in short intervals so shutdown works even when the server stops responding.
|
||||
//A negative deadline permits an idle sample stream; setup and commands have fixed deadlines.
|
||||
static int rtltcp_wait(int sock, short events, int64_t deadline)
|
||||
{
|
||||
struct pollfd pfd = {sock, events, 0};
|
||||
while (!exitflag) {
|
||||
int timeout = 100;
|
||||
if (deadline >= 0) {
|
||||
//check if we have exceeded our wait time
|
||||
int64_t remaining = deadline - rtltcp_now_ms();
|
||||
if (remaining <= 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
//set the next poll timeout
|
||||
if (remaining < timeout) timeout = (int)remaining;
|
||||
}
|
||||
|
||||
//poll and handle the result
|
||||
int r = poll(&pfd, 1, timeout);
|
||||
if (r > 0) return 0; //recv/send/getsockopt will report EOF or socket errors
|
||||
if (r < 0 && errno != EINTR) return -1;
|
||||
}
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//rtl_tcp command bytes, each named after the librtlsdr function the server calls with the parameter
|
||||
enum
|
||||
{
|
||||
RTLTCP_SET_CENTER_FREQ = 0x01,
|
||||
RTLTCP_SET_SAMPLE_RATE = 0x02,
|
||||
RTLTCP_SET_TUNER_GAIN_MODE = 0x03,
|
||||
RTLTCP_SET_TUNER_GAIN = 0x04,
|
||||
RTLTCP_SET_FREQ_CORRECTION = 0x05,
|
||||
RTLTCP_SET_DIRECT_SAMPLING = 0x09,
|
||||
RTLTCP_SET_OFFSET_TUNING = 0x0a
|
||||
};
|
||||
|
||||
//send one rtl_tcp command: a command byte followed by a 32-bit big-endian parameter.
|
||||
//A partly sent command would misalign every later one, so a failed send ends the connection.
|
||||
static int rtltcp_command(uint8_t cmd, uint32_t param)
|
||||
{
|
||||
//encode command and parameter to send to rtl_tcp
|
||||
unsigned char buf[5] = {cmd, (unsigned char)(param >> 24), (unsigned char)(param >> 16), (unsigned char)(param >> 8), (unsigned char)param};
|
||||
|
||||
size_t sent = 0;
|
||||
pthread_mutex_lock(&rtltcp_m);
|
||||
|
||||
//non-blocking send with a timeout
|
||||
int64_t deadline = rtltcp_now_ms() + RTLTCP_TIMEOUT_MS;
|
||||
while (sent < 5) {
|
||||
if (rtltcp_wait(rtltcp_sock, POLLOUT, deadline) < 0) break;
|
||||
ssize_t n = send(rtltcp_sock, buf + sent, 5 - sent, MSG_NOSIGNAL);
|
||||
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
continue;
|
||||
}
|
||||
if (n <= 0) {
|
||||
break;
|
||||
}
|
||||
sent += n;
|
||||
}
|
||||
|
||||
//a failed or partial send ends the connection
|
||||
if (sent < 5) {
|
||||
shutdown(rtltcp_sock, SHUT_RDWR);
|
||||
}
|
||||
pthread_mutex_unlock(&rtltcp_m);
|
||||
return sent < 5 ? -1 : 0;
|
||||
}
|
||||
|
||||
//connect to an rtl_tcp server over IPv4 or IPv6, read its greeting, and store the socket in rtltcp_sock; prints the outcome and returns 0, or -1 on failure
|
||||
int rtltcp_open(const char *hostname, int portno)
|
||||
{
|
||||
struct addrinfo hints, *res, *ai;
|
||||
char portstr[16];
|
||||
unsigned char header[12];
|
||||
int sock = -1;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
snprintf(portstr, sizeof(portstr), "%d", portno);
|
||||
if (getaddrinfo(hostname, portstr, &hints, &res) != 0) {
|
||||
fprintf (stderr, "Failed to resolve rtl_tcp server %s.\n", hostname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//try each resolved address in turn until one connects
|
||||
for (ai = res; ai != NULL && !exitflag; ai = ai->ai_next) {
|
||||
//socket creation
|
||||
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if (sock < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//set nonblocking
|
||||
int flags = fcntl(sock, F_GETFL, 0);
|
||||
if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
close(sock);
|
||||
sock = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
//connect
|
||||
int r = connect(sock, ai->ai_addr, ai->ai_addrlen);
|
||||
|
||||
//connect returns immediately; wait for the attempt to finish, then SO_ERROR says whether it succeeded
|
||||
if (r < 0 && errno == EINPROGRESS && rtltcp_wait(sock, POLLOUT, rtltcp_now_ms() + RTLTCP_TIMEOUT_MS) == 0) {
|
||||
int error = 0;
|
||||
socklen_t len = sizeof(error);
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && error == 0) r = 0;
|
||||
}
|
||||
|
||||
//successful connection
|
||||
if (r == 0 && !exitflag) {
|
||||
break;
|
||||
}
|
||||
|
||||
//close socket and retry with different address
|
||||
close(sock);
|
||||
sock = -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
if (sock < 0) {
|
||||
fprintf (stderr, "Failed to connect to rtl_tcp server %s:%d.\n", hostname, portno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef SO_NOSIGPIPE
|
||||
//macOS and the BSDs: a send to a closed connection returns an error instead of raising SIGPIPE
|
||||
int one = 1;
|
||||
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
|
||||
#endif
|
||||
|
||||
size_t received = 0;
|
||||
|
||||
//non-blocking receive with a timeout
|
||||
int64_t deadline = rtltcp_now_ms() + RTLTCP_TIMEOUT_MS;
|
||||
while (received < sizeof(header)) {
|
||||
if (rtltcp_wait(sock, POLLIN, deadline) < 0) break;
|
||||
ssize_t n = recv(sock, header + received, sizeof(header) - received, 0);
|
||||
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) continue;
|
||||
if (n <= 0) break;
|
||||
received += n;
|
||||
}
|
||||
|
||||
//the greeting is "RTL0", the tuner type, and the number of tuner gain steps
|
||||
if (received != sizeof(header) || memcmp(header, "RTL0", 4) != 0) {
|
||||
fprintf (stderr, "%s:%d did not send an rtl_tcp greeting.\n", hostname, portno);
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//socket is valid and ready to use
|
||||
rtltcp_sock = sock;
|
||||
|
||||
//store tuner type for later use in rtltcp_get_tuner_gains
|
||||
rtltcp_tuner_type = ((uint32_t)header[4] << 24) | ((uint32_t)header[5] << 16) | ((uint32_t)header[6] << 8) | header[7];
|
||||
fprintf (stderr, "Using rtl_tcp server %s:%d. \n", hostname, portno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//rtlsdr_get_tuner_gains for the server's tuner: copies its gain steps, in tenths of a dB, into gains unless gains is NULL, and returns the number of steps
|
||||
static int rtltcp_get_tuner_gains(int *gains)
|
||||
{
|
||||
//rtl_tcp advertises only the gain count, not the values; librtlsdr's gain query requires a local device.
|
||||
//Unfortunately this means we have to copy librtlsdr's tables here to select the nearest valid gain given the remote device.
|
||||
//It would be better if rtl_tcp told us the actual list of supported gains, but it doesn't.
|
||||
static const int e4k[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, 240, 290, 340, 420 };
|
||||
static const int fc0012[] = { -99, -40, 71, 179, 192 };
|
||||
static const int fc0013[] = { -99, -73, -65, -63, -60, -58, -54, 58, 61, 63, 65, 67, 68, 70, 71, 179, 181, 182, 184, 186, 188, 191, 197 };
|
||||
static const int r82xx[] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, 207, 229, 254, 280, 297, 328, 338, 364, 372, 386, 402, 421, 434, 439, 445, 480, 496 };
|
||||
static const int none[] = { 0 };
|
||||
const int *table = none;
|
||||
int count = 1;
|
||||
|
||||
//different tuners have different possible gains
|
||||
switch (rtltcp_tuner_type) {
|
||||
case RTLSDR_TUNER_E4000: table = e4k; count = sizeof(e4k) / sizeof(int); break;
|
||||
case RTLSDR_TUNER_FC0012: table = fc0012; count = sizeof(fc0012) / sizeof(int); break;
|
||||
case RTLSDR_TUNER_FC0013: table = fc0013; count = sizeof(fc0013) / sizeof(int); break;
|
||||
case RTLSDR_TUNER_R820T:
|
||||
case RTLSDR_TUNER_R828D: table = r82xx; count = sizeof(r82xx) / sizeof(int); break;
|
||||
}
|
||||
|
||||
if (gains != NULL) {
|
||||
memcpy(gains, table, count * sizeof(int));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//close the rtl_tcp connection; returns close's result
|
||||
static int rtltcp_close(void)
|
||||
{
|
||||
int r = close(rtltcp_sock);
|
||||
rtltcp_sock = -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
//pass rtl_tcp samples to cb until the connection closes or exitflag is set, in the same block size that rtlsdr_read_async delivers by default
|
||||
static int rtltcp_read_async(rtlsdr_read_async_cb_t cb, void *ctx)
|
||||
{
|
||||
static unsigned char buf[RTLTCP_BUF_LENGTH];
|
||||
size_t received = 0;
|
||||
|
||||
//receive until exit is requested or the connection ends; no deadline, so an idle stream can wait indefinitely
|
||||
while (rtltcp_wait(rtltcp_sock, POLLIN, -1) == 0) {
|
||||
ssize_t n = recv(rtltcp_sock, buf + received, sizeof(buf) - received, 0);
|
||||
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) continue;
|
||||
if (n <= 0) break;
|
||||
received += n;
|
||||
if (received == sizeof(buf)) {
|
||||
cb(buf, sizeof(buf), ctx);
|
||||
received = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//we are finished (for a variety of reasons). ensure exitflag is set
|
||||
if (!exitflag) {
|
||||
fprintf (stderr, "rtl_tcp server closed the connection.\n");
|
||||
exitflag = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Each rtlsdr_or_tcp_* function takes the arguments of the librtlsdr function it is named after.
|
||||
//While an rtl_tcp server is connected, the call goes to the server; otherwise it goes to librtlsdr.
|
||||
int rtlsdr_or_tcp_close(rtlsdr_dev_t *dev)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_close() : rtlsdr_close(dev);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_read_async(cb, ctx) : rtlsdr_read_async(dev, cb, ctx, buf_num, buf_len);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_cancel_async(rtlsdr_dev_t *dev)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? shutdown(rtltcp_sock, SHUT_RDWR) : rtlsdr_cancel_async(dev);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_reset_buffer(rtlsdr_dev_t *dev)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? 0 : rtlsdr_reset_buffer(dev);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_get_tuner_gains(rtlsdr_dev_t *dev, int *gains)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_get_tuner_gains(gains) : rtlsdr_get_tuner_gains(dev, gains);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_CENTER_FREQ, freq) : rtlsdr_set_center_freq(dev, freq);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_SAMPLE_RATE, rate) : rtlsdr_set_sample_rate(dev, rate);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_set_direct_sampling(rtlsdr_dev_t *dev, int on)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_DIRECT_SAMPLING, on) : rtlsdr_set_direct_sampling(dev, on);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_set_offset_tuning(rtlsdr_dev_t *dev, int on)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_OFFSET_TUNING, on) : rtlsdr_set_offset_tuning(dev, on);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_TUNER_GAIN_MODE, manual) : rtlsdr_set_tuner_gain_mode(dev, manual);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_set_tuner_gain(rtlsdr_dev_t *dev, int gain)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_TUNER_GAIN, gain) : rtlsdr_set_tuner_gain(dev, gain);
|
||||
}
|
||||
|
||||
int rtlsdr_or_tcp_set_freq_correction(rtlsdr_dev_t *dev, int ppm)
|
||||
{
|
||||
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_FREQ_CORRECTION, ppm) : rtlsdr_set_freq_correction(dev, ppm);
|
||||
}
|
||||
4632
src/sjis_unicode.c
Normal file
4632
src/sjis_unicode.c
Normal file
File diff suppressed because it is too large
Load Diff
541
src/soft_viterbi_k5.c
Normal file
541
src/soft_viterbi_k5.c
Normal file
@@ -0,0 +1,541 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* 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
|
||||
//--------------------------------------------------------------------
|
||||
209
src/ysf.c
209
src/ysf.c
@@ -228,7 +228,12 @@ void ysf_dch_decode (dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint
|
||||
}
|
||||
|
||||
if (fn == ft) //last frame
|
||||
fprintf (stderr, " %s", state->event_history_s[0].Event_History_Items[0].text_message);
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -377,15 +382,14 @@ 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, k, err;
|
||||
uint8_t s0, s1;
|
||||
uint8_t trellis_buf[100];
|
||||
int i, j, err;
|
||||
uint8_t viterbi_bits[100];
|
||||
uint8_t temp[210];
|
||||
uint8_t m_data[100];
|
||||
uint8_t viterbi_bytes[100];
|
||||
uint8_t bits[210];
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (viterbi_bits, 0, sizeof(viterbi_bits));
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
memset (bits, 0, sizeof(bits));
|
||||
err = 0;
|
||||
|
||||
@@ -398,56 +402,27 @@ int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, u
|
||||
}
|
||||
}
|
||||
|
||||
k = 0;
|
||||
//convert dibits to bits
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
bits[k++] = (buf[i] >> 1) & 1;
|
||||
bits[k++] = (buf[i] >> 0) & 1;
|
||||
}
|
||||
uint32_t v_error =
|
||||
ysf_soft_decision_viterbi(buf, 200, 13, 8, viterbi_bits, viterbi_bytes);
|
||||
|
||||
//setup for the convolutional decoder
|
||||
for (i = 0; i < 200; i++)
|
||||
temp[i] = bits[i] << 1;
|
||||
//debug
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, " D2-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
|
||||
|
||||
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);
|
||||
uint16_t crc = crc16ysf(viterbi_bits, 96);
|
||||
if (crc != 0) err = -2; // crc failure
|
||||
|
||||
for (i = 0; i < 80; i++)
|
||||
trellis_buf[i] = trellis_buf[i] ^ pn95[i];
|
||||
viterbi_bits[i] = viterbi_bits[i] ^ pn95[i];
|
||||
|
||||
//reload after de-whitening
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
for (i = 0; i < 12; i++)
|
||||
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
|
||||
viterbi_bytes[i] = (uint8_t)ConvertBitIntoBytes(&viterbi_bits[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, trellis_buf);
|
||||
ysf_dch_decode2 (state, bn, bt, fn, ft, cm, viterbi_bits);
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
@@ -460,25 +435,24 @@ 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]", m_data[i]);
|
||||
fprintf (stderr, "[%02X]", viterbi_bytes[i]);
|
||||
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
//modified version of nxdn_deperm_facch1 -- this one for Full Rate, Type 1 CC, Headers and Terminators DCH (180 dibit version)
|
||||
//Soft Decision 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, k, err;
|
||||
uint8_t s0, s1;
|
||||
uint8_t trellis_buf[190];
|
||||
int i, j, err;
|
||||
uint8_t viterbi_bits[190];
|
||||
uint8_t temp[370];
|
||||
uint8_t m_data[100];
|
||||
uint8_t viterbi_bytes[100];
|
||||
uint8_t bits[370];
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (viterbi_bits, 0, sizeof(viterbi_bits));
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
memset (bits, 0, sizeof(bits));
|
||||
err = 0;
|
||||
|
||||
@@ -491,56 +465,27 @@ int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, ui
|
||||
}
|
||||
}
|
||||
|
||||
k = 0;
|
||||
//convert dibits to bits
|
||||
for (i = 0; i < 180; i++)
|
||||
{
|
||||
bits[k++] = (buf[i] >> 1) & 1;
|
||||
bits[k++] = (buf[i] >> 0) & 1;
|
||||
}
|
||||
uint32_t v_error =
|
||||
ysf_soft_decision_viterbi(buf, 360, 23, 8, viterbi_bits, viterbi_bytes);
|
||||
|
||||
//setup for the convolutional decoder
|
||||
for (i = 0; i < 360; i++)
|
||||
temp[i] = bits[i] << 1;
|
||||
//debug
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, " D1-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
|
||||
|
||||
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);
|
||||
uint16_t crc = crc16ysf(viterbi_bits, 176);
|
||||
if (crc != 0) err = -2; // crc failure
|
||||
|
||||
for (i = 0; i < 160; i++)
|
||||
trellis_buf[i] = trellis_buf[i] ^ pn95[i];
|
||||
viterbi_bits[i] = viterbi_bits[i] ^ pn95[i];
|
||||
|
||||
//reload after de-whitening
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
for (i = 0; i < 22; i++)
|
||||
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
|
||||
viterbi_bytes[i] = (uint8_t)ConvertBitIntoBytes(&viterbi_bits[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, trellis_buf);
|
||||
ysf_dch_decode (state, bn, bt, fn, ft, cm, viterbi_bits);
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
@@ -553,25 +498,24 @@ 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]", m_data[i]);
|
||||
fprintf (stderr, "[%02X]", viterbi_bytes[i]);
|
||||
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
//modified version of nxdn_deperm_facch1
|
||||
int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
|
||||
//FICH Soft Decision Viterbi
|
||||
int ysf_conv_fich (dsd_opts * opts, uint8_t input[], uint8_t dest[32])
|
||||
{
|
||||
int i, j, k, err;
|
||||
uint8_t s0, s1;
|
||||
uint8_t trellis_buf[100];
|
||||
int i, j, err;
|
||||
uint8_t viterbi_bits[100];
|
||||
uint8_t temp[210];
|
||||
uint8_t m_data[100];
|
||||
uint8_t viterbi_bytes[100];
|
||||
uint8_t bits[210];
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (viterbi_bits, 0, sizeof(viterbi_bits));
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
|
||||
memset (bits, 0, sizeof(bits));
|
||||
err = 0;
|
||||
|
||||
@@ -584,41 +528,12 @@ int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
|
||||
}
|
||||
}
|
||||
|
||||
k = 0;
|
||||
//convert dibits to bits
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
bits[k++] = (buf[i] >> 1) & 1;
|
||||
bits[k++] = (buf[i] >> 0) & 1;
|
||||
}
|
||||
uint32_t v_error =
|
||||
ysf_soft_decision_viterbi(buf, 200, 13, 8, viterbi_bits, viterbi_bytes);
|
||||
|
||||
//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;
|
||||
}
|
||||
//debug
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, " F-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
|
||||
|
||||
uint8_t fich_bits[12*4];
|
||||
uint8_t temp_b[24];
|
||||
@@ -631,31 +546,27 @@ int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
|
||||
g[i] = FALSE;
|
||||
|
||||
for (j = 0; j < 24; j++)
|
||||
temp_b[j] = (char) trellis_buf[(i*24)+j];
|
||||
temp_b[j] = (char) viterbi_bits[(i*24)+j];
|
||||
|
||||
g[i] = Golay_24_12_decode(temp_b);
|
||||
if(g[i] == FALSE) err = -1;
|
||||
|
||||
for (j = 0; j < 24; j++)
|
||||
trellis_buf[(i*24)+j] = (uint8_t) temp_b[j];
|
||||
viterbi_bits[(i*24)+j] = (uint8_t) temp_b[j];
|
||||
}
|
||||
|
||||
//load corrected bits
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
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];
|
||||
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];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -684,7 +595,7 @@ void pn95_lfsr() //test to see if this generates the correct bits now
|
||||
}
|
||||
|
||||
|
||||
void ysf_ehr (dsd_opts * opts, dsd_state * state, uint8_t dbuf[180], int start, int stop )
|
||||
void ysf_ehr (dsd_opts * opts, dsd_state * state, uint8_t * dbuf, int start, int stop )
|
||||
{
|
||||
int i;
|
||||
char ambe_fr[4][24];
|
||||
@@ -808,7 +719,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 (fichrawdibits, fich_decode);
|
||||
err = ysf_conv_fich (opts, fichrawdibits, fich_decode);
|
||||
|
||||
//if errors decoding fich, then just treat it like the last frame that came in
|
||||
if (err == 0)
|
||||
@@ -939,7 +850,7 @@ void processYSF(dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
// send VCH to ehr voice handler
|
||||
ysf_ehr (opts, state, vbuf, 0, 4);
|
||||
ysf_ehr (opts, state, vbuf, 0, 5);
|
||||
|
||||
//send DCH to decoder
|
||||
ysf_conv_dch (opts, state, bn, bt, fn, ft, cm, dbuf);
|
||||
|
||||
Reference in New Issue
Block a user