mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 09:17:20 -05:00
Compare commits
213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57629cefe4 | ||
|
|
fe3a135a92 | ||
|
|
8d33c2b3d1 | ||
|
|
593ffbbd66 | ||
|
|
592d57ad87 | ||
|
|
9a31678d9a | ||
|
|
7b3a257df8 | ||
|
|
55e792a925 | ||
|
|
755f77f96c | ||
|
|
1c17757a4e | ||
|
|
abe2e61cf2 | ||
|
|
c404825451 | ||
|
|
769168da5b | ||
|
|
14284c3cf1 | ||
|
|
51f971b68d | ||
|
|
02fdb7cd90 | ||
|
|
64541a1958 | ||
|
|
0a675d3f0f | ||
|
|
e79b6fd231 | ||
|
|
d356ed0dbc | ||
|
|
495eb71090 | ||
|
|
6bf96f1df5 | ||
|
|
3a2db53c3b | ||
|
|
e038016c30 | ||
|
|
7d6269efad | ||
|
|
152f0ea7f6 | ||
|
|
13443a25f3 | ||
|
|
528f7294d9 | ||
|
|
83e3fa1061 | ||
|
|
9b3a2d96e3 | ||
|
|
0829cdbdb0 | ||
|
|
9541aef382 | ||
|
|
4caf6bb153 | ||
|
|
dd77273218 | ||
|
|
3bd3edb36f | ||
|
|
6f1cf0a279 | ||
|
|
5d045b89e4 | ||
|
|
a2c4679632 | ||
|
|
986ed27d7d | ||
|
|
bae6a1545e | ||
|
|
5240f00441 | ||
|
|
f7511e0976 | ||
|
|
bc27344b04 | ||
|
|
db97eb82a1 | ||
|
|
3f1b8ff736 | ||
|
|
54b9017146 | ||
|
|
8ccaf2d261 | ||
|
|
209770175d | ||
|
|
602079e681 | ||
|
|
82267b9ec8 | ||
|
|
87bf997b6e | ||
|
|
0a43d3202d | ||
|
|
ee04cef6f2 | ||
|
|
b6fdfe48c1 | ||
|
|
7a4bd2833f | ||
|
|
432ad448ba | ||
|
|
61be710bb0 | ||
|
|
3be8aacf77 | ||
|
|
4feb21cf0a | ||
|
|
7d8162ab99 | ||
|
|
8e230a08b5 | ||
|
|
dfc4f6f304 | ||
|
|
5a3061f942 | ||
|
|
b40281fda3 | ||
|
|
966fbf8c6a | ||
|
|
ca65f1b209 | ||
|
|
a5d73f06d2 | ||
|
|
56b0e8440e | ||
|
|
b6a12b6ceb | ||
|
|
15a12acfd3 | ||
|
|
c534e0f1fe | ||
|
|
5e68580fa6 | ||
|
|
e6d83e7305 | ||
|
|
d56cf106b4 | ||
|
|
4279ccf292 | ||
|
|
846b202192 | ||
|
|
330ebe2bf3 | ||
|
|
79b4a5e720 | ||
|
|
8186b62a91 | ||
|
|
20f0c852aa | ||
|
|
9e4ae23c0b | ||
|
|
585f6f2fa5 | ||
|
|
a4fba3b19c | ||
|
|
a2180eb5d3 | ||
|
|
514161fefa | ||
|
|
bba059f35f | ||
|
|
5977f865e3 | ||
|
|
ade7c0477f | ||
|
|
a2fded8340 | ||
|
|
005e15c45f | ||
|
|
e9962a8923 | ||
|
|
c6611eae9c | ||
|
|
261193959c | ||
|
|
e7a71eabb6 | ||
|
|
afba84e584 | ||
|
|
96faff7711 | ||
|
|
94166b493c | ||
|
|
6a0b1b2dcf | ||
|
|
5c8e3d95a7 | ||
|
|
4c91588af6 | ||
|
|
35c05654a5 | ||
|
|
fbc9b5fab8 | ||
|
|
1126ab6578 | ||
|
|
0842613b24 | ||
|
|
bf4a4b574c | ||
|
|
380657679e | ||
|
|
0a214af29b | ||
|
|
b008122149 | ||
|
|
7b6feb9e4f | ||
|
|
e4861bf94c | ||
|
|
587a3f01a2 | ||
|
|
8d5c82356e | ||
|
|
df039ab1c1 | ||
|
|
f5e3c6eddd | ||
|
|
fc01712132 | ||
|
|
7ec0bf83b4 | ||
|
|
504476c5db | ||
|
|
f78dbd57bc | ||
|
|
d2b27fdf25 | ||
|
|
3aa20f82b9 | ||
|
|
402b2f0264 | ||
|
|
4dd2328e10 | ||
|
|
98bcdd203e | ||
|
|
741ed51d08 | ||
|
|
5d8e07228e | ||
|
|
4a43a48b2e | ||
|
|
0ef3b4ab4b | ||
|
|
00e0f00974 | ||
|
|
91181f9e93 | ||
|
|
5c42c50a40 | ||
|
|
36399386c5 | ||
|
|
8eee4cd699 | ||
|
|
1410e8bd81 | ||
|
|
915375fb1c | ||
|
|
52fff28dc4 | ||
|
|
bd035622b8 | ||
|
|
44163710f9 | ||
|
|
44b1ccdeae | ||
|
|
34113cb782 | ||
|
|
2a336a9275 | ||
|
|
5c08a9193c | ||
|
|
2cae7f9c18 | ||
|
|
e056c97d9d | ||
|
|
dc49e0d08b | ||
|
|
b639f6af28 | ||
|
|
6b45c4ba9b | ||
|
|
2062502a59 | ||
|
|
a490d9ead2 | ||
|
|
c73832ac2c | ||
|
|
fe1fa79f70 | ||
|
|
f2f29f2287 | ||
|
|
3181e58b19 | ||
|
|
6204aec2a5 | ||
|
|
267180ed0e | ||
|
|
8d9cbb3720 | ||
|
|
8af6d2e185 | ||
|
|
abeb92ff8a | ||
|
|
be47075611 | ||
|
|
1640b58ae4 | ||
|
|
7a34d531b1 | ||
|
|
0af814c72f | ||
|
|
1477190dea | ||
|
|
b0cd1881bb | ||
|
|
44ad75ec17 | ||
|
|
1ce487dc13 | ||
|
|
ed9a96fdc8 | ||
|
|
c677efda49 | ||
|
|
fea4f97b89 | ||
|
|
f2e3834e0a | ||
|
|
b8b62ef69a | ||
|
|
4a8a4a088a | ||
|
|
9308c5b251 | ||
|
|
cdfe9435d3 | ||
|
|
f6cd915f4d | ||
|
|
1dfd59801e | ||
|
|
c4513fab7a | ||
|
|
a3b8839897 | ||
|
|
9e009867ff | ||
|
|
e95e2218d9 | ||
|
|
a9742043b8 | ||
|
|
05eef039c9 | ||
|
|
818e952b7b | ||
|
|
38b962aacf | ||
|
|
0217202801 | ||
|
|
ef07b48540 | ||
|
|
db67a8b227 | ||
|
|
4e2c4577d5 | ||
|
|
e581c96560 | ||
|
|
f3505269d0 | ||
|
|
20ccdbbced | ||
|
|
fc2fb99502 | ||
|
|
143c560958 | ||
|
|
d122d7e935 | ||
|
|
d0f9bb4bad | ||
|
|
ac752029bd | ||
|
|
5b994f5129 | ||
|
|
ec7d7c24a5 | ||
|
|
1d49481f2d | ||
|
|
7ca8cb421c | ||
|
|
4eb44b3c67 | ||
|
|
e16e4b61fc | ||
|
|
fbd04fa869 | ||
|
|
7f9b852f36 | ||
|
|
f5a7ab2b77 | ||
|
|
ffe6098fb8 | ||
|
|
157ca8c043 | ||
|
|
b722e43a3a | ||
|
|
2ba9953d33 | ||
|
|
bb2199ee84 | ||
|
|
4efec4e4ef | ||
|
|
12cf9aa80f | ||
|
|
e5a42dbb11 | ||
|
|
67eaeadc03 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,3 +5,6 @@ build
|
|||||||
*.sh
|
*.sh
|
||||||
*.csv
|
*.csv
|
||||||
examples
|
examples
|
||||||
|
*.bin
|
||||||
|
*.ans
|
||||||
|
*.patch
|
||||||
@@ -1,11 +1,51 @@
|
|||||||
|
#Ubuntu 20.04 uses 3.16.3
|
||||||
|
#Ubuntu 22.04 uses 3.22.1
|
||||||
|
#Arch currently on 3.26.3
|
||||||
|
#Cygwin currently using 3.23.2 (or newer)
|
||||||
|
cmake_minimum_required(VERSION 3.10.2)
|
||||||
project(dsd-fme)
|
project(dsd-fme)
|
||||||
cmake_minimum_required(VERSION 2.8.11)
|
|
||||||
|
|
||||||
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
|
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
|
||||||
#Set curses to ncurses, and wide true for ascii
|
#Set curses to ncurses, and wide true for ascii
|
||||||
set(CURSES_NEED_NCURSES TRUE)
|
set(CURSES_NEED_NCURSES TRUE)
|
||||||
set(CURSES_NEED_WIDE TRUE)
|
set(CURSES_NEED_WIDE TRUE)
|
||||||
|
|
||||||
|
#use cmake option -DAERO=ON to enable Aero Builds (OSS support and specific tweaks)
|
||||||
|
option(AERO
|
||||||
|
"Build for Aero Environments (Cygwin)" OFF)
|
||||||
|
if (AERO)
|
||||||
|
add_definitions(-DAERO_BUILD)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DCOLORS=OFF to disable color output
|
||||||
|
option(COLORS
|
||||||
|
"Build with Colors Enabled" ON)
|
||||||
|
if (COLORS)
|
||||||
|
add_definitions(-DPRETTY_COLORS)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DPVC=ON to enable Provoice Conventional Frame Sync
|
||||||
|
option(PVC
|
||||||
|
"Build with Provoice Conventional Frame Sync Enabled" OFF)
|
||||||
|
if (PVC)
|
||||||
|
add_definitions(-DPVCONVENTIONAL)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
|
#use cmake option -DLZ=ON to enable LimaZulu Requested NXDN Tweaks
|
||||||
|
option(LZ
|
||||||
|
"Build with new LimaZulu Requested NXDN Tweaks Enabled" OFF)
|
||||||
|
if (LZ)
|
||||||
|
add_definitions(-DLIMAZULUTWEAKS)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DTONES to change default location of the tones.wav files (lwvmobile precompiled 'Aero' releases only)
|
||||||
|
option(TN
|
||||||
|
"Build with tone wav files in the dsd-fme folder and not in /usr/share/" OFF)
|
||||||
|
if (TN)
|
||||||
|
add_definitions(-DTONES)
|
||||||
|
endif ()
|
||||||
|
|
||||||
include(git_revision)
|
include(git_revision)
|
||||||
git_describe(GIT_TAG)
|
git_describe(GIT_TAG)
|
||||||
|
|
||||||
@@ -14,7 +54,8 @@ find_package(MBE REQUIRED)
|
|||||||
find_package(ITPP REQUIRED)
|
find_package(ITPP REQUIRED)
|
||||||
find_package(RTLSDR)
|
find_package(RTLSDR)
|
||||||
find_package(Curses REQUIRED)
|
find_package(Curses REQUIRED)
|
||||||
find_package(PULSEAUDIO 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_DIR})
|
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIR})
|
||||||
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${CURSES_LIBRARY})
|
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${CURSES_LIBRARY})
|
||||||
@@ -26,6 +67,12 @@ if(RTLSDR_FOUND)
|
|||||||
add_definitions(-DUSE_RTLSDR)
|
add_definitions(-DUSE_RTLSDR)
|
||||||
endif(RTLSDR_FOUND)
|
endif(RTLSDR_FOUND)
|
||||||
|
|
||||||
|
if(CODEC2_FOUND)
|
||||||
|
include_directories(SYSTEM ${CODEC2_INCLUDE_DIRS})
|
||||||
|
list(APPEND LIBS ${CODEC2_LIBRARIES})
|
||||||
|
add_definitions(-DUSE_CODEC2)
|
||||||
|
endif(CODEC2_FOUND)
|
||||||
|
|
||||||
FILE(GLOB SRCS src/*.c src/*.cpp)
|
FILE(GLOB SRCS src/*.c src/*.cpp)
|
||||||
FILE(GLOB HEADERS include/*.h include/*.hpp)
|
FILE(GLOB HEADERS include/*.h include/*.hpp)
|
||||||
|
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -2,13 +2,9 @@
|
|||||||
# Digital Speech Decoder - Florida Man Edition
|
# 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
|
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"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), 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, etc, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users, so please understand that this version may not compile or run correctly in other environments.
|
[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, 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 and others for the many hours of wav samples submitted 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. 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. Thank you everybody.
|
||||||
|
|
||||||
Click image below for video of current DMR/P25 trunking capabilities.
|
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=hGIijBbbUxg "DSD-FME Trunking Demo")
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -16,11 +12,7 @@ Click image below for video of current DMR/P25 trunking capabilities.
|
|||||||
|
|
||||||
## Information
|
## Information
|
||||||
|
|
||||||
See the example folder for information on cloning, installing, example usage, and trunking features.
|
See the [examples](https://github.com/lwvmobile/dsd-fme/tree/main/examples "examples") folder for information on [cloning and installing](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Install_Notes.md "cloning and installing"), [example usage](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Example_Usage.md "example usage"), and [trunking examples](https://github.com/lwvmobile/dsd-fme/blob/main/examples/trunking.sh "trunking examples").
|
||||||
|
|
||||||
## Notice
|
|
||||||
|
|
||||||
The pulseaudio branch will hearby be for legacy use as the promotion of this version to main branch will obsolete that branch. Any pre-compiled windows versions found in the release link will be considered legacy as well, and do not feature trunking support and have older handling for some other modes. Users are free to use those, but keep in mind the use case syntax may and will be different between versions. Furthermore, any wiki articles made for windows users may contain outdated information for building the pulseaudio branch, please adjust any information found in those accordingly.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Copyright (C) 2010 DSD Author
|
Copyright (C) 2010 DSD Author
|
||||||
@@ -37,5 +29,4 @@ GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|||||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
PERFORMANCE OF THIS SOFTWARE.
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
31
cmake/FindCODEC2.cmake
Normal file
31
cmake/FindCODEC2.cmake
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# - Try to find CODEC2
|
||||||
|
# Once done this will define
|
||||||
|
|
||||||
|
#Seems to work on Cygwin as well now -- more testing for may be required
|
||||||
|
# Not sure what causes the 'Package 'codec2', required by 'virtual:world', not found' message, but seems to work anyways
|
||||||
|
# -- Checking for module 'codec2'
|
||||||
|
# -- Package 'codec2', required by 'virtual:world', not found
|
||||||
|
# -- Found CODEC2: /usr/local/lib/libcodec2.dll.a
|
||||||
|
|
||||||
|
if (NOT CODEC2_FOUND)
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(CODEC2_PKG codec2)
|
||||||
|
set(CODEC2_DEFINITIONS ${PC_CODEC2_CFLAGS_OTHER})
|
||||||
|
|
||||||
|
find_path(CODEC2_INCLUDE_DIR
|
||||||
|
NAMES codec2/codec2.h codec2.h
|
||||||
|
HINTS ${CODEC2_PKG_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
find_library(CODEC2_LIBRARY
|
||||||
|
NAMES codec2/codec2 codec2
|
||||||
|
HINTS ${CODEC2_PKG_LIBRARY_DIRS})
|
||||||
|
|
||||||
|
set(CODEC2_LIBRARIES ${CODEC2_LIBRARY})
|
||||||
|
set(CODEC2_INCLUDE_DIRS ${CODEC2_INCLUDE_DIR})
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(CODEC2 DEFAULT_MSG CODEC2_LIBRARY CODEC2_INCLUDE_DIR)
|
||||||
|
mark_as_advanced(CODEC2_INCLUDE_DIR CODEC2_LIBRARY)
|
||||||
|
|
||||||
|
endif (NOT CODEC2_FOUND)
|
||||||
67
download-and-install-Arch.sh
Normal file
67
download-and-install-Arch.sh
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
clear
|
||||||
|
echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer For Arch Linux
|
||||||
|
echo This will install the required packages, clone, build, and install DSD-FME only.
|
||||||
|
echo This has been tested on Arch 2023.08.01 and Manjaro XFCE 22.1.3 Minimal.
|
||||||
|
echo
|
||||||
|
echo MBELib is considered a requirement on this build.
|
||||||
|
echo You must view this notice prior to continuing.
|
||||||
|
echo The Patent Notice can be found at the site below.
|
||||||
|
echo https://github.com/lwvmobile/mbelib#readme
|
||||||
|
echo Please confirm that you have viewed the patent notice by entering y below.
|
||||||
|
echo
|
||||||
|
echo y/N
|
||||||
|
read ANSWER
|
||||||
|
Y='y'
|
||||||
|
if [[ $Y == $ANSWER ]]; 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
|
||||||
|
|
||||||
|
echo Installing itpp 4.3.1 from Arch Strike --Tested on Arch 2023.08.01 and Manjaro XFCE 22.1.3 Minimal
|
||||||
|
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
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
#sudo cp /usr/local/lib/libmbe.a /usr/lib/libmbe.a
|
||||||
|
sudo cp /usr/local/lib/libmbe.so /usr/lib/libmbe.so #this is the only one we need to copy
|
||||||
|
#sudo cp /usr/local/lib/libmbe.so.1 /usr/lib/libmbe.so.1
|
||||||
|
#sudo cp /usr/local/lib/libmbe.so.1.3 /usr/lib/libmbe.so.1.3
|
||||||
|
cd ..
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
|
### For branch v2.1b - git clone --branch v2.1b https://github.com/lwvmobile/dsd-fme
|
||||||
|
cd dsd-fme
|
||||||
|
git checkout v2.1b
|
||||||
|
sudo cp tone8.wav /usr/share/
|
||||||
|
sudo cp tone24.wav /usr/share/
|
||||||
|
sudo cp tone48.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
|
sudo chmod 777 /usr/share/tone48.wav
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
sudo rm -R dsd-fme
|
||||||
|
|
||||||
|
echo Any issues, Please report to either
|
||||||
|
echo https://github.com/lwvmobile/dsd-fme/issues
|
||||||
|
echo https://forums.radioreference.com/threads/dsd-fme.438137/
|
||||||
|
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.
|
||||||
|
fi
|
||||||
@@ -13,7 +13,7 @@ Y='y'
|
|||||||
if [[ $Y == $ANSWER ]]; then
|
if [[ $Y == $ANSWER ]]; then
|
||||||
|
|
||||||
sudo apt update
|
sudo apt update
|
||||||
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 libncursesw5-dev
|
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev libcodec2-dev
|
||||||
|
|
||||||
echo ITPP Manual Build and Install has been temporarily removed from this script due to issues with older versions of Ubuntu.
|
echo ITPP Manual Build and Install has been temporarily removed from this script due to issues with older versions of Ubuntu.
|
||||||
echo Please check to see that libitpp-dev has successfully been downloaded and installed from the repo.
|
echo Please check to see that libitpp-dev has successfully been downloaded and installed from the repo.
|
||||||
|
|||||||
BIN
dsd-fme.png
BIN
dsd-fme.png
Binary file not shown.
|
Before Width: | Height: | Size: 706 KiB |
BIN
dsd-fme2.png
BIN
dsd-fme2.png
Binary file not shown.
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 125 KiB |
BIN
dsd-fme3.png
BIN
dsd-fme3.png
Binary file not shown.
|
Before Width: | Height: | Size: 692 KiB After Width: | Height: | Size: 251 KiB |
@@ -1,9 +1,9 @@
|
|||||||
### Example Usage and Notes!
|
### Example Usage and Notes!
|
||||||
|
|
||||||
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, and P25 (1 and 2) . To use other decoding methods which cannot be auto detected, please use the following command line switches. Make sure to route audio into and out of DSD-FME using pavucontrol and virtual sinks as needed.
|
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, and P25. To use other decoding methods which cannot be auto detected, please use the following command line switches. Make sure to route audio into and out of DSD-FME using pavucontrol and virtual sinks as needed.
|
||||||
|
|
||||||
```
|
```
|
||||||
-ft XDMA decoder class (P25 1 and 2, and DMR Stereo)
|
-ft XDMA decoder class (P25p1 and p2, YSF, and DMR Stereo)
|
||||||
-fs DMR Stereo, also may need to use -xr if inverted DMR
|
-fs DMR Stereo, also may need to use -xr if inverted DMR
|
||||||
-fa Legacy Auto (not recommended)
|
-fa Legacy Auto (not recommended)
|
||||||
-fi NXDN48
|
-fi NXDN48
|
||||||
@@ -12,10 +12,29 @@
|
|||||||
-fm dPMR, also may need to use -xd if inverted dPMR.
|
-fm dPMR, also may need to use -xd if inverted dPMR.
|
||||||
-fr DMR Mono, also may need to use -xr if inverted DMR.
|
-fr DMR Mono, also may need to use -xr if inverted DMR.
|
||||||
-f1 P25P1
|
-f1 P25P1
|
||||||
-f2 P25P2 (will need to specify wacn/sys/nac manually)
|
-f2 P25P2 (may need to specify wacn/sys/nac manually)
|
||||||
-fx X2-TDMA
|
-fx X2-TDMA
|
||||||
|
-fy YSF
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Conventional Frequency "Fast Scanner"
|
||||||
|
This feature is used to allow DSD-FME to use RIGCTL or RTL input and cycle through frequencies as fast as possible to attain a frame sync.
|
||||||
|
|
||||||
|
This works almost identically to how the trunkng methods work, but instead of locking onto a control channel and tuning based on its decoding, it rapidly tunes through all loaded frequencies in a CSV file and stops when it finds a frame sync. This method is meant for loading a bunch of conventional non-trunking frequencies that signal only when voice or short data bursts are present (DMR T2, Conventional P25 P1, Conventional NXDN, etc) and decode them until the frame sync stops and then resume its scan. This method uses SDR++ RIGCTL, or RTL internal handling to go through signal. -Y is the fast scanner switch.
|
||||||
|
|
||||||
|
To scan through a mixture of DMR and P25 Conventional, use:
|
||||||
|
`dsd-fme -i tcp -C channel_map.csv -G group.csv -Y -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
To scan through NXDN48 only conventional, use:
|
||||||
|
`dsd-fme -fi -i tcp -C channel_map.csv -Y -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
To scan through NXDN96 only conventional, use:
|
||||||
|
`dsd-fme -fn -i tcp -C channel_map.csv -Y -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
The Channel Map setup will be identical to any of the channel maps found for trunking systems, channel numbers do not matter and can be arbitrarily organized, and frequencies can be duplicated to be 'scanned' more frequently in larger CSV files. Scan speed by default is 1 second, and is most stable at that speed. This can be adjusted by using the `-t 2` option, for example, to specify 2 seconds of hang time per frequency. ~~If activity occurs on a frequency, an additional 2 seconds of 'hangtime' will occur on that frequency for any follow up voice activity.~~
|
||||||
|
|
||||||
|
Please Note: DMR Simplex may not behave well on fast scan, I have not been able to test it as such, but given the nature of the on-off timeslot signalling on DMR Simplex, missed frame syncs and skipping/resuming scan may occur.
|
||||||
|
|
||||||
## Trunking Examples
|
## Trunking Examples
|
||||||
Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation.
|
Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation.
|
||||||
|
|
||||||
@@ -45,26 +64,33 @@ Then you can open up your pavucontrol "Pulse Audio Volume Control" or "Volume Co
|
|||||||
Wav File Input Note: Due to 96000 rate audio requiring me to double the symbol rate and center, be sure to use the -s 96000 at the very end of the startup command. Also, some NXDN48/96 may have difficulties decoding properly with wav file input.
|
Wav File Input Note: Due to 96000 rate audio requiring me to double the symbol rate and center, be sure to use the -s 96000 at the very end of the startup command. Also, some NXDN48/96 may have difficulties decoding properly with wav file input.
|
||||||
|
|
||||||
## Example Usage - RTL2832 Dongle Input
|
## Example Usage - RTL2832 Dongle Input
|
||||||
|
Short Setup (all default values):
|
||||||
|
|
||||||
|
`dsd-fme -i rtl`
|
||||||
|
|
||||||
|
Detailed Setup:
|
||||||
|
|
||||||
`dsd-fme -ft -i rtl:0:154.9875M:26:-2:8:0:6020 `
|
`dsd-fme -ft -i rtl:0:154.9875M:26:-2:8:0:6020 `
|
||||||
|
|
||||||
```
|
```
|
||||||
RTL-SDR options:
|
RTL-SDR options:
|
||||||
WARNING! Old CLI Switch Handling has been depreciated in favor of rtl:<parms>
|
|
||||||
Usage: rtl:dev:freq:gain:ppm:bw:sq:udp
|
Usage: rtl:dev:freq:gain:ppm:bw:sq:udp
|
||||||
|
NOTE: all arguments after rtl are optional now for trunking, but user configuration is recommended
|
||||||
dev <num> RTL-SDR Device Index Number
|
dev <num> RTL-SDR Device Index Number
|
||||||
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
|
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
|
||||||
gain <num> RTL-SDR Device Gain (0-49) (default = 26)(0 = Hardware AGC, not recommended)
|
gain <num> RTL-SDR Device Gain (0-49)(default = 0; Hardware AGC recommended)
|
||||||
ppm <num> RTL-SDR PPM Error (default = 0)
|
ppm <num> RTL-SDR PPM Error (default = 0)
|
||||||
bw <num> RTL-SDR VFO Bandwidth kHz (default = 12)(6, 8, 12, 24)
|
bw <num> RTL-SDR Bandwidth kHz (default = 12)(8, 12, 16, 24)
|
||||||
sq <num> RTL-SDR Squelch Level (0 - Open, 25 - Little, 50 - Higher)
|
sq <num> RTL-SDR Squelch Level vs RMS Value (Optional)
|
||||||
udp <num> RTL-SDR UDP Remote Port (default = 6020)
|
udp <num> RTL-SDR Legacy UDP Remote Port (Optional -- External Use Only)
|
||||||
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:12:0:6021
|
Example: dsd-fme-zdev -fs -i rtl -C cap_plus_channel.csv -T
|
||||||
|
Example: dsd-fme-zdev -fp -i rtl:0:851.375M:22:-2:24:0:6021
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Usage - Extra Payload/PDU Logging
|
## Example Usage - Extra Payload/PDU Logging
|
||||||
|
|
||||||
`dsd-fme -Z -N 2>> log.ans`
|
`dsd-fme -Z -N 2> log.ans`
|
||||||
|
|
||||||
and in a second terminal tab, same folder, run
|
and in a second terminal tab, same folder, run
|
||||||
|
|
||||||
@@ -74,7 +100,7 @@ and in a second terminal tab, same folder, run
|
|||||||
|
|
||||||
EDACS Trunking (w/ channel map import)
|
EDACS Trunking (w/ channel map import)
|
||||||
|
|
||||||
--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (Tested and Working on EDACS/EDACS-EA with Provoice only, no analog voice monitoring)
|
--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (Tested and Working on EDACS/EDACS-EA with Provoice, Analog Voice Monitoring and Per Call is Experimental)
|
||||||
|
|
||||||
`dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans`
|
`dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
@@ -94,11 +120,11 @@ EDACS Trunking (w/ channel map import)
|
|||||||
|
|
||||||
`dsd-fme -i tcp -T -U 4532 -N 2> log.ans`
|
`dsd-fme -i tcp -T -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
--P25 Trunking (CQPSK) with P1 Control Channel (Should switch symbol rate and center on Phase 2 audio channels)
|
--P25 Trunking (QPSK) with P1 Control Channel (Should switch symbol rate and center on Phase 2 audio channels)
|
||||||
|
|
||||||
`dsd-fme -i tcp -T -U 4532 -N -mq 2> log.ans`
|
`dsd-fme -i tcp -T -U 4532 -N -mq 2> log.ans`
|
||||||
|
|
||||||
--P25 Trunking Phase 2 TDMA Control Channel systems with CQPSK (non Phase 1 systems)
|
--P25 Trunking Phase 2 TDMA Control Channel systems with QPSK (non Phase 1 systems)
|
||||||
|
|
||||||
`dsd-fme -i tcp -T -U 4532 -N -f2 -m2 2> log.ans`
|
`dsd-fme -i tcp -T -U 4532 -N -f2 -m2 2> log.ans`
|
||||||
|
|
||||||
@@ -106,9 +132,9 @@ Trunking Note1: All samples above can also be run with the RTL input method and
|
|||||||
|
|
||||||
`dsd-fme -fp -i rtl:0:851.8M:22:-2:24:0:6020 -T -C channel_map.csv -G group.csv -N 2> log.ans`
|
`dsd-fme -fp -i rtl:0:851.8M:22:-2:24:0:6020 -T -C channel_map.csv -G group.csv -N 2> log.ans`
|
||||||
|
|
||||||
Trunking Note2: CQPSK Phase 1 and Phase 2 Systems are subceptible to LSM distortion issues, but seem to do okay, but require really good signal. Some CRC issues still occur with Phase 2 TDMA LCCH Mac Signal that can affect reliability, I believe this issue is ultimately caused by the PSK demodulation inside of FME. I also don't believe this will work on 8-level PSK, but I cannot determine that at the moment. Update: I have improved the LCCH Mac Signal decoding my increasing the QPSK decision point buffers to their maximum values.
|
Trunking Note2: QPSK Phase 1 and Phase 2 Systems may be subceptible to LSM distortion issues, but seem to do okay, but require really good signal. Some CRC issues still occur with Phase 2 TDMA LCCH Mac Signal that can affect reliability, I believe this issue is ultimately caused by the PSK demodulation inside of FME. I also don't believe this will work on 8-level PSK, but I cannot determine that at the moment. Update: I have improved the LCCH Mac Signal decoding my increasing the QPSK decision point buffers to their maximum values.
|
||||||
|
|
||||||
Trunking Note3: DMR Trunking has been coded, and some testing and tweaks have been carried out. Cap+, Con+, and TIII systems seem to do well with trunking now. Placing the frequency for the control channel at channel map 0 in your channel_map.csv file is not required now if using RIGCTL or the RTL Input, both can poll the VFO for the current frequency if it believes its on a control channel, but using channel 0 as the control channel will hardset that frequency to the control channel. If you need to map out your channels for TIII, you can observe the console output and look for channel numbers. For conveniece I have included the DSDPlus channel numbering (as best as I can figure it) into the console print so it will make it easier for users from DSDPlus to map frequencies into the channel_map.csv file. Make sure your channel numbers are the Cd (channel decimal) values from the log, and not the C+ (dsdplus) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area.
|
Trunking Note3: DMR Trunking has been coded, and some testing and tweaks have been carried out. Cap+, Con+, and TIII systems seem to do well with trunking now. Placing the frequency for the control channel at channel map 0 in your channel_map.csv file is not required now if using RIGCTL or the RTL Input, both can poll the VFO for the current frequency if it believes its on a control channel, but setting a fake channel number (i.e. 999) first with the CC frequency will result in finding the CC faster on startup if desired. If you need to map out your channels for TIII, you can observe the console output and look for channel numbers. For conveniece I have included the DSDPlus channel numbering (as best as I can figure it, but they seem consistent) into the console print so it will make it easier for users from DSDPlus to map frequencies into the channel_map.csv file. Make sure your channel numbers are the Cd (channel decimal) values from the log, and not the C+ (dsdplus) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area.
|
||||||
|
|
||||||
```
|
```
|
||||||
Talkgroup Voice Channel Grant (TV_GRANT) - Logical
|
Talkgroup Voice Channel Grant (TV_GRANT) - Logical
|
||||||
@@ -117,14 +143,18 @@ Trunking Note3: DMR Trunking has been coded, and some testing and tweaks have be
|
|||||||
|
|
||||||
Use channel 54 in your import file, which would correspond to dsdplus channels 109 and 110 (109 = TS0 / 110 = TS1).
|
Use channel 54 in your import file, which would correspond to dsdplus channels 109 and 110 (109 = TS0 / 110 = TS1).
|
||||||
|
|
||||||
For Connect Plus, enumerate your list from 1 to the last channel and add the frequency. For Capacity Plus, Rest Channels 1 and 2 will share the same frequency, 3 and 4 will share, 5 and 6 will share, and 7-8 will share, as Capacity Plus counts each RF frequency as two seperate channels, one for each time slot. Capacity Plus Quirk: DSD-FME makes its best effort to follow the rest channel in the event that the sync is lost for longer than the hangtime, but occassionally, DSD-FME will lose the rest channel and will have to hunt through all frequencies to find it again.
|
For Connect Plus, enumerate your list from 1 to the last channel and add the frequency. For Capacity Plus, LSN 1 and 2 will share the same frequency, 3 and 4 will share, 5 and 6 will share, and 7-8 will share, as Capacity Plus counts each 'channel' as two seperate channels (LSN), one for each time slot. Capacity Plus Quirk: DSD-FME makes its best effort to follow the rest channel in the event that the sync is lost for longer than the hangtime, but occassionally, DSD-FME may lose the rest channel and will have to hunt through all frequencies to find it again.
|
||||||
|
|
||||||
Currently uncoded/unknown DMR trunking systems include Hytera XPT.
|
Trunking Note4: NXDN Trunking v1 will require a channel map. Please see the example folder for an appropriate channel map. NOTICE: NXDN trunking has been fixed on the RTL (rtl_fm) input method and works correctly now using a soft squelch value to determine when to look for frame sync. Tests show this works very well, but squelch adjustments may be required to differentiate the noise floor from the signal.
|
||||||
|
|
||||||
Trunking Note4: NXDN Trunking may also require a channel map file, depending on the system. If it uses a custom range (above 800), then channels will need to be mapped. If channels do not require mapping (have channels below 800) but you are not tuning properly, then please use a channel_map and please report the issue in the issues along with the channels you are tuning and the actual rf frequency so corrections can be made.
|
NXDN Trunking Update: NXDN DFA (Direct Frequency Assignment) has been coded from the v2 documents, so using a channel map may not be required, as long as the DFA is configured with standard values, and not 'system definable' values. This will only work on trunking systems that use DFA (newer systems) in accordance to the v2 CAI document NXDN TS 1-A Version 2.0 September 2016. If the system uses 'system definable' values, then make sure to put the channels in a channel map csv file as as the 16-bit OFN values for proper tuning.
|
||||||
|
|
||||||
|
NXDN Type-D/IDAS Update: Type-D or IDAS decoding/trunking has been coded now and tested on a small two channel system. Set up a channel map much like Type-C trunking. If preferred, insert channel 31 at the top of your channel map for the 'home repeater' or go to channel of choice during a TX_REL or DISC message. Extensive testing of Type-D has not been conducted. Some cavaets may currently include iffy per call wav file saving and potentially shifting SRC and TGT ID values due to how SCCH messages are handled and how Busy Repeater Messages are handled. Private calls will need to be enabled by enabling 'data calls', if desired. During testing, only data calls came on private calls, so this was set to disabled by default, but can be enabled with data calls enabled. Also, note that some home repeater channels may or may not carry calls from all systems.
|
||||||
|
|
||||||
Channel Map and Group CSV Note: Leave the top line of the channel_map.csv and group.csv as the label, do not delete the line, if no line is there, dsd_import skips the first line so it will not import the first channel or first group in those files if there is something there that isn't a label.
|
Channel Map and Group CSV Note: Leave the top line of the channel_map.csv and group.csv as the label, do not delete the line, if no line is there, dsd_import skips the first line so it will not import the first channel or first group in those files if there is something there that isn't a label.
|
||||||
|
|
||||||
|
Hytera XPT: Code has been added for XPT system slco/flco/csbk decoding and trunking. The setup will be similar to Capacity Plus trunking in the csv file, listing each LSN channel to a frequency (see examples/hytera_xpt_chan.csv). Currently, this set up is working with smaller XPT systems that I have been able to test with.
|
||||||
|
|
||||||
## NCurses Keyboard Shortcuts ##
|
## NCurses Keyboard Shortcuts ##
|
||||||
|
|
||||||
Some Keyboard Shortcuts have been implemented for testing to see how users like them. Just hope nobody pushes a key on accident and doesn't know which one it was or what it does. The current list of keyboard shortcuts include:
|
Some Keyboard Shortcuts have been implemented for testing to see how users like them. Just hope nobody pushes a key on accident and doesn't know which one it was or what it does. The current list of keyboard shortcuts include:
|
||||||
@@ -136,20 +166,60 @@ c - toggle compact mode
|
|||||||
h - toggle call history
|
h - toggle call history
|
||||||
z - toggle console payloads
|
z - toggle console payloads
|
||||||
a - toggle call alert beep
|
a - toggle call alert beep
|
||||||
4 - force dmr privacy key assertion over fid and svc bits
|
4 - force privacy/scrambler key assertion over enc identifiers (dmr and nxdn)
|
||||||
|
6 - force rc4 key assertion over missing pi header/le enc identifiers (dmr)
|
||||||
i - toggle signal inversion on types that can't auto detect (dmr, dpmr)
|
i - toggle signal inversion on types that can't auto detect (dmr, dpmr)
|
||||||
m - toggle c4fm/cqpsk 10/4 (everything but phase 2 signal)
|
m - toggle c4fm/qpsk 10/4 (everything but phase 2 signal)
|
||||||
M - toggle c4fm/cqpsk 8/3 (phase 2 tdma control channel)
|
M - toggle c4fm/qpsk 8/3 (phase 2 tdma control channel)
|
||||||
t - toggle trunking (needs either rtl input, or rigctl setup at CLI)
|
|
||||||
R - start capturing symbol capture bin (date/time name file)
|
R - start capturing symbol capture bin (date/time name file)
|
||||||
r - stop capturing symbol capture bin
|
r - stop capturing symbol capture bin
|
||||||
spacebar - replay last symbol capture bin (captures must be stopped first)
|
spacebar - replay last symbol capture bin (captures must be stopped first)
|
||||||
s - stop playing symbol capture bin or wav input file
|
s - stop playing symbol capture bin or wav input file
|
||||||
P - start per call decoded wav files
|
P - start per call decoded wav files (Capital P)
|
||||||
p - stop per call decoded wav files
|
p - stop per call decoded wav files (Lower p)
|
||||||
1 - Lockout Tuning/Playback of TG in Slot 1 or Conventional (Current Session Only)
|
t - toggle trunking (needs either rtl input, or rigctl connection)
|
||||||
2 - Lockout Tuning/Playback of TG in Slot 2 (Current Session Only)
|
y - toggle scanner (needs either rtl input, or rigctl connection)
|
||||||
|
1 - Lockout Tuning/Playback of TG in Slot 1 or Conventional -- Current Session Only if no group.csv file specified
|
||||||
|
2 - Lockout Tuning/Playback of TG in Slot 2 -- Current Session Only if no group.csv file specified
|
||||||
0 - Toggle Audio Smoothing - May produce crackling if enabled on RTL/TCP or wav/bin files
|
0 - Toggle Audio Smoothing - May produce crackling if enabled on RTL/TCP or wav/bin files
|
||||||
w - Toggle Trunking/Playback White List (Allow A Groups Only) / Black List (Block B or DE groups only) Mode
|
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)
|
||||||
|
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)
|
||||||
|
8 - Connect to SDR++ TCP Audio Sink with Defaults/Retry Sink Connection on Disconnect
|
||||||
|
9 - Connect to SDR++ RIGCTL Server with Defaults/Retry RIGCTL Connection
|
||||||
|
D - Reset DMR Site Parms/Call Strings, etc.
|
||||||
|
Z - Simulate NoCarrier/No VC/CC sync (capital Z)
|
||||||
|
```
|
||||||
|
|
||||||
```
|
## Sending Audio to a Icecast 2 Server via FFmpeg (Windows)
|
||||||
|
|
||||||
|
Make sure to enable "Stereo Mix" from Volume Control Panel and then disable Windows Sounds.
|
||||||
|
|
||||||
|
`ffmpeg -f dshow -i audio="Stereo Mix (Realtek High Definition Audio)" -c:a aac -b:a 64k -content_type 'audio/aac' -vn -f adts icecast://source:password@192.168.5.251:8844/DSDFME`
|
||||||
|
|
||||||
|
## Sending Audio to a Icecast 2 Server via FFmpeg (Linux w/ Pulse Audio)
|
||||||
|
|
||||||
|
`pactl list short sources`
|
||||||
|
|
||||||
|
Choose Desired Device to Cast:
|
||||||
|
|
||||||
|
```
|
||||||
|
0 auto_null.monitor module-null-sink.c s16le 2ch 48000Hz IDLE
|
||||||
|
1 alsa_output.pci-0000_0d_00.3.analog-stereo.monitor module-alsa-card.c s16le 2ch 44100Hz RUNNING
|
||||||
|
2 alsa_input.pci-0000_0d_00.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz RUNNING
|
||||||
|
3 virtual_sink.monitor module-null-sink.c s16le 2ch 44100Hz RUNNING
|
||||||
|
4 virtual_sink2.monitor module-null-sink.c s16le 2ch 44100Hz RUNNING
|
||||||
|
7 alsa_output.pci-0000_0b_00.1.hdmi-stereo-extra1.monitor module-alsa-card.c s16le 2ch 44100Hz RUNNING
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
francis@12cores:~$ pactl list short sources
|
||||||
|
0 auto_null.monitor module-null-sink.c s16le 2ch 48000Hz IDLE
|
||||||
|
francis@12coresx:~$ ffmpeg -f pulse -i 0 -c:a libmp3lame -ab 64k -f mp3 icecast://source:password@XXXXXXXXX:8000
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
### Ubuntu 22.04/20.04/LM20/Debian Bullseye or Newer:
|
### Ubuntu 22.04/20.04/LM20/Debian Bullseye or Newer:
|
||||||
|
|
||||||
Using the included download-and-install.sh should make for a simple and painless clone, build, and install on newer Debian/Ubuntu/Mint/Pi systems. Simply acquire or copy the script, and run it. Update: Ubuntu 22.04 and RPi Bullseye 64-bit has been tested working with the installer script and functions appropriately.
|
Using the included download-and-install.sh should make for a simple and painless clone, build, and install on newer Debian/Ubuntu/Mint/Pi systems. Simply acquire or copy the script, and run it. Update: Ubuntu 23.04 and RPi Bullseye 64-bit has been tested working with the installer script and functions appropriately.
|
||||||
|
|
||||||
If you need all dependencies build and installed first (only on Debian/Ubuntu/Mint/Pi), run:
|
If you need all dependencies build and installed first (only on Debian/Ubuntu/Mint/Pi), run:
|
||||||
|
|
||||||
@@ -12,7 +12,17 @@ chmod +x download-and-install.sh
|
|||||||
./download-and-install.sh
|
./download-and-install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have dependencies already installed (i.e. need a fresh clean install on a system with DSD-FME already or using system other than Debian/Ubuntu, etc), please run this instead:
|
### Arch Based Distros:
|
||||||
|
|
||||||
|
```
|
||||||
|
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/v2.1b/download-and-install-Arch.sh
|
||||||
|
chmod +x download-and-install-Arch.sh
|
||||||
|
./download-and-install-Arch.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other:
|
||||||
|
|
||||||
|
If you have dependencies already installed, please run this instead:
|
||||||
|
|
||||||
```
|
```
|
||||||
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install-nodeps.sh
|
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install-nodeps.sh
|
||||||
@@ -20,23 +30,39 @@ chmod +x download-and-install-nodeps.sh
|
|||||||
./download-and-install-nodeps.sh
|
./download-and-install-nodeps.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ubuntu 18.04/LM19/Buster Note:
|
|
||||||
The above install.sh should now function on older system types. You can elect to manually follow the steps down below. Do NOT Manually build and install ITPP 4.3.1 on older systems, it is currently not wanting to build on Ubuntu 18.04 and Linux Mint 19. Install it from the repository instead.
|
|
||||||
|
|
||||||
## Manual Install
|
## Manual Install
|
||||||
|
|
||||||
First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different.
|
First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different.
|
||||||
|
|
||||||
|
Debian/Mint/Ubuntu/Pi
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo apt update
|
sudo apt update
|
||||||
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 libncursesw5-dev
|
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev libcodec2-dev
|
||||||
```
|
```
|
||||||
## Headless
|
|
||||||
|
Fedora 36/37 -- from https://github.com/lwvmobile/dsd-fme/issues/99
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo dnf update
|
||||||
|
sudo dnf install libsndfile-devel fftw-devel lapack-devel rtl-sdr-devel pulseaudio-libs-devel libusb-devel cmake git ncurses ncurses-devel gcc wget pavucontrol gcc-c++ codec2-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
Arch -- https://github.com/lwvmobile/dsd-fme/issues/153 and https://github.com/lwvmobile/dsd-fme/issues/153
|
||||||
|
|
||||||
|
```
|
||||||
|
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
|
||||||
|
|
||||||
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.
|
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
|
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!!
|
### Build and Install ITPP - ONLY IF NOT IN REPO!!
|
||||||
@@ -71,6 +97,9 @@ cd ..
|
|||||||
cd ..
|
cd ..
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: Some environment/distro users (Cygwin, Arch, and others) may need to manually copy any libmbe files found in the build folder to the /usr/lib folder on root.
|
||||||
|
`sudo cp libmbe* /usr/lib`
|
||||||
|
|
||||||
Finish by running these steps to clone and build DSD-FME.
|
Finish by running these steps to clone and build DSD-FME.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -90,13 +119,45 @@ sudo make install
|
|||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
|
|
||||||
```
|
```
|
||||||
Optional 'Virtual Sinks' for routing audio from SDR++ or GQRX, Media Players, etc. into DSD-FME
|
|
||||||
|
|
||||||
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired.
|
### Windows Cygwin Builds
|
||||||
|
|
||||||
|
If using RTL input support, you must install libusb-win32 before proceeding with Cygwin installation. You will also need to build and compile a version of librtlsdr, but this can be tricky, and personally, I had to resort to using a much older version of the source code to get it to build at all with the rtl support in the precompiled 'Aero' builds.
|
||||||
|
|
||||||
|
Then install all dependencies available in the repo prior to building, either directly selecting them during setup with the setup-x86.exe or setup-x64.exe or by issuing a command similar to this:
|
||||||
|
|
||||||
```
|
```
|
||||||
pacmd load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
setup-x64.exe --packages libpulse-devel,libpulse-mainloop-glib0,libpulse-simple0,libpulse0,pulseaudio,pulseaudio-debuginfo,pulseaudio-equalizer,pulseaudio-module-x11,pulseaudio-module-zeroconf,pulseaudio-utils,sox-fmt-pulseaudio,xfce-pulseaudio-plugin,libusb0,libusb1.0,libusb1.0-debuginfo,libusb1.0-devel,libncurses++w10,libncurses-devel,libncursesw10,ncurses,cmake,gcc-core,gcc-debuginfo,gcc-objc,git,make,socat,sox,sox-fmt-ao,unzip,wget,gcc-g++,libsndfile-devel
|
||||||
pacmd load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
|
```
|
||||||
|
|
||||||
|
Then manually install ITPP and MBElib, see above install notes. Be sure to run `cp libmbe* /usr/lib` after compiling MBElib.
|
||||||
|
|
||||||
|
Then you can build and install using
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
|
cd dsd-fme
|
||||||
|
cp tone8.wav /usr/share/
|
||||||
|
cp tone24.wav /usr/share/
|
||||||
|
cp tone48.wav /usr/share/
|
||||||
|
chmod 777 /usr/share/tone8.wav
|
||||||
|
chmod 777 /usr/share/tone24.wav
|
||||||
|
chmod 777 /usr/share/tone48.wav
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
# -DAERO=ON is optional, but recommended if you want the older OSS support as well
|
||||||
|
cmake -DAERO=ON ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Virtual Sinks
|
||||||
|
|
||||||
|
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired. Note: This setup is completely optional if using TCP Network Sink Audio and/or RTL Input.
|
||||||
|
|
||||||
|
```
|
||||||
|
pactl load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
||||||
|
pactl load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
|
||||||
```
|
```
|
||||||
|
|
||||||
Already have this branch, and just want to pull the latest build? You can run the rebuild.sh file in the dsd-fme folder, or manually do the pull with the commands:
|
Already have this branch, and just want to pull the latest build? You can run the rebuild.sh file in the dsd-fme folder, or manually do the pull with the commands:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
LSN(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
1,858606250
|
1,858606250
|
||||||
2,858606250
|
2,858606250
|
||||||
3,859606250
|
3,859606250
|
||||||
|
|||||||
|
@@ -1,4 +1,4 @@
|
|||||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
LSN(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
1,851375000
|
1,851375000
|
||||||
2,851800000
|
2,851800000
|
||||||
3,855987500
|
3,855987500
|
||||||
|
|||||||
|
10
examples/hytera_xpt_chan.csv
Normal file
10
examples/hytera_xpt_chan.csv
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
LSN(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
|
1,858606250
|
||||||
|
2,858606250
|
||||||
|
3,859606250
|
||||||
|
4,859606250
|
||||||
|
5,860606250
|
||||||
|
6,860606250
|
||||||
|
7,861606250
|
||||||
|
8,861606250
|
||||||
|
|
||||||
|
6
examples/multi_key.csv
Normal file
6
examples/multi_key.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
key id or tg id (dec), key number or value (dec) //NXDN Scrambler, BP, 10-char Hytera BP only
|
||||||
|
2,70
|
||||||
|
1,123
|
||||||
|
100,70
|
||||||
|
12,48713912656
|
||||||
|
672560,254
|
||||||
|
5
examples/multi_key_hex.csv
Normal file
5
examples/multi_key_hex.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
key id(hex), key value (hex) //10-char Hytera BP, RC4
|
||||||
|
1,1234567890
|
||||||
|
2,ABCDEF1234
|
||||||
|
C,0B57935150
|
||||||
|
64,0B57935150
|
||||||
|
8
examples/nxdn_chan_map.csv
Normal file
8
examples/nxdn_chan_map.csv
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
channel(dec), freq(Hz)
|
||||||
|
141,423862500
|
||||||
|
142,424337500
|
||||||
|
143,424912500
|
||||||
|
203,424100000
|
||||||
|
204,424625000
|
||||||
|
271,422650000
|
||||||
|
302,423287500
|
||||||
|
5
examples/nxdn_sc_key.csv
Normal file
5
examples/nxdn_sc_key.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
key(dec) or tg(dec), value(dec), load any single keys as key 0
|
||||||
|
0,23466
|
||||||
|
1,12345
|
||||||
|
2,32700
|
||||||
|
63,32767
|
||||||
|
@@ -1,33 +1,41 @@
|
|||||||
#example trunking sh scripts (SDR++ and RTL Input) -T = trunking
|
#example trunking sh scripts (SDR++ with RIGCTL and RTL Input)
|
||||||
#make sure to put a # in front of all lines except the one you wish to use
|
#make sure to put a # in front of all lines except the one you wish to use
|
||||||
|
|
||||||
#P25 P1 FDMA CC with C4FM
|
#P25 P1 FDMA CC with C4FM
|
||||||
#dsd-fme -i tcp -U 4532 -T -N 2> log.ans
|
#dsd-fme -i tcp -U 4532 -T -N 2> log.ans
|
||||||
#dsd-fme -i rtl:0:850M:26:-2:8:0:6020 -T -N 2> log.ans
|
#dsd-fme -i rtl:0:850M:0:-2:12 -T -N 2> log.ans
|
||||||
|
|
||||||
#P25 P1 FDMA CC with QPSK (Simulcast)
|
#P25 P1 FDMA CC with QPSK (Simulcast)
|
||||||
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -mq -N 2> log.ans
|
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -mq -N 2> log.ans
|
||||||
#dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -T -mq -N 2> log.ans
|
#dsd-fme -i rtl:0:855.625M:0:-2:12 -T -mq -N 2> log.ans
|
||||||
|
|
||||||
#P25 P2 TDMA CC with QPSK (Simulcast)
|
#P25 P2 TDMA CC with QPSK (Simulcast)
|
||||||
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -m2 -N 2> log.ans
|
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -m2 -N 2> log.ans
|
||||||
#dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -T -f2 -m2 -N 2> log.ans
|
#dsd-fme -i rtl:0:855.625M:0:-2:12 -T -f2 -m2 -N 2> log.ans
|
||||||
|
|
||||||
#DMR TIII, Con+, Cap+
|
#P25 P2 TDMA CC with C4FM
|
||||||
|
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -N 2> log.ans
|
||||||
|
#dsd-fme -i rtl:0:855.625M:0:-2:12 -T -f2 -m2 -N 2> log.ans
|
||||||
|
|
||||||
|
#DMR TIII, Con+, Cap+, XPT
|
||||||
dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
|
dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
|
||||||
#dsd-fme -fs -i rtl:0:450M:26:-2:8:0:6020 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans
|
#dsd-fme -fs -i rtl:0:450M:44:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans
|
||||||
|
|
||||||
#EDACS/EDACS-EA Digital Only
|
#EDACS/EDACS-EA Digital Only
|
||||||
#dsd-fme -fp -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
#dsd-fme -fp -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
#dsd-fme -fp -i rtl:0:850M:26:-2:24:0:6020 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
#dsd-fme -fp -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
|
||||||
#NXDN48 Trunking with Standard Band Plan (Channels 0-800)
|
#NXDN48 Type-C or Type-D Trunking with Channel Map
|
||||||
|
#dsd-fme -fi -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
|
#dsd-fme -fi -i rtl:0:855.625M:0:-2:12:100 -T -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
|
|
||||||
|
#NXDN48 Type-C Trunking with DFA
|
||||||
#dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans
|
#dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans
|
||||||
#dsd-fme -fi -i rtl:0:450M:26:-2:8:75:6020 -T -G group.csv -N 2> log.ans
|
#dsd-fme -fi -i rtl:0:855.625M:0:-2:12:100 -T -N 2> log.ans
|
||||||
|
|
||||||
#NXDN48 Trunking with Channel Map (Custom Channels > 800)
|
#NXDN96 Trunking with Channel Map
|
||||||
#dsd-fme -fi -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans
|
#dsd-fme -fn -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
#dsd-fme -fi -i rtl:0:450M:26:-2:8:75:6020 -T -G group.csv -C channel_map.csv -N 2> log.ans
|
#dsd-fme -fn -i rtl -T -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
|
|
||||||
#Notes: parameters after -i tcp can be omitted if using localhost as the default hostname to connect
|
#Notes: parameters after -i tcp can be omitted if using localhost as the default hostname to connect
|
||||||
#and 7355 as the default port number. -U is the port number for RIGCTL and will mirror the tcp hostname
|
#and 7355 as the default port number. -U is the port number for RIGCTL and will mirror the tcp hostname
|
||||||
@@ -36,5 +44,6 @@ dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
|
|||||||
#inside of SDR++ match, i.e., change in SDR++ from 'localhost' to an IP to bind to (machine's IP address)
|
#inside of SDR++ match, i.e., change in SDR++ from 'localhost' to an IP to bind to (machine's IP address)
|
||||||
|
|
||||||
#Note2: RTL input should only be used on strong signal, the SDR++ VFO will perform much better on marginal signal
|
#Note2: RTL input should only be used on strong signal, the SDR++ VFO will perform much better on marginal signal
|
||||||
|
#paramters after -i rtl can also be omitted now if using the default configurations
|
||||||
|
|
||||||
#Having a -G group.csv is purely optional, but useful if you want to block groups, or give groups names
|
#Having a -G group.csv is purely optional, but useful if you want to block groups, or give groups names
|
||||||
|
|||||||
248
include/dsd.h
248
include/dsd.h
@@ -17,7 +17,8 @@
|
|||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//pretty pretty colors
|
//defined by CMakeLists.txt -- Disable by using cmake -DCOLORS=OFF ..
|
||||||
|
#ifdef PRETTY_COLORS
|
||||||
#define KNRM "\x1B[0m"
|
#define KNRM "\x1B[0m"
|
||||||
#define KRED "\x1B[31m"
|
#define KRED "\x1B[31m"
|
||||||
#define KGRN "\x1B[32m"
|
#define KGRN "\x1B[32m"
|
||||||
@@ -26,6 +27,16 @@
|
|||||||
#define KMAG "\x1B[35m"
|
#define KMAG "\x1B[35m"
|
||||||
#define KCYN "\x1B[36m"
|
#define KCYN "\x1B[36m"
|
||||||
#define KWHT "\x1B[37m"
|
#define KWHT "\x1B[37m"
|
||||||
|
#else
|
||||||
|
#define KNRM ""
|
||||||
|
#define KRED ""
|
||||||
|
#define KGRN ""
|
||||||
|
#define KYEL ""
|
||||||
|
#define KBLU ""
|
||||||
|
#define KMAG ""
|
||||||
|
#define KCYN ""
|
||||||
|
#define KWHT ""
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -47,11 +58,14 @@
|
|||||||
|
|
||||||
#include "p25p1_heuristics.h"
|
#include "p25p1_heuristics.h"
|
||||||
|
|
||||||
|
//OSS support
|
||||||
|
#include <sys/soundcard.h>
|
||||||
|
|
||||||
#include <pulse/simple.h> //PULSE AUDIO
|
#include <pulse/simple.h> //PULSE AUDIO
|
||||||
#include <pulse/error.h> //PULSE AUDIO
|
#include <pulse/error.h> //PULSE AUDIO
|
||||||
|
|
||||||
#define SAMPLE_RATE_IN 48000 //48000
|
#define SAMPLE_RATE_IN 48000 //48000
|
||||||
#define SAMPLE_RATE_OUT 8000 //8000,
|
#define SAMPLE_RATE_OUT 48000 //8000,
|
||||||
|
|
||||||
#ifdef USE_RTLSDR
|
#ifdef USE_RTLSDR
|
||||||
#include <rtl-sdr.h>
|
#include <rtl-sdr.h>
|
||||||
@@ -60,7 +74,11 @@
|
|||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
static volatile int exitflag;
|
#ifdef USE_CODEC2
|
||||||
|
#include <codec2/codec2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern volatile uint8_t exitflag; //fix for issue #136
|
||||||
|
|
||||||
//group csv import struct
|
//group csv import struct
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -190,6 +208,7 @@ typedef struct
|
|||||||
int audio_in_type;
|
int audio_in_type;
|
||||||
char audio_out_dev[1024];
|
char audio_out_dev[1024];
|
||||||
int audio_out_fd;
|
int audio_out_fd;
|
||||||
|
int audio_out_fdR; //right channel audio for OSS hack
|
||||||
SNDFILE *audio_out_file;
|
SNDFILE *audio_out_file;
|
||||||
SF_INFO *audio_out_file_info;
|
SF_INFO *audio_out_file_info;
|
||||||
|
|
||||||
@@ -199,8 +218,10 @@ typedef struct
|
|||||||
int playoffsetR;
|
int playoffsetR;
|
||||||
char mbe_out_dir[1024];
|
char mbe_out_dir[1024];
|
||||||
char mbe_out_file[1024];
|
char mbe_out_file[1024];
|
||||||
|
char mbe_out_fileR[1024]; //second slot on a TDMA system
|
||||||
char mbe_out_path[2048]; //1024
|
char mbe_out_path[2048]; //1024
|
||||||
FILE *mbe_out_f;
|
FILE *mbe_out_f;
|
||||||
|
FILE *mbe_out_fR; //second slot on a TDMA system
|
||||||
FILE *symbol_out_f;
|
FILE *symbol_out_f;
|
||||||
float audio_gain;
|
float audio_gain;
|
||||||
float audio_gainR;
|
float audio_gainR;
|
||||||
@@ -214,6 +235,7 @@ typedef struct
|
|||||||
char szNumbers[1024]; //**tera 10/32/64 char str
|
char szNumbers[1024]; //**tera 10/32/64 char str
|
||||||
short int symbol_out;
|
short int symbol_out;
|
||||||
short int mbe_out; //flag for mbe out, don't attempt fclose more than once
|
short int mbe_out; //flag for mbe out, don't attempt fclose more than once
|
||||||
|
short int mbe_outR; //flag for mbe out, don't attempt fclose more than once
|
||||||
SNDFILE *wav_out_f;
|
SNDFILE *wav_out_f;
|
||||||
SNDFILE *wav_out_fR;
|
SNDFILE *wav_out_fR;
|
||||||
SNDFILE *wav_out_raw;
|
SNDFILE *wav_out_raw;
|
||||||
@@ -252,6 +274,7 @@ typedef struct
|
|||||||
int rtl_udp_port;
|
int rtl_udp_port;
|
||||||
int rtl_bandwidth;
|
int rtl_bandwidth;
|
||||||
int rtl_started;
|
int rtl_started;
|
||||||
|
long int rtl_rms;
|
||||||
int monitor_input_audio;
|
int monitor_input_audio;
|
||||||
int pulse_raw_rate_in;
|
int pulse_raw_rate_in;
|
||||||
int pulse_raw_rate_out;
|
int pulse_raw_rate_out;
|
||||||
@@ -290,6 +313,9 @@ typedef struct
|
|||||||
int inverted_ysf;
|
int inverted_ysf;
|
||||||
short int aggressive_framesync;
|
short int aggressive_framesync;
|
||||||
|
|
||||||
|
int frame_m17;
|
||||||
|
int inverted_m17;
|
||||||
|
|
||||||
FILE *symbolfile;
|
FILE *symbolfile;
|
||||||
int call_alert;
|
int call_alert;
|
||||||
|
|
||||||
@@ -318,12 +344,15 @@ typedef struct
|
|||||||
|
|
||||||
int p25_trunk; //experimental P25 trunking with RIGCTL (or RTLFM)
|
int p25_trunk; //experimental P25 trunking with RIGCTL (or RTLFM)
|
||||||
int p25_is_tuned; //set to 1 if currently on VC, set back to 0 if on CC
|
int p25_is_tuned; //set to 1 if currently on VC, set back to 0 if on CC
|
||||||
int trunk_hangtime; //hangtime in seconds before tuning back to CC
|
float trunk_hangtime; //hangtime in seconds before tuning back to CC
|
||||||
|
|
||||||
|
int scanner_mode; //experimental -- use the channel map as a conventional scanner, quicker tuning, but no CC
|
||||||
|
|
||||||
//csv import filenames
|
//csv import filenames
|
||||||
char group_in_file[1024];
|
char group_in_file[1024];
|
||||||
char lcn_in_file[1024];
|
char lcn_in_file[1024];
|
||||||
char chan_in_file[1024];
|
char chan_in_file[1024];
|
||||||
|
char key_in_file[1024];
|
||||||
//end import filenames
|
//end import filenames
|
||||||
|
|
||||||
//reverse mute
|
//reverse mute
|
||||||
@@ -336,15 +365,34 @@ typedef struct
|
|||||||
uint8_t dmr_dmrla_is_set; //flag to tell us dmrla is set by the user
|
uint8_t dmr_dmrla_is_set; //flag to tell us dmrla is set by the user
|
||||||
uint8_t dmr_dmrla_n; //n value for dmrla
|
uint8_t dmr_dmrla_n; //n value for dmrla
|
||||||
|
|
||||||
|
//DMR Late Entry
|
||||||
|
uint8_t dmr_le; //user option to turn on or turn off late entry for enc identifiers
|
||||||
|
|
||||||
//Trunking - Use Group List as Allow List
|
//Trunking - Use Group List as Allow List
|
||||||
uint8_t trunk_use_allow_list;
|
uint8_t trunk_use_allow_list;
|
||||||
|
|
||||||
|
//Trunking - Tune Group Calls
|
||||||
|
uint8_t trunk_tune_group_calls;
|
||||||
|
|
||||||
//Trunking - Tune Private Calls
|
//Trunking - Tune Private Calls
|
||||||
uint8_t trunk_tune_private_calls;
|
uint8_t trunk_tune_private_calls;
|
||||||
|
|
||||||
//Trunking - Tune Data Calls...why?
|
//Trunking - Tune Data Calls
|
||||||
uint8_t trunk_tune_data_calls;
|
uint8_t trunk_tune_data_calls;
|
||||||
|
|
||||||
|
//Trunking - Tune Enc Calls (P25 only on applicable grants with svc opts)
|
||||||
|
uint8_t trunk_tune_enc_calls;
|
||||||
|
|
||||||
|
//OSS audio - slot preference
|
||||||
|
int slot_preference;
|
||||||
|
|
||||||
|
//'DSP' Format Output
|
||||||
|
uint8_t use_dsp_output;
|
||||||
|
char dsp_out_file[2048];
|
||||||
|
|
||||||
|
//Use P25p1 heuristics
|
||||||
|
uint8_t use_heuristics;
|
||||||
|
|
||||||
} dsd_opts;
|
} dsd_opts;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -366,6 +414,10 @@ typedef struct
|
|||||||
float *audio_out_temp_buf_p;
|
float *audio_out_temp_buf_p;
|
||||||
float audio_out_temp_bufR[160];
|
float audio_out_temp_bufR[160];
|
||||||
float *audio_out_temp_buf_pR;
|
float *audio_out_temp_buf_pR;
|
||||||
|
//analog/raw signal audio buffers
|
||||||
|
short analog_out[960];
|
||||||
|
int analog_sample_counter;
|
||||||
|
//
|
||||||
int audio_out_idx;
|
int audio_out_idx;
|
||||||
int audio_out_idx2;
|
int audio_out_idx2;
|
||||||
int audio_out_idxR;
|
int audio_out_idxR;
|
||||||
@@ -498,28 +550,32 @@ typedef struct
|
|||||||
|
|
||||||
unsigned int dmr_fid;
|
unsigned int dmr_fid;
|
||||||
unsigned int dmr_so;
|
unsigned int dmr_so;
|
||||||
|
unsigned int dmr_flco;
|
||||||
|
|
||||||
unsigned int dmr_fidR;
|
unsigned int dmr_fidR;
|
||||||
unsigned int dmr_soR;
|
unsigned int dmr_soR;
|
||||||
|
unsigned int dmr_flcoR;
|
||||||
|
|
||||||
char slot1light[8];
|
char slot1light[8];
|
||||||
char slot2light[8];
|
char slot2light[8];
|
||||||
int directmode;
|
int directmode;
|
||||||
|
|
||||||
char dmr_branding[99];
|
|
||||||
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
|
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
|
||||||
uint8_t data_header_blocks[2]; //collect number of blocks to follow from data header per slot
|
int data_header_blocks[2]; //collect number of blocks to follow from data header per slot
|
||||||
|
int data_block_counter[2]; //counter for number of data blocks collected
|
||||||
|
uint8_t data_header_valid[2]; //flag for verifying the data header if still valid (in case of tact/burst fec errs)
|
||||||
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
|
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
|
||||||
uint8_t data_header_format[2]; //collect format of data header (conf or unconf) per slot
|
uint8_t data_header_format[2]; //collect format of data header (conf or unconf) per slot
|
||||||
uint8_t data_header_sap[2]; //collect sap info per slot
|
uint8_t data_header_sap[2]; //collect sap info per slot
|
||||||
uint8_t data_block_counter[2]; //counter for number of data blocks collected
|
|
||||||
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
|
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
|
||||||
|
|
||||||
//new stuff below here
|
//new stuff below here
|
||||||
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
|
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
|
||||||
uint8_t dmr_pdu_sf[2][288]; //unified pdu 'superframe' //[slot][byte]
|
uint8_t dmr_pdu_sf[2][288]; //unified pdu 'superframe' //[slot][byte]
|
||||||
|
uint8_t cap_plus_csbk_bits[2][12*8*8]; //CSBK Cap+ FL initial and appended block bit storage, by slot
|
||||||
|
uint8_t cap_plus_block_num[2]; //received block number storage -- per timeslot
|
||||||
uint8_t data_block_crc_valid[2][25]; //flag each individual block as good crc on confirmed data
|
uint8_t data_block_crc_valid[2][25]; //flag each individual block as good crc on confirmed data
|
||||||
char dmr_embedded_signalling[2][6][48]; //embedded signalling 2 slots by 6 vc by 48 bits (replacing TS1SuperFrame.TimeSlotRawVoiceFrame.Sync structure)
|
char dmr_embedded_signalling[2][7][48]; //embedded signalling 2 slots by 6 vc by 48 bits (replacing TS1SuperFrame.TimeSlotRawVoiceFrame.Sync structure)
|
||||||
|
|
||||||
char dmr_cach_fragment[4][17]; //unsure of size, will need to check/verify
|
char dmr_cach_fragment[4][17]; //unsure of size, will need to check/verify
|
||||||
int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet.
|
int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet.
|
||||||
@@ -532,12 +588,14 @@ typedef struct
|
|||||||
char dmr_lrrp_gps[2][200]; //2 slots by 99 char string for string lrrp gps
|
char dmr_lrrp_gps[2][200]; //2 slots by 99 char string for string lrrp gps
|
||||||
char dmr_site_parms[200]; //string for site/net info depending on type of DMR system (TIII or Con+)
|
char dmr_site_parms[200]; //string for site/net info depending on type of DMR system (TIII or Con+)
|
||||||
char call_string[2][200]; //string for call information
|
char call_string[2][200]; //string for call information
|
||||||
|
char active_channel[31][200]; //string for storing and displaying active trunking channels
|
||||||
|
|
||||||
|
|
||||||
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
|
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
|
||||||
|
|
||||||
unsigned char * dpmr_caller_id;
|
char dpmr_caller_id[20];
|
||||||
unsigned char * dpmr_target_id;
|
char dpmr_target_id[20];
|
||||||
|
|
||||||
int dpmr_color_code;
|
int dpmr_color_code;
|
||||||
|
|
||||||
short int dmr_stereo; //need state variable for upsample function
|
short int dmr_stereo; //need state variable for upsample function
|
||||||
@@ -562,6 +620,8 @@ typedef struct
|
|||||||
unsigned long long int p2_wacn;
|
unsigned long long int p2_wacn;
|
||||||
unsigned long long int p2_sysid;
|
unsigned long long int p2_sysid;
|
||||||
unsigned long long int p2_cc; //p1 NAC
|
unsigned long long int p2_cc; //p1 NAC
|
||||||
|
unsigned long long int p2_siteid;
|
||||||
|
unsigned long long int p2_rfssid;
|
||||||
int p2_hardset; //flag for checking whether or not P2 wacn and sysid are hard set by user
|
int p2_hardset; //flag for checking whether or not P2 wacn and sysid are hard set by user
|
||||||
int p2_scramble_offset; //offset counter for scrambling application
|
int p2_scramble_offset; //offset counter for scrambling application
|
||||||
int p2_vch_chan_num; //vch channel number (0 or 1, not the 0-11 TS)
|
int p2_vch_chan_num; //vch channel number (0 or 1, not the 0-11 TS)
|
||||||
@@ -570,6 +630,7 @@ typedef struct
|
|||||||
int p2_is_lcch; //flag to tell us when a frame is lcch and not sacch
|
int p2_is_lcch; //flag to tell us when a frame is lcch and not sacch
|
||||||
|
|
||||||
//iden freq storage for frequency calculations
|
//iden freq storage for frequency calculations
|
||||||
|
int p25_chan_tdma[16]; //set from iden_up vs iden_up_tdma
|
||||||
int p25_chan_iden;
|
int p25_chan_iden;
|
||||||
int p25_chan_type[16];
|
int p25_chan_type[16];
|
||||||
int p25_trans_off[16];
|
int p25_trans_off[16];
|
||||||
@@ -615,6 +676,7 @@ typedef struct
|
|||||||
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
|
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
|
||||||
time_t last_cc_sync_time; //use this to start hunting for CC after signal lost
|
time_t last_cc_sync_time; //use this to start hunting for CC after signal lost
|
||||||
time_t last_vc_sync_time; //flag for voice activity bursts, tune back on con+ after more than x seconds no voice
|
time_t last_vc_sync_time; //flag for voice activity bursts, tune back on con+ after more than x seconds no voice
|
||||||
|
time_t last_active_time; //time the a 'call grant' was received, used to clear the active_channel strings after x seconds
|
||||||
int is_con_plus; //con_plus flag for knowing its safe to skip payload channel after x seconds of no voice sync
|
int is_con_plus; //con_plus flag for knowing its safe to skip payload channel after x seconds of no voice sync
|
||||||
|
|
||||||
//new nxdn stuff
|
//new nxdn stuff
|
||||||
@@ -626,16 +688,28 @@ typedef struct
|
|||||||
uint8_t nxdn_sacch_frame_segcrc[4];
|
uint8_t nxdn_sacch_frame_segcrc[4];
|
||||||
uint8_t nxdn_alias_block_number;
|
uint8_t nxdn_alias_block_number;
|
||||||
char nxdn_alias_block_segment[4][4][8];
|
char nxdn_alias_block_segment[4][4][8];
|
||||||
|
|
||||||
//site/srv/cch info
|
//site/srv/cch info
|
||||||
char nxdn_location_category[14];
|
char nxdn_location_category[14];
|
||||||
uint32_t nxdn_location_sys_code;
|
uint32_t nxdn_location_sys_code;
|
||||||
uint16_t nxdn_location_site_code;
|
uint16_t nxdn_location_site_code;
|
||||||
|
|
||||||
|
//channel access information
|
||||||
|
uint8_t nxdn_rcn;
|
||||||
|
uint8_t nxdn_base_freq;
|
||||||
|
uint8_t nxdn_step;
|
||||||
|
uint8_t nxdn_bw;
|
||||||
|
|
||||||
|
//multi-key array
|
||||||
|
unsigned long long int rkey_array[0xFFFF];
|
||||||
|
int keyloader; //let us know the keyloader is active
|
||||||
|
|
||||||
//dmr late entry mi
|
//dmr late entry mi
|
||||||
uint64_t late_entry_mi_fragment[2][7][3];
|
uint64_t late_entry_mi_fragment[2][7][3];
|
||||||
|
|
||||||
//char dmr_branding[25]; //may need higher value?
|
//dmr manufacturer branding and sub_branding (i.e., Motorola and Con+)
|
||||||
char dmr_branding_sub[25];
|
char dmr_branding[20];
|
||||||
|
char dmr_branding_sub[80];
|
||||||
|
|
||||||
//Remus DMR End Call Alert Beep
|
//Remus DMR End Call Alert Beep
|
||||||
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
|
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
|
||||||
@@ -643,14 +717,63 @@ typedef struct
|
|||||||
//Upsampled Audio Smoothing
|
//Upsampled Audio Smoothing
|
||||||
uint8_t audio_smoothing;
|
uint8_t audio_smoothing;
|
||||||
|
|
||||||
|
//YSF Fusion Call Strings and Info
|
||||||
|
uint8_t ysf_dt; //data type -- VD1, VD2, Full Rate, etc.
|
||||||
|
uint8_t ysf_fi; //frame information -- HC, CC, TC
|
||||||
|
uint8_t ysf_cm; //group or private call
|
||||||
|
char ysf_tgt[11];
|
||||||
|
char ysf_src[11];
|
||||||
|
char ysf_upl[11];
|
||||||
|
char ysf_dnl[11];
|
||||||
|
char ysf_rm1[6];
|
||||||
|
char ysf_rm2[6];
|
||||||
|
char ysf_rm3[6];
|
||||||
|
char ysf_rm4[6];
|
||||||
|
char ysf_txt[21][21]; //text storage blocks
|
||||||
|
|
||||||
|
//M17 Storage
|
||||||
|
uint8_t m17_lsf[360];
|
||||||
|
uint8_t m17_str_dt; //stream contents
|
||||||
|
|
||||||
|
unsigned long long int m17_dst;
|
||||||
|
unsigned long long int m17_src;
|
||||||
|
uint8_t m17_can;
|
||||||
|
|
||||||
|
char m17_dst_csd[20];
|
||||||
|
char m17_src_csd[20];
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
//Codec2
|
||||||
|
#ifdef USE_CODEC2
|
||||||
|
struct CODEC2 *codec2_3200; //M17 fullrate
|
||||||
|
struct CODEC2 *codec2_1600; //M17 halfrate
|
||||||
|
#endif
|
||||||
|
|
||||||
} dsd_state;
|
} dsd_state;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frame sync patterns
|
* Frame sync patterns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define FUSION_SYNC "31111311313113131131" //HA!
|
//The inverse of LSF also seems to be Stream Frame Type, only going to work on Stream mode for now
|
||||||
#define INV_FUSION_SYNC "13333133131331313313" //HA!
|
#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"
|
||||||
|
|
||||||
#define INV_P25P1_SYNC "333331331133111131311111"
|
#define INV_P25P1_SYNC "333331331133111131311111"
|
||||||
#define P25P1_SYNC "111113113311333313133333"
|
#define P25P1_SYNC "111113113311333313133333"
|
||||||
@@ -701,6 +824,26 @@ typedef struct
|
|||||||
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
||||||
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
||||||
|
|
||||||
|
//define the provoice conventional string pattern to default 85/85 if not enabled, else mute it so we won't double sync on accident in frame_sync
|
||||||
|
#ifdef PVCONVENTIONAL
|
||||||
|
#define PROVOICE_CONV "00000000000000000000000000000000" //all zeroes should be unobtainable string in the frame_sync synctests
|
||||||
|
#define INV_PROVOICE_CONV "00000000000000000000000000000000" //all zeroes should be unobtainable string in the frame_sync synctests
|
||||||
|
#else
|
||||||
|
#define PROVOICE_CONV "13131333111311311313131313131313" //TX 85 RX 85 (default programming value)
|
||||||
|
#define INV_PROVOICE_CONV "31313111333133133131313131313131" //TX 85 RX 85 (default programming value)
|
||||||
|
#endif
|
||||||
|
//we use the short sync instead of the default 85/85 wnen PVCONVENTIONAL is defined by cmake
|
||||||
|
#define PROVOICE_CONV_SHORT "1313133311131131" //16-bit short pattern, last 16-bits change based on TX an RX values
|
||||||
|
#define INV_PROVOICE_CONV_SHORT "3131311133313313"
|
||||||
|
//In this pattern (inverted polarity, the norm for PV) 3 is bit 0, and 1 is bit 1 (2 level GFSK)
|
||||||
|
//same pattern //TX //RX
|
||||||
|
// Sync Pattern = 3131311133313313 31331131 31331131 TX/RX 77 -- 31331131 symbol = 01001101 binary = 77 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 33333333 33333333 TX/RX 0 -- 33333333 symbol = 00000000 binary = 0 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 33333331 33333331 TX/RX 1 -- 33333331 symbol = 00000001 binary = 1 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 13131133 13131133 TX/RX 172 -- 13131133 symbol = 10101100 binary = 172 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 11333111 11333111 TX/RX 199 -- 11333111 symbol = 11000111 binary = 199 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 31313131 31313131 TX/RX 85 -- 31313131 symbol = 01010101 binary = 85 decimal
|
||||||
|
|
||||||
#define EDACS_SYNC "313131313131313131313111333133133131313131313131"
|
#define EDACS_SYNC "313131313131313131313111333133133131313131313131"
|
||||||
#define INV_EDACS_SYNC "131313131313131313131333111311311313131313131313"
|
#define INV_EDACS_SYNC "131313131313131313131333111311311313131313131313"
|
||||||
|
|
||||||
@@ -739,13 +882,16 @@ int get_dibit_and_analog_signal (dsd_opts * opts, dsd_state * state, int * out_a
|
|||||||
void skipDibit (dsd_opts * opts, dsd_state * state, int count);
|
void skipDibit (dsd_opts * opts, dsd_state * state, int count);
|
||||||
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
||||||
void saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
void saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
||||||
|
void saveAmbe2450DataR (dsd_opts * opts, dsd_state * state, char *ambe_d); //tdma slot 2
|
||||||
void PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
void PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
||||||
void PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
void PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
||||||
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
||||||
int readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
int readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
||||||
void openMbeInFile (dsd_opts * opts, dsd_state * state);
|
void openMbeInFile (dsd_opts * opts, dsd_state * state);
|
||||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
|
void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
|
void closeMbeOutFileR (dsd_opts * opts, dsd_state * state); //tdma slot 2
|
||||||
void openMbeOutFile (dsd_opts * opts, dsd_state * state);
|
void openMbeOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
|
void openMbeOutFileR (dsd_opts * opts, dsd_state * state); //tdma slot 2
|
||||||
void openWavOutFile (dsd_opts * opts, dsd_state * state);
|
void openWavOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
void openWavOutFileL (dsd_opts * opts, dsd_state * state);
|
void openWavOutFileL (dsd_opts * opts, dsd_state * state);
|
||||||
void openWavOutFileR (dsd_opts * opts, dsd_state * state);
|
void openWavOutFileR (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -779,6 +925,9 @@ int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
|
|||||||
void upsample (dsd_state * state, float invalue);
|
void upsample (dsd_state * state, float invalue);
|
||||||
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
|
//new p25lcw
|
||||||
|
void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t irrecoverable_errors);
|
||||||
|
|
||||||
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
||||||
void processHDU (dsd_opts * opts, dsd_state * state);
|
void processHDU (dsd_opts * opts, dsd_state * state);
|
||||||
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -790,15 +939,17 @@ void processX2TDMAdata (dsd_opts * opts, dsd_state * state);
|
|||||||
void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
|
void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
|
||||||
void processDSTAR_HD (dsd_opts * opts, dsd_state * state);
|
void processDSTAR_HD (dsd_opts * opts, dsd_state * state);
|
||||||
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
|
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
|
||||||
|
void processM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR)
|
||||||
|
void processM17LSF(dsd_opts * opts, dsd_state * state); //M17 (LSF)
|
||||||
void processP2(dsd_opts * opts, dsd_state * state); //P2
|
void processP2(dsd_opts * opts, dsd_state * state); //P2
|
||||||
void processTSBK(dsd_opts * opts, dsd_state * state);
|
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)
|
||||||
short dmr_filter(short sample);
|
short dmr_filter(short sample);
|
||||||
short nxdn_filter(short sample);
|
short nxdn_filter(short sample);
|
||||||
short dpmr_filter(short sample);
|
short dpmr_filter(short sample);
|
||||||
|
short m17_filter(short sample);
|
||||||
|
|
||||||
int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr);
|
uint64_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
|
||||||
/* Global functions */
|
|
||||||
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
|
|
||||||
|
|
||||||
void ncursesOpen (dsd_opts * opts, dsd_state * state);
|
void ncursesOpen (dsd_opts * opts, dsd_state * state);
|
||||||
void ncursesPrinter (dsd_opts * opts, dsd_state * state);
|
void ncursesPrinter (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -807,10 +958,15 @@ void ncursesClose ();
|
|||||||
//new NXDN Functions start here!
|
//new NXDN Functions start here!
|
||||||
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
||||||
void nxdn_descramble (uint8_t dibits[], int len);
|
void nxdn_descramble (uint8_t dibits[], int len);
|
||||||
|
//nxdn deinterleaving/depuncturing functions
|
||||||
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
|
void nxdn_deperm_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_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_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]);
|
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);
|
||||||
|
//end
|
||||||
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
|
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]);
|
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
|
||||||
|
|
||||||
@@ -820,6 +976,7 @@ static uint8_t crc6(const uint8_t buf[], int len);
|
|||||||
static uint16_t crc12f(const uint8_t buf[], int len);
|
static uint16_t crc12f(const uint8_t buf[], int len);
|
||||||
static uint16_t crc15(const uint8_t buf[], int len);
|
static uint16_t crc15(const uint8_t buf[], int len);
|
||||||
static uint16_t crc16cac(const uint8_t buf[], int len);
|
static uint16_t crc16cac(const uint8_t buf[], int len);
|
||||||
|
static uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
|
||||||
|
|
||||||
/* NXDN Convolution functions */
|
/* NXDN Convolution functions */
|
||||||
void CNXDNConvolution_start(void);
|
void CNXDNConvolution_start(void);
|
||||||
@@ -842,7 +999,9 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
|
|||||||
void NXDN_decode_cch_info(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_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_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
void nxdn_location_id_handler (dsd_state * state, uint32_t location_id);
|
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, 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 dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
@@ -858,7 +1017,7 @@ int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
|
|||||||
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
|
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]);
|
||||||
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
|
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 BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32], uint32_t ParityCheckTypeOdd);
|
||||||
|
|
||||||
//Reed Solomon (12,9) functions
|
//Reed Solomon (12,9) functions
|
||||||
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
|
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
|
||||||
@@ -871,6 +1030,8 @@ uint16_t ComputeCrcCCITT(uint8_t * DMRData);
|
|||||||
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len);
|
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len);
|
||||||
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
|
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
|
||||||
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
|
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
|
||||||
|
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData);
|
||||||
|
uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData);
|
||||||
|
|
||||||
//new simplified dmr functions
|
//new simplified dmr functions
|
||||||
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
|
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
|
||||||
@@ -880,6 +1041,8 @@ 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_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[]);
|
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]);
|
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
|
||||||
|
void beeper (dsd_opts * opts, dsd_state * state, int type); //the tone beeper function
|
||||||
|
|
||||||
//Embedded Alias and GPS
|
//Embedded Alias and GPS
|
||||||
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||||
@@ -894,11 +1057,12 @@ void dmrMSData (dsd_opts * opts, dsd_state * state);
|
|||||||
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
|
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified)
|
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified)
|
||||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
||||||
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type);
|
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type);
|
||||||
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||||
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state);
|
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state);
|
||||||
uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||||
|
void dmr_locn (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||||
|
|
||||||
//dmr alg stuff
|
//dmr alg stuff
|
||||||
void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
|
void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -906,14 +1070,24 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state);
|
|||||||
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, char ambe_fr[4][24], char ambe_fr2[4][24], char ambe_fr3[4][24]);
|
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, char ambe_fr[4][24], char ambe_fr2[4][24], char ambe_fr3[4][24]);
|
||||||
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state);
|
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
|
//handle Single Burst (Voice Burst F) or Reverse Channel Signalling
|
||||||
|
void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power);
|
||||||
|
|
||||||
//DMR FEC/CRC from Boatbod - OP25
|
//DMR FEC/CRC from Boatbod - OP25
|
||||||
bool Hamming17123(uint8_t* d);
|
bool Hamming17123(uint8_t* d);
|
||||||
uint8_t crc8(uint8_t bits[], unsigned int len);
|
uint8_t crc8(uint8_t bits[], unsigned int len);
|
||||||
bool crc8_ok(uint8_t bits[], unsigned int len);
|
bool crc8_ok(uint8_t bits[], unsigned int len);
|
||||||
|
//modified CRC functions for SB/RC
|
||||||
|
uint8_t crc7(uint8_t bits[], unsigned int len);
|
||||||
|
uint8_t crc3(uint8_t bits[], unsigned int len);
|
||||||
|
uint8_t crc4(uint8_t bits[], unsigned int len);
|
||||||
|
|
||||||
//LFSR code courtesy of https://github.com/mattames/LFSR/
|
//LFSR and LFSRP code courtesy of https://github.com/mattames/LFSR/
|
||||||
void LFSR(dsd_state * state);
|
void LFSR(dsd_state * state);
|
||||||
|
void LFSRP(dsd_state * state);
|
||||||
|
|
||||||
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
|
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
|
||||||
|
void LFSR64(dsd_state * state);
|
||||||
|
|
||||||
void Hamming_7_4_init();
|
void Hamming_7_4_init();
|
||||||
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
|
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
|
||||||
@@ -953,6 +1127,7 @@ bool QR_16_7_6_decode(unsigned char *rxBits);
|
|||||||
|
|
||||||
void InitAllFecFunction(void);
|
void InitAllFecFunction(void);
|
||||||
void resetState (dsd_state * state);
|
void resetState (dsd_state * state);
|
||||||
|
void reset_dibit_buffer(dsd_state * state);
|
||||||
void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]);
|
void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]);
|
||||||
|
|
||||||
//P25 PDU Handler
|
//P25 PDU Handler
|
||||||
@@ -998,6 +1173,8 @@ unsigned long long int edacs_bch (unsigned long long int message);
|
|||||||
int csvGroupImport(dsd_opts * opts, dsd_state * state);
|
int csvGroupImport(dsd_opts * opts, dsd_state * state);
|
||||||
int csvLCNImport(dsd_opts * opts, dsd_state * state);
|
int csvLCNImport(dsd_opts * opts, dsd_state * state);
|
||||||
int csvChanImport(dsd_opts * opts, dsd_state * state);
|
int csvChanImport(dsd_opts * opts, dsd_state * state);
|
||||||
|
int csvKeyImportDec(dsd_opts * opts, dsd_state * state);
|
||||||
|
int csvKeyImportHex(dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -1008,22 +1185,17 @@ void open_rtlsdr_stream(dsd_opts *opts);
|
|||||||
void cleanup_rtlsdr_stream();
|
void cleanup_rtlsdr_stream();
|
||||||
void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
|
void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
|
||||||
void rtlsdr_sighandler();
|
void rtlsdr_sighandler();
|
||||||
|
void rtl_dev_tune(dsd_opts * opts, long int frequency);
|
||||||
|
long int rtl_return_rms();
|
||||||
|
void rtl_clean_queue();
|
||||||
#endif
|
#endif
|
||||||
//DMR TRELLIS
|
|
||||||
void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* payload);
|
|
||||||
unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits);
|
|
||||||
bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload);
|
|
||||||
void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits);
|
|
||||||
void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points);
|
|
||||||
void CDMRTrellisPointsToDibits(const unsigned char* points, signed char* dibits);
|
|
||||||
void CDMRTrellisBitsToTribits(const unsigned char* payload, unsigned char* tribits);
|
|
||||||
bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload);
|
|
||||||
|
|
||||||
//Phase 2 Helper Functions
|
//Phase 2 Helper Functions
|
||||||
int ez_rs28_ess (int payload[96], int parity[168]); //ezpwd bridge for FME
|
int ez_rs28_ess (int payload[96], int parity[168]); //ezpwd bridge for FME
|
||||||
int ez_rs28_facch (int payload[156], int parity[114]); //ezpwd bridge for FME
|
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 ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
|
||||||
unsigned long long int isch_lookup (unsigned long long int isch); //isch map lookup
|
int isch_lookup (uint64_t isch); //isch map lookup
|
||||||
int bd_bridge (int payload[196], uint8_t decoded[12]); //bridge to Michael Ossmann Block De-interleaver and 1/2 rate trellis decoder
|
int bd_bridge (int payload[196], uint8_t decoded[12]); //bridge to Michael Ossmann Block De-interleaver and 1/2 rate trellis decoder
|
||||||
int crc16_lb_bridge (int payload[190], int len);
|
int crc16_lb_bridge (int payload[190], int len);
|
||||||
int crc12_xb_bridge (int payload[190], int len);
|
int crc12_xb_bridge (int payload[190], int len);
|
||||||
|
|||||||
62
src/bptc.c
62
src/bptc.c
@@ -134,6 +134,13 @@ const uint8_t DeInterleaveReverseChannelBptc[32] =
|
|||||||
24, 9, 26, 11, 28, 13, 30, 15
|
24, 9, 26, 11, 28, 13, 30, 15
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const uint8_t DeInterleaveReverseChannelBptcPlacement[32] =
|
||||||
|
{
|
||||||
|
0, 16, 1, 17, 2, 18, 3, 19,
|
||||||
|
4, 20, 5, 21, 6, 22, 7, 23,
|
||||||
|
8, 24, 9, 25, 10, 26, 11, 27,
|
||||||
|
12, 28, 13, 29, 14, 30, 15, 31
|
||||||
|
};
|
||||||
|
|
||||||
/* Functions ----------------------------------------------------------------*/
|
/* Functions ----------------------------------------------------------------*/
|
||||||
|
|
||||||
@@ -192,18 +199,10 @@ uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* R(0) to R(2) may be used to transport some
|
|
||||||
* Restricted Access System (RAS) information,
|
|
||||||
* So save these three bytes before reset all of them
|
|
||||||
* 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) */
|
|
||||||
|
|
||||||
/* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */
|
/* 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][0] = 0; /* R(2) */
|
||||||
DataMatrix[0][1] = 0; /* R(1) */
|
// DataMatrix[0][1] = 0; /* R(1) */
|
||||||
DataMatrix[0][2] = 0; /* R(0) */
|
// DataMatrix[0][2] = 0; /* R(0) */
|
||||||
|
|
||||||
/* Init the Hamming (15,11,3) library
|
/* Init the Hamming (15,11,3) library
|
||||||
* Not needed because it has already been done
|
* Not needed because it has already been done
|
||||||
@@ -356,6 +355,14 @@ uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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) */
|
||||||
|
|
||||||
/* Return the number of irrecoverable Hamming errors */
|
/* Return the number of irrecoverable Hamming errors */
|
||||||
return HammingIrrecoverableErrorNb;
|
return HammingIrrecoverableErrorNb;
|
||||||
} /* End BPTC_196x96_Extract_Data() */
|
} /* End BPTC_196x96_Extract_Data() */
|
||||||
@@ -498,33 +505,38 @@ uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDat
|
|||||||
* DMRDataExtracted[11..15] = Hamming data
|
* DMRDataExtracted[11..15] = Hamming data
|
||||||
* DMRDataExtracted[16..31] = 16 bits of odd parity (to be checked)
|
* DMRDataExtracted[16..31] = 16 bits of odd parity (to be checked)
|
||||||
*
|
*
|
||||||
|
* @param ParityCheckTypeOdd : 0 = Parity to check is even (used in Non-Reverse Channel Single Burst BPTC)
|
||||||
|
* 1 = Parity to check is odd (used in Reverse Channel Single Burst BPTC)
|
||||||
|
*
|
||||||
* @return 0 = All is OK
|
* @return 0 = All is OK
|
||||||
* Other = Error - Irrecoverable Hamming parity check error number
|
* Other = Error - Irrecoverable Hamming parity check error number
|
||||||
*/
|
*/
|
||||||
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32])
|
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32], uint32_t ParityCheckTypeOdd)
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint8_t DataMatrix[32];
|
uint8_t DataMatrix[32];
|
||||||
uint8_t LineUncorrected[16];
|
uint8_t LineUncorrected[16];
|
||||||
uint8_t LineCorrected[11];
|
uint8_t LineCorrected[11];
|
||||||
uint32_t HammingIrrecoverableErrorNb = 0;
|
uint32_t HammingIrrecoverableErrorNb = 0;
|
||||||
uint32_t ParityCheckErrorNb = 0;
|
uint32_t ParityCheckOddErrorNb = 0;
|
||||||
|
uint32_t ParityCheckEvenErrorNb = 0;
|
||||||
|
|
||||||
|
//TODO: make this so we can load either rc interleave, or single burst interleave
|
||||||
for(i = 0; i < 32; i++)
|
for(i = 0; i < 32; i++)
|
||||||
{
|
{
|
||||||
DataMatrix[DeInterleaveReverseChannelBptc[i]] = InputInterleavedData[i] & 1;
|
DataMatrix[DeInterleaveReverseChannelBptcPlacement[DeInterleaveReverseChannelBptc[i]]] = InputInterleavedData[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the entire input buffer to the output buffer */
|
/* Copy the entire input buffer to the output buffer */
|
||||||
for(i = 0; i < 32; i++)
|
for(i = 0; i < 32; i++)
|
||||||
{
|
{
|
||||||
DMRDataExtracted[i] = DataMatrix[i];
|
DMRDataExtracted[i] = DataMatrix[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare the line to be corrected */
|
/* Prepare the line to be corrected */
|
||||||
for(i = 0; i < 16; i++)
|
for(i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
LineUncorrected[i] = DataMatrix[i];
|
LineUncorrected[i] = DataMatrix[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply Hamming (16,11,4) code correction */
|
/* Apply Hamming (16,11,4) code correction */
|
||||||
@@ -536,7 +548,7 @@ uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDat
|
|||||||
/* Copy the corrected content to the output buffer */
|
/* Copy the corrected content to the output buffer */
|
||||||
for(i = 0; i < 11; i++)
|
for(i = 0; i < 11; i++)
|
||||||
{
|
{
|
||||||
DMRDataExtracted[i] = LineCorrected[i];
|
DMRDataExtracted[i] = LineCorrected[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check Parity bits */
|
/* Check Parity bits */
|
||||||
@@ -544,14 +556,24 @@ uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDat
|
|||||||
{
|
{
|
||||||
/* Odd parity ==> If data = 1 then parity = 0
|
/* Odd parity ==> If data = 1 then parity = 0
|
||||||
* If data = 0 then parity = 1 */
|
* If data = 0 then parity = 1 */
|
||||||
if(DMRDataExtracted[i] == DMRDataExtracted[i + 16]) ParityCheckErrorNb++;
|
if((DMRDataExtracted[i] & 1) == (DMRDataExtracted[i + 16] & 1)) ParityCheckOddErrorNb++;
|
||||||
|
if((DMRDataExtracted[i] & 1) != (DMRDataExtracted[i + 16] & 1)) ParityCheckEvenErrorNb++;
|
||||||
}
|
}
|
||||||
|
|
||||||
//fprintf(stderr, "Hamming ERR=%u ; Parity ERR=%u", HammingIrrecoverableErrorNb, ParityCheckErrorNb);
|
//fprintf(stderr, "Hamming ERR=%u ; Parity odd ERR=%u, Parity even ERR=%u, ", HammingIrrecoverableErrorNb, ParityCheckOddErrorNb, ParityCheckEvenErrorNb);
|
||||||
|
|
||||||
|
//fprintf(stderr, "Content =\n");
|
||||||
|
//for(i = 0; i < 32; i++)
|
||||||
|
//{
|
||||||
|
// fprintf(stderr, "%u", DMRDataExtracted[i] & 1);
|
||||||
|
// if((i == 15) || (i == 31)) fprintf(stderr, "\n");
|
||||||
|
// else fprintf(stderr, "-");
|
||||||
|
//}
|
||||||
|
|
||||||
/* Return the number of irrecoverable Hamming errors +
|
/* Return the number of irrecoverable Hamming errors +
|
||||||
* the number of parity check error */
|
* the number of parity check error */
|
||||||
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
|
if(ParityCheckTypeOdd) return (HammingIrrecoverableErrorNb + ParityCheckOddErrorNb);
|
||||||
|
else return (HammingIrrecoverableErrorNb + ParityCheckEvenErrorNb);
|
||||||
|
|
||||||
} /* End BPTC_16x2_Extract_Data() */
|
} /* End BPTC_16x2_Extract_Data() */
|
||||||
|
|
||||||
|
|||||||
136
src/dmr_34.c
Normal file
136
src/dmr_34.c
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_34.c
|
||||||
|
* DMR 3/4 Rate Simple Trellis Decoder
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-08 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
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,
|
||||||
|
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 contellation points
|
||||||
|
uint8_t constellation_map[16] = {
|
||||||
|
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
//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};
|
||||||
|
|
||||||
|
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
uint32_t irr_err = 0;
|
||||||
|
|
||||||
|
uint8_t deinterleaved_dibits[98];
|
||||||
|
memset (deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
|
||||||
|
|
||||||
|
//deinterleave our input dibits
|
||||||
|
for (i = 0; i < 98; i++)
|
||||||
|
deinterleaved_dibits[interleave[i]] = input[i];
|
||||||
|
|
||||||
|
//pack the input into nibbles (dibit pairs)
|
||||||
|
uint8_t nibs[49];
|
||||||
|
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));
|
||||||
|
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
point[i] = constellation_map[nibs[i]];
|
||||||
|
|
||||||
|
//debug view points -- point[0] should be zero
|
||||||
|
// fprintf (stderr, "\n P =");
|
||||||
|
// for (i = 0; i < 49; i++)
|
||||||
|
// fprintf (stderr, " %02d", point[i]);
|
||||||
|
|
||||||
|
//free-bee on err correction, point[0] should always be zero (flush bits)
|
||||||
|
point[0] = 0;
|
||||||
|
|
||||||
|
//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++)
|
||||||
|
{
|
||||||
|
|
||||||
|
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 position of error and state value
|
||||||
|
// fprintf (stderr, " %d:%d;", i, state);
|
||||||
|
|
||||||
|
//Make a hard decision and flip point to fit in current state
|
||||||
|
point[i] ^= 7; //lucky number 7 (0111)
|
||||||
|
|
||||||
|
//NOTE: Ideally, a full path metric to find the surviving path
|
||||||
|
//is the best way to correct this, but it is also complex and no garauntee to fix errs
|
||||||
|
|
||||||
|
//decrement one and try again
|
||||||
|
if (i != 0) i--;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//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;
|
||||||
|
if (1 == 1) //irr_err == 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, " ERR = %d", irr_err);
|
||||||
|
|
||||||
|
return (irr_err);
|
||||||
|
}
|
||||||
|
|
||||||
896
src/dmr_block.c
896
src/dmr_block.c
File diff suppressed because it is too large
Load Diff
159
src/dmr_bs.c
159
src/dmr_bs.c
@@ -41,6 +41,11 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
uint8_t internalslot;
|
uint8_t internalslot;
|
||||||
uint8_t vc1;
|
uint8_t vc1;
|
||||||
uint8_t vc2;
|
uint8_t vc2;
|
||||||
|
|
||||||
|
//assign as nonsensical numbers
|
||||||
|
uint8_t cc = 25;
|
||||||
|
uint8_t power = 9; //power and pre-emption indicator
|
||||||
|
uint8_t lcss = 9;
|
||||||
|
|
||||||
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
|
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
|
||||||
uint8_t dummy_bits[196];
|
uint8_t dummy_bits[196];
|
||||||
@@ -91,6 +96,10 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//Run Loop while the getting is good
|
//Run Loop while the getting is good
|
||||||
while (loop == 1) {
|
while (loop == 1) {
|
||||||
|
|
||||||
|
memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
memset (ambe_fr2, 0, sizeof(ambe_fr2));
|
||||||
|
memset (ambe_fr3, 0, sizeof(ambe_fr3));
|
||||||
|
|
||||||
internalslot = -1; //reset here so we know if this value is being set properly
|
internalslot = -1; //reset here so we know if this value is being set properly
|
||||||
for(i = 0; i < 12; i++)
|
for(i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
@@ -109,13 +118,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//disabled for dmr_cach testing
|
//disabled for dmr_cach testing
|
||||||
tact_okay = 0;
|
tact_okay = 0;
|
||||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||||
if (tact_okay != 1)
|
if (tact_okay != 1) goto END;
|
||||||
{
|
|
||||||
if (opts->aggressive_framesync == 1)
|
|
||||||
{
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internalslot = state->currentslot = tact_bits[1];
|
internalslot = state->currentslot = tact_bits[1];
|
||||||
|
|
||||||
@@ -189,13 +193,13 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
//embedded link control
|
//embedded link control
|
||||||
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
|
if(internalslot == 0 && vc1 > 1 && vc1 < 7) //grab on vc1 values 2-5 B C D and E
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
|
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
|
if(internalslot == 1 && vc2 > 1 && vc2 < 7) //grab on vc2 values 2-5 B C D and E
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
|
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
@@ -265,6 +269,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
else fprintf (stderr,"Sync: -DMR ");
|
else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
|
||||||
vc1 = 1;
|
vc1 = 1;
|
||||||
|
|
||||||
|
//close MBEout file - slot 1
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
if (internalslot == 1)
|
if (internalslot == 1)
|
||||||
{
|
{
|
||||||
@@ -276,6 +283,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
vc2 = 1;
|
vc2 = 1;
|
||||||
|
|
||||||
|
//close MBEout file - slot 2
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
}
|
}
|
||||||
dmr_data_sync (opts, state);
|
dmr_data_sync (opts, state);
|
||||||
skipcount++;
|
skipcount++;
|
||||||
@@ -294,9 +304,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
|
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
|
||||||
else if (emb_ok == 1)
|
else if (emb_ok == 1)
|
||||||
{
|
{
|
||||||
uint8_t cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||||
uint8_t power = EmbeddedSignalling[4];
|
power = EmbeddedSignalling[4];
|
||||||
uint8_t lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -312,12 +322,16 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
state->dmrburstL = 16;
|
state->dmrburstL = 16;
|
||||||
vc = vc1;
|
vc = vc1;
|
||||||
sprintf (light, "%s", " [SLOT1] slot2 ");
|
sprintf (light, "%s", " [SLOT1] slot2 ");
|
||||||
|
//open MBEout file - slot 1
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state->dmrburstR = 16;
|
state->dmrburstR = 16;
|
||||||
vc = vc2;
|
vc = vc2;
|
||||||
sprintf (light, "%s", " slot1 [SLOT2] ");
|
sprintf (light, "%s", " slot1 [SLOT2] ");
|
||||||
|
//open MBEout file - slot 2
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
|
||||||
}
|
}
|
||||||
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
||||||
else sprintf (polarity, "%s", "-");
|
else sprintf (polarity, "%s", "-");
|
||||||
@@ -329,6 +343,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//process embedded link control
|
//process embedded link control
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||||
|
//check the single burst/reverse channel opportunity -- moved
|
||||||
|
// dmr_sbrc (opts, state, power);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (internalslot == 1 && vc2 == 6)
|
if (internalslot == 1 && vc2 == 6)
|
||||||
@@ -336,6 +353,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//process embedded link control
|
//process embedded link control
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||||
|
//check the single burst/reverse channel opportunity -- moved
|
||||||
|
// dmr_sbrc (opts, state, power);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
||||||
@@ -349,10 +369,14 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
|
||||||
|
//run sbrc here to look for the late entry key and alg after we observe potential errors in VC6
|
||||||
|
if (internalslot == 0 && vc1 == 6) dmr_sbrc (opts, state, power);
|
||||||
|
if (internalslot == 1 && vc2 == 6) dmr_sbrc (opts, state, power);
|
||||||
|
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
fprintf (stderr, "\n");
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
|
||||||
// run alg refresh after vc6 ambe processing
|
// run alg refresh after vc6 ambe processing
|
||||||
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
|
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
|
||||||
@@ -369,12 +393,37 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
state->last_vc_sync_time = time(NULL);
|
state->last_vc_sync_time = time(NULL);
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 98 ", internalslot+1); //'98' is CACH designation value
|
||||||
|
for (i = 0; i < 6; i++) //3 byte CACH
|
||||||
|
{
|
||||||
|
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", cach_byte);
|
||||||
|
}
|
||||||
|
fprintf (pFile, "\n%d 10 ", internalslot+1); //0x10 for voice burst
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
//reset err checks
|
//reset err checks
|
||||||
cach_err = 1;
|
cach_err = 1;
|
||||||
tact_okay = 0;
|
tact_okay = 0;
|
||||||
emb_ok = 0;
|
emb_ok = 0;
|
||||||
|
|
||||||
|
//reset emb components
|
||||||
|
cc = 25;
|
||||||
|
power = 9;
|
||||||
|
lcss = 9;
|
||||||
|
|
||||||
//Extra safeguards to break loop
|
//Extra safeguards to break loop
|
||||||
// if ( (vc1 > 7 && vc2 > 7) ) goto END;
|
// if ( (vc1 > 7 && vc2 > 7) ) goto END;
|
||||||
@@ -407,6 +456,10 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
state->errs2R = 0;
|
state->errs2R = 0;
|
||||||
state->errs2 = 0;
|
state->errs2 = 0;
|
||||||
|
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
//if we have a tact or emb err, then produce sync pattern/err message
|
//if we have a tact or emb err, then produce sync pattern/err message
|
||||||
if (tact_okay != 1 || emb_ok != 1)
|
if (tact_okay != 1 || emb_ok != 1)
|
||||||
{
|
{
|
||||||
@@ -417,18 +470,16 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr, "| VOICE CACH/EMB ERR");
|
fprintf (stderr, "| VOICE CACH/EMB ERR");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
//run LFSR if either slot had an active MI in it.
|
//run refresh if either slot had an active MI in it.
|
||||||
if (state->payload_algid >= 0x21)
|
if (state->payload_algid >= 0x21)
|
||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
LFSR(state);
|
dmr_alg_refresh (opts, state);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
if (state->payload_algidR >= 0x21)
|
if (state->payload_algidR >= 0x21)
|
||||||
{
|
{
|
||||||
state->currentslot = 1;
|
state->currentslot = 1;
|
||||||
LFSR(state);
|
dmr_alg_refresh (opts, state);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//failsafe to reset all data header and blocks when bad tact or emb
|
//failsafe to reset all data header and blocks when bad tact or emb
|
||||||
@@ -447,6 +498,10 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
char ambe_fr2[4][24];
|
char ambe_fr2[4][24];
|
||||||
char ambe_fr3[4][24];
|
char ambe_fr3[4][24];
|
||||||
|
|
||||||
|
memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
memset (ambe_fr2, 0, sizeof(ambe_fr2));
|
||||||
|
memset (ambe_fr3, 0, sizeof(ambe_fr3));
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
//memcpy of ambe_fr for late entry
|
||||||
char m1[4][24];
|
char m1[4][24];
|
||||||
char m2[4][24];
|
char m2[4][24];
|
||||||
@@ -458,6 +513,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
uint8_t cach_err = 0;
|
uint8_t cach_err = 0;
|
||||||
uint8_t sync_okay = 1;
|
uint8_t sync_okay = 1;
|
||||||
|
|
||||||
|
uint8_t internalslot;
|
||||||
|
|
||||||
char cachdata[25];
|
char cachdata[25];
|
||||||
int cachInterleave[24] =
|
int cachInterleave[24] =
|
||||||
@@ -490,10 +546,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
dibit_p++;
|
dibit_p++;
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,15 +566,9 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
//decode and correct tact and compare
|
//decode and correct tact and compare
|
||||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||||
if (tact_okay != 1)
|
if (tact_okay != 1) goto END;
|
||||||
{
|
|
||||||
if (opts->aggressive_framesync == 1)
|
|
||||||
{
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state->currentslot = tact_bits[1];
|
internalslot = state->currentslot = tact_bits[1];
|
||||||
|
|
||||||
//Setup for first AMBE Frame
|
//Setup for first AMBE Frame
|
||||||
|
|
||||||
@@ -577,17 +624,15 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
||||||
{
|
{
|
||||||
if (opts->aggressive_framesync == 1)
|
sync_okay = 0;
|
||||||
{
|
goto END;
|
||||||
sync_okay = 0;
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -610,6 +655,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+108] = dibit;
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -620,6 +666,26 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 98 ", internalslot+1); //'98' is CACH designation value
|
||||||
|
for (i = 0; i < 6; i++) //3 byte CACH
|
||||||
|
{
|
||||||
|
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", cach_byte);
|
||||||
|
}
|
||||||
|
fprintf (pFile, "\n%d 10 ", internalslot+1); //0x10 for "voice burst"
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", getTime());
|
||||||
char polarity[3];
|
char polarity[3];
|
||||||
char light[18];
|
char light[18];
|
||||||
@@ -627,10 +693,14 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
sprintf (light, "%s", " [SLOT1] slot2 ");
|
sprintf (light, "%s", " [SLOT1] slot2 ");
|
||||||
|
//open MBEout file - slot 1
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sprintf (light, "%s", " slot1 [SLOT2] ");
|
sprintf (light, "%s", " slot1 [SLOT2] ");
|
||||||
|
//open MBEout file - slot 2
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
|
||||||
}
|
}
|
||||||
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
||||||
else sprintf (polarity, "%s", "-");
|
else sprintf (polarity, "%s", "-");
|
||||||
@@ -652,10 +722,10 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
|
||||||
//collect the mi fragment
|
//collect the mi fragment
|
||||||
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
|
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
|
||||||
|
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
fprintf (stderr, "\n");
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
|
||||||
//update voice sync time for trunking purposes (particularly Con+)
|
//update voice sync time for trunking purposes (particularly Con+)
|
||||||
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
|
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
|
||||||
@@ -670,18 +740,17 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf (stderr, "| VOICE CACH/SYNC ERR");
|
fprintf (stderr, "| VOICE CACH/SYNC ERR");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
//run LFSR if either slot had an active MI in it.
|
fprintf (stderr, "\n");
|
||||||
|
//run refresh if either slot had an active MI in it.
|
||||||
if (state->payload_algid >= 0x21)
|
if (state->payload_algid >= 0x21)
|
||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
LFSR(state);
|
dmr_alg_refresh (opts, state);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
if (state->payload_algidR >= 0x21)
|
if (state->payload_algidR >= 0x21)
|
||||||
{
|
{
|
||||||
state->currentslot = 1;
|
state->currentslot = 1;
|
||||||
LFSR(state);
|
dmr_alg_refresh (opts, state);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//failsafe to reset all data header and blocks when bad tact or emb
|
//failsafe to reset all data header and blocks when bad tact or emb
|
||||||
|
|||||||
1146
src/dmr_csbk.c
1146
src/dmr_csbk.c
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i];
|
dibit = (int)state->dmr_stereo_payload[i];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[i] = dibit;
|
||||||
|
|
||||||
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
|
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
|
||||||
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||||
@@ -75,11 +76,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
cach_okay = -1;
|
cach_okay = -1;
|
||||||
if (opts->aggressive_framesync == 1)
|
SlotTypeOk = 0;
|
||||||
{
|
goto END;
|
||||||
SlotTypeOk = 0;
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->currentslot = tact_bits[1];
|
state->currentslot = tact_bits[1];
|
||||||
@@ -103,6 +101,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+12];
|
dibit = (int)state->dmr_stereo_payload[i+12];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[i+12] = dibit;
|
||||||
info[2*i] = (1 & (dibit >> 1)); // bit 1
|
info[2*i] = (1 & (dibit >> 1)); // bit 1
|
||||||
info[(2*i)+1] = (1 & dibit); // bit 0
|
info[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
@@ -118,6 +117,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[61];
|
dibit = (int)state->dmr_stereo_payload[61];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[61] = dibit;
|
||||||
|
|
||||||
SlotType[0] = (1 & (dibit >> 1)); // bit 1
|
SlotType[0] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[1] = (1 & dibit); // bit 0
|
SlotType[1] = (1 & dibit); // bit 0
|
||||||
@@ -132,6 +132,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[62];
|
dibit = (int)state->dmr_stereo_payload[62];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[62] = dibit;
|
||||||
|
|
||||||
SlotType[2] = (1 & (dibit >> 1)); // bit 1
|
SlotType[2] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[3] = (1 & dibit); // bit 0
|
SlotType[3] = (1 & dibit); // bit 0
|
||||||
@@ -146,6 +147,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[63];
|
dibit = (int)state->dmr_stereo_payload[63];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[63] = dibit;
|
||||||
|
|
||||||
SlotType[4] = (1 & (dibit >> 1)); // bit 1
|
SlotType[4] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[5] = (1 & dibit); // bit 0
|
SlotType[5] = (1 & dibit); // bit 0
|
||||||
@@ -160,6 +162,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[64];
|
dibit = (int)state->dmr_stereo_payload[64];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[64] = dibit;
|
||||||
|
|
||||||
SlotType[6] = (1 & (dibit >> 1)); // bit 1
|
SlotType[6] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[7] = (1 & dibit); // bit 0
|
SlotType[7] = (1 & dibit); // bit 0
|
||||||
@@ -175,6 +178,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[65];
|
dibit = (int)state->dmr_stereo_payload[65];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[65] = dibit;
|
||||||
SlotType[8] = (1 & (dibit >> 1)); // bit 1
|
SlotType[8] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[9] = (1 & dibit); // bit 0
|
SlotType[9] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -191,6 +195,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+66];
|
dibit = (int)state->dmr_stereo_payload[i+66];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[i+66] = dibit;
|
||||||
|
|
||||||
syncdata[2*i] = (1 & (dibit >> 1)); // bit 1
|
syncdata[2*i] = (1 & (dibit >> 1)); // bit 1
|
||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
sync[i] = (dibit | 1) + 48;
|
sync[i] = (dibit | 1) + 48;
|
||||||
@@ -236,6 +242,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->dmr_stereo == 0)
|
if (state->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
@@ -251,12 +258,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
|
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SlotTypeOk = -1;
|
SlotTypeOk = 0;
|
||||||
if (opts->aggressive_framesync == 1)
|
goto END;
|
||||||
{
|
|
||||||
SlotTypeOk = 0;
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
||||||
@@ -277,6 +280,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->dmr_stereo == 0)
|
if (state->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+95] = dibit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
@@ -288,10 +292,11 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
info[(2*i) + 99] = (1 & dibit); // bit 0
|
info[(2*i) + 99] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
|
//don't run on MS Reverse Channel Data
|
||||||
|
if (state->dmr_ms_rc == 0) dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
|
||||||
|
|
||||||
//don't run cach on simplex or mono
|
//don't run cach on simplex or mono
|
||||||
if (state->dmr_ms_mode == 0 || opts->dmr_mono == 0)
|
if (state->dmr_ms_mode == 0 && opts->dmr_mono == 0)
|
||||||
{
|
{
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
}
|
}
|
||||||
@@ -319,8 +324,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
//con+ voice channels can have extremely long idle periods without properly tearing down
|
//con+ voice channels can have extremely long idle periods without properly tearing down
|
||||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1)
|
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1)
|
||||||
{
|
{
|
||||||
//at 3 seconds, the CC was sending back to the VC even without voice present, increased to 5
|
//bug fixed that caused hopping from CC to VC; decreased to 2 seconds.
|
||||||
if ( (time(NULL) - state->last_vc_sync_time > 5) )
|
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||||
{
|
{
|
||||||
if (opts->use_rigctl == 1) //rigctl tuning
|
if (opts->use_rigctl == 1) //rigctl tuning
|
||||||
{
|
{
|
||||||
@@ -330,13 +335,15 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
else if (opts->audio_in_type == 3) //rtl_fm tuning
|
else if (opts->audio_in_type == 3) //rtl_fm tuning
|
||||||
{
|
{
|
||||||
//UDP command to tune the RTL dongle
|
#ifdef USE_RTLSDR
|
||||||
rtl_udp_tune(opts, state, state->p25_cc_freq);
|
rtl_dev_tune(opts, state->p25_cc_freq);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
opts->p25_is_tuned = 0;
|
opts->p25_is_tuned = 0;
|
||||||
//zero out vc frequencies
|
//zero out vc frequencies
|
||||||
state->p25_vc_freq[0] = 0;
|
state->p25_vc_freq[0] = 0;
|
||||||
state->p25_vc_freq[1] = 0;
|
state->p25_vc_freq[1] = 0;
|
||||||
|
memset (state->active_channel, 0, sizeof(state->active_channel));
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
state->is_con_plus = 0; //con+ flag off
|
state->is_con_plus = 0; //con+ flag off
|
||||||
}
|
}
|
||||||
|
|||||||
337
src/dmr_dburst.c
337
src/dmr_dburst.c
@@ -9,9 +9,11 @@
|
|||||||
* 2022-12 DSD-FME Florida Man Edition
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
*-----------------------------------------------------------------------------*/
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
//TODO: CRC9 on confirmed data blocks; CRC32 on completed data sf;
|
//WIP: Unified Single Block Data - USBD -- TODO: USBD LIP Protocol Handling
|
||||||
//TODO: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC
|
//WIP: UDT Header and Blocks (coded - need samples)
|
||||||
//TODO: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
|
//WIP: CRC9/CRC32 on Rate 1 Data (need samples)
|
||||||
|
//WIP: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC (rc interleave) and when to run RC and not SB
|
||||||
|
//WIP: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
@@ -26,6 +28,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
uint8_t slot = state->currentslot;
|
uint8_t slot = state->currentslot;
|
||||||
uint8_t block = state->data_block_counter[slot];
|
uint8_t block = state->data_block_counter[slot];
|
||||||
|
|
||||||
|
//confirmed data
|
||||||
|
uint8_t dbsn = 0; //data block serial number for confirmed data blocks
|
||||||
|
uint8_t blockcounter = 0; //local block count
|
||||||
|
uint8_t confdatabits[250]; //array to reshuffle conf data block bits into sequence for crc9 check
|
||||||
|
memset (confdatabits, 0, sizeof(confdatabits));
|
||||||
|
|
||||||
//BPTC 196x96 Specific
|
//BPTC 196x96 Specific
|
||||||
uint8_t BPTCDeInteleavedData[196];
|
uint8_t BPTCDeInteleavedData[196];
|
||||||
uint8_t BPTCDmrDataBit[96];
|
uint8_t BPTCDmrDataBit[96];
|
||||||
@@ -37,25 +45,37 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
uint8_t LC_DataBytes[10];
|
uint8_t LC_DataBytes[10];
|
||||||
int Burst = -1;
|
int Burst = -1;
|
||||||
|
|
||||||
//Unified PDU (is this the best nomenclature) Bytes
|
memset (BPTCDeInteleavedData, 0, sizeof(BPTCDeInteleavedData));
|
||||||
uint8_t DMR_PDU[25]; //using 25 bytes as the max for now
|
memset (BPTCDmrDataBit, 0, sizeof(BPTCDmrDataBit));
|
||||||
uint8_t DMR_PDU_bits[196]; //bitwise version of unified payload, will probably be a lot easier to use this w/ manual
|
memset (BPTCDmrDataByte, 0, sizeof(BPTCDmrDataByte));
|
||||||
|
memset (BptcDataMatrix, 0, sizeof(BptcDataMatrix));
|
||||||
|
memset (LC_DataBit, 0, sizeof(LC_DataBit));
|
||||||
|
memset (LC_DataBytes, 0, sizeof(LC_DataBytes));
|
||||||
|
|
||||||
|
//PDU Bytes and Bits
|
||||||
|
uint8_t DMR_PDU[25];
|
||||||
|
uint8_t DMR_PDU_bits[196];
|
||||||
memset (DMR_PDU, 0, sizeof (DMR_PDU));
|
memset (DMR_PDU, 0, sizeof (DMR_PDU));
|
||||||
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
||||||
|
|
||||||
uint8_t R[3];
|
uint8_t R[3];
|
||||||
uint8_t BPTCReservedBits = 0;
|
uint8_t BPTCReservedBits = 0;
|
||||||
|
uint8_t is_ras = 0;
|
||||||
|
uint8_t crc_original_validity = 0;
|
||||||
|
|
||||||
uint32_t crcmask = 0;
|
uint32_t crcmask = 0;
|
||||||
uint8_t crclen = 0;
|
uint8_t crclen = 0;
|
||||||
|
|
||||||
uint8_t is_bptc = 0;
|
uint8_t is_bptc = 0;
|
||||||
uint8_t is_trellis = 0;
|
uint8_t is_trellis = 0;
|
||||||
uint8_t is_emb = 0;
|
uint8_t is_emb = 0;
|
||||||
uint8_t is_lc = 0;
|
uint8_t is_lc = 0;
|
||||||
uint8_t is_full = 0;
|
uint8_t is_full = 0;
|
||||||
|
uint8_t is_udt = 0;
|
||||||
uint8_t pdu_len = 0;
|
uint8_t pdu_len = 0;
|
||||||
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
||||||
|
|
||||||
|
uint8_t usbd_st = 0; //usbd service type
|
||||||
|
|
||||||
switch(databurst){
|
switch(databurst){
|
||||||
case 0x00: //PI
|
case 0x00: //PI
|
||||||
@@ -108,32 +128,36 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
pdu_len = 12; //12 bytes
|
pdu_len = 12; //12 bytes
|
||||||
sprintf(state->fsubtype, " DATA");
|
sprintf(state->fsubtype, " DATA");
|
||||||
break;
|
break;
|
||||||
//TODO - 1/2 0x0F0 - 3/4 0x1FF - Rate 1 0x10F
|
|
||||||
//going to set crclen and mask according to confirmed data here,
|
|
||||||
//use block assmebler to carry out completed message crc
|
|
||||||
case 0x07: //1/2 Rate Data
|
case 0x07: //1/2 Rate Data
|
||||||
is_bptc = 1;
|
is_bptc = 1;
|
||||||
crclen = 9; //confirmed data only
|
crclen = 9; //confirmed data only
|
||||||
crcmask = 0x0F0;
|
crcmask = 0x0F0;
|
||||||
pdu_len = 12; //12 bytes unconfirmed
|
pdu_len = 12; //12 bytes unconfirmed
|
||||||
|
sprintf(state->fsubtype, " R12U ");
|
||||||
if (state->data_conf_data[slot] == 1)
|
if (state->data_conf_data[slot] == 1)
|
||||||
{
|
{
|
||||||
pdu_len = 10;
|
pdu_len = 10;
|
||||||
pdu_start = 2; //start at plus two when assembling
|
pdu_start = 2; //start at plus two when assembling
|
||||||
}
|
sprintf(state->fsubtype, " R12C ");
|
||||||
sprintf(state->fsubtype, " R12D ");
|
}
|
||||||
|
if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks
|
||||||
|
{
|
||||||
|
is_udt = 1;
|
||||||
|
sprintf(state->fsubtype, " UDTC ");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0x08: //3/4 Rate Data
|
case 0x08: //3/4 Rate Data
|
||||||
is_trellis = 1;
|
is_trellis = 1;
|
||||||
crclen = 9; //confirmed data only
|
crclen = 9; //confirmed data only
|
||||||
crcmask = 0x1FF;
|
crcmask = 0x1FF;
|
||||||
pdu_len = 18; //18 bytes unconfirmed
|
pdu_len = 18; //18 bytes unconfirmed
|
||||||
|
sprintf(state->fsubtype, " R34U ");
|
||||||
if (state->data_conf_data[slot] == 1)
|
if (state->data_conf_data[slot] == 1)
|
||||||
{
|
{
|
||||||
pdu_len = 16;
|
pdu_len = 16;
|
||||||
pdu_start = 2; //start at plus two when assembling
|
pdu_start = 2; //start at plus two when assembling
|
||||||
|
sprintf(state->fsubtype, " R34C ");
|
||||||
}
|
}
|
||||||
sprintf(state->fsubtype, " R34D ");
|
|
||||||
break;
|
break;
|
||||||
case 0x09: //Idle
|
case 0x09: //Idle
|
||||||
//pseudo-random data fill, no need to do anything with this
|
//pseudo-random data fill, no need to do anything with this
|
||||||
@@ -141,33 +165,70 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
break;
|
break;
|
||||||
case 0x0A: //1 Rate Data
|
case 0x0A: //1 Rate Data
|
||||||
crclen = 9; //confirmed data only
|
crclen = 9; //confirmed data only
|
||||||
crcmask = 0x1FF;
|
crcmask = 0x10F;
|
||||||
is_full = 1;
|
is_full = 1;
|
||||||
pdu_len = 25; //196 bits - 24.5 bytes
|
pdu_len = 25; //196 bits - 24.5 bytes
|
||||||
sprintf(state->fsubtype, " R_1D ");
|
sprintf(state->fsubtype, " R_1U ");
|
||||||
|
if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
pdu_len = 23;
|
||||||
|
pdu_start = 2; //start at plus two when assembling
|
||||||
|
sprintf(state->fsubtype, " R_1C ");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0x0B: //Unified Data
|
case 0x0B: //Unified Single Block Data USBD
|
||||||
is_bptc = 1;
|
is_bptc = 1;
|
||||||
crclen = 16;
|
crclen = 16;
|
||||||
crcmask = 0x3333;
|
crcmask = 0x3333;
|
||||||
pdu_len = 12; //12 bytes
|
pdu_len = 12; //12 bytes
|
||||||
sprintf(state->fsubtype, " UDAT ");
|
sprintf(state->fsubtype, " USBD ");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//special types (not real data 'sync' bursts)
|
//special types (not real data 'sync' bursts)
|
||||||
case 0xEB: //Embedded Signalling
|
case 0xEB: //Embedded Signalling
|
||||||
|
crclen = 5;
|
||||||
is_emb = 1;
|
is_emb = 1;
|
||||||
pdu_len = 9;
|
pdu_len = 9;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
//Slot Type FEC should catch this so we never see it,
|
||||||
|
//but if it doesn't, then we can still dump the entire 'packet'
|
||||||
|
//treat like rate 1 unconfirmed data
|
||||||
|
is_full = 1;
|
||||||
|
pdu_len = 25; //196 bits - 24.5 bytes
|
||||||
|
sprintf(state->fsubtype, " _UNK ");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//flag off prop head when not looking at data blocks
|
||||||
|
if (databurst != 0x6 && databurst != 0x7 && databurst != 0x8 && databurst != 0xA && databurst != 0xB) state->data_p_head[slot] = 0;
|
||||||
|
|
||||||
if (databurst != 0xEB)
|
if (databurst != 0xEB)
|
||||||
{
|
{
|
||||||
if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
||||||
fprintf(stderr, "|%s", state->fsubtype);
|
fprintf(stderr, "|%s", state->fsubtype);
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 98 ", slot+1); //'98' is CACH designation value
|
||||||
|
for (i = 0; i < 6; i++) //3 byte CACH
|
||||||
|
{
|
||||||
|
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", cach_byte); //nibble, not a byte, next time I look at this and wonder why its not %02X
|
||||||
|
}
|
||||||
|
fprintf (pFile, "\n%d %02X ", slot+1, databurst); //use hex value of current data burst type
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Most Data Sync Burst types will use the bptc 196x96
|
//Most Data Sync Burst types will use the bptc 196x96
|
||||||
@@ -184,7 +245,10 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
||||||
|
|
||||||
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
|
/* 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) & 0x08);
|
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x04);
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
//fprintf (stderr, " RAS? %X - %d %d %d", BPTCReservedBits, R[0], R[1], R[2]);
|
||||||
|
|
||||||
/* Convert the 96 bits BPTC data into 12 bytes */
|
/* Convert the 96 bits BPTC data into 12 bytes */
|
||||||
k = 0;
|
k = 0;
|
||||||
@@ -213,18 +277,57 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
||||||
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed 1/2 data blocks
|
//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] == 1)
|
else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
|
||||||
{
|
{
|
||||||
//run CRC on individual blocks according to type and validate each individual block
|
CRCComputed = 0;
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCCorrect = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//run CRC9 on intermediate and last 1/2 confirmed data blocks
|
||||||
|
else if (state->data_conf_data[slot] == 1 && databurst == 0x7)
|
||||||
|
{
|
||||||
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
||||||
|
dbsn = (uint32_t)ConvertBitIntoBytes(&BPTCDmrDataBit[0], 7) + 1; //recover data block serial number
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&BPTCDmrDataBit[7], 9); //extract CRC from data
|
||||||
|
CRCExtracted = CRCExtracted ^ crcmask;
|
||||||
|
|
||||||
|
//reorganize the BPTCDmrDataBit array into confdatabits, just for CRC9 check
|
||||||
|
//need to find a sample to confirm this
|
||||||
|
for(i = 0; i < 80; i++) confdatabits[i] = BPTCDmrDataBit[i + 16];
|
||||||
|
for(i = 0; i < 7; i++) confdatabits[i + 80] = BPTCDmrDataBit[i];
|
||||||
|
|
||||||
|
CRCComputed = ComputeCrc9Bit(confdatabits, 87);
|
||||||
|
if (CRCExtracted == CRCComputed)
|
||||||
|
{
|
||||||
|
CRCCorrect = 1;
|
||||||
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
||||||
|
}
|
||||||
|
else state->data_block_crc_valid[slot][blockcounter] = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//run CCITT on other data forms
|
//run CCITT on other data forms
|
||||||
else //will still run on unconfirmed data (shouldn't matter other than a possible bogus warning message)
|
else
|
||||||
{
|
{
|
||||||
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
||||||
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||||
else CRCCorrect = 0;
|
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)
|
||||||
|
{
|
||||||
|
crc_original_validity = CRCCorrect;
|
||||||
|
CRCCorrect = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
|
if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
|
||||||
@@ -249,7 +352,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
//convert to unified DMR_PDU and DMR_PDU_bits
|
//convert to DMR_PDU and DMR_PDU_bits
|
||||||
for (i = 0; i < pdu_len; i++)
|
for (i = 0; i < pdu_len; i++)
|
||||||
{
|
{
|
||||||
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
||||||
@@ -295,11 +398,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
||||||
|
|
||||||
/* Reconstitute the 5 bit CRC */
|
/* Reconstitute the 5 bit CRC */
|
||||||
CRCExtracted = (LC_DataBit[72] & 1) << 4;
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5);
|
||||||
CRCExtracted |= (LC_DataBit[73] & 1) << 3;
|
|
||||||
CRCExtracted |= (LC_DataBit[74] & 1) << 2;
|
|
||||||
CRCExtracted |= (LC_DataBit[75] & 1) << 1;
|
|
||||||
CRCExtracted |= (LC_DataBit[76] & 1) << 0;
|
|
||||||
|
|
||||||
/* Compute the 5 bit CRC */
|
/* Compute the 5 bit CRC */
|
||||||
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
||||||
@@ -319,35 +418,71 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//the ugly one
|
//the sexy one
|
||||||
if (is_trellis)
|
if (is_trellis)
|
||||||
{
|
{
|
||||||
CRCExtracted = 0;
|
CRCExtracted = 0;
|
||||||
CRCComputed = 0;
|
CRCComputed = 0;
|
||||||
IrrecoverableErrors = 1;
|
IrrecoverableErrors = 1;
|
||||||
bool trellis_good = TRUE;
|
|
||||||
|
|
||||||
uint8_t tdibits[98];
|
uint8_t tdibits[98];
|
||||||
memset (tdibits, 0, sizeof(tdibits));
|
memset (tdibits, 0, sizeof(tdibits));
|
||||||
|
|
||||||
//reconstitute info bits into reverse ordered dibits for the trellis decoder
|
//reconstitute info bits into dibits for the trellis decoder
|
||||||
for (i = 0; i < 98; i++)
|
for (i = 0; i < 98; i++)
|
||||||
{
|
tdibits[i] = (info[i*2] << 1) | info[i*2+1];
|
||||||
tdibits[97-i] = (info[i*2] << 1) | info[i*2+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char TrellisReturn[18];
|
uint8_t TrellisReturn[18];
|
||||||
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
||||||
|
IrrecoverableErrors = dmr_34(tdibits, TrellisReturn);
|
||||||
|
|
||||||
trellis_good = CDMRTrellisDecode(tdibits, TrellisReturn);
|
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
|
||||||
if (trellis_good == TRUE) IrrecoverableErrors = 0;
|
//they may have been successfully corrected, the CRC will reveal as much
|
||||||
// else (fprintf (stderr, " Trellis Failure")); //debug print
|
|
||||||
|
|
||||||
for (i = 0; i < pdu_len; i++) //18
|
for (i = 0; i < pdu_len; i++)
|
||||||
|
DMR_PDU[i] = TrellisReturn[i+pdu_start];
|
||||||
|
|
||||||
|
for(i = 0, j = 0; i < 18; i++, j+=8)
|
||||||
{
|
{
|
||||||
DMR_PDU[i] = (uint8_t)TrellisReturn[i+pdu_start];
|
DMR_PDU_bits[j + 0] = (TrellisReturn[i] >> 7) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 1] = (TrellisReturn[i] >> 6) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 2] = (TrellisReturn[i] >> 5) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 3] = (TrellisReturn[i] >> 4) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 4] = (TrellisReturn[i] >> 3) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 5] = (TrellisReturn[i] >> 2) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 6] = (TrellisReturn[i] >> 1) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 7] = (TrellisReturn[i] >> 0) & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//set CRC to correct on unconfirmed 3/4 data blocks (for reporting due to no CRC available on these)
|
||||||
|
if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
|
||||||
|
|
||||||
|
//run CRC9 on intermediate and last 3/4 data blocks
|
||||||
|
else if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
||||||
|
dbsn = (uint32_t)ConvertBitIntoBytes(&DMR_PDU_bits[0], 7) + 1; //recover data block serial number
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&DMR_PDU_bits[7], 9); //extract CRC from data
|
||||||
|
CRCExtracted = CRCExtracted ^ crcmask;
|
||||||
|
|
||||||
|
//reorganize the DMR_PDU_bits array into confdatabits, just for CRC9 check
|
||||||
|
for(i = 0; i < 128; i++) confdatabits[i] = DMR_PDU_bits[i + 16];
|
||||||
|
for(i = 0; i < 7; i++) confdatabits[i + 128] = DMR_PDU_bits[i];
|
||||||
|
|
||||||
|
CRCComputed = ComputeCrc9Bit(confdatabits, 135);
|
||||||
|
if (CRCExtracted == CRCComputed)
|
||||||
|
{
|
||||||
|
CRCCorrect = 1;
|
||||||
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
state->data_block_crc_valid[slot][blockcounter] = 0;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//reorganize the DMR_PDU_bits into PDU friendly format (minus the dbsn and crc9)
|
||||||
|
memset (DMR_PDU_bits, 0, sizeof(DMR_PDU_bits));
|
||||||
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
||||||
{
|
{
|
||||||
DMR_PDU_bits[j + 0] = (TrellisReturn[i+pdu_start] >> 7) & 0x01;
|
DMR_PDU_bits[j + 0] = (TrellisReturn[i+pdu_start] >> 7) & 0x01;
|
||||||
@@ -360,17 +495,13 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed 3/4 data blocks
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_full) //Rate 1 Data
|
if (is_full) //Rate 1 Data -- todo: fix padding bits, the four padding bits are on bit 96,97,98,99
|
||||||
{
|
{
|
||||||
CRCExtracted = 0;
|
CRCExtracted = 0;
|
||||||
CRCComputed = 0;
|
CRCComputed = 0;
|
||||||
IrrecoverableErrors = 0;
|
IrrecoverableErrors = 0; //implicit, since there is no encoding
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed data
|
|
||||||
|
|
||||||
for (i = 0; i < 24; i++)
|
for (i = 0; i < 24; i++)
|
||||||
{
|
{
|
||||||
@@ -381,6 +512,32 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
DMR_PDU[25] = (uint8_t)ConvertBitIntoBytes(&info[192], 4);
|
DMR_PDU[25] = (uint8_t)ConvertBitIntoBytes(&info[192], 4);
|
||||||
DMR_PDU[25] = DMR_PDU[25] << 4;
|
DMR_PDU[25] = DMR_PDU[25] << 4;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
//run CRC9 on intermediate and last 1 rate data blocks
|
||||||
|
else if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
||||||
|
dbsn = (uint32_t)ConvertBitIntoBytes(&info[0], 7) + 1; //recover data block serial number
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&info[7], 9); //extract CRC from data
|
||||||
|
CRCExtracted = CRCExtracted ^ crcmask;
|
||||||
|
|
||||||
|
//reorganize the info bit array into confdatabits, just for CRC9 check
|
||||||
|
//unconfirmed, haven't seen any samples with rate 1 data
|
||||||
|
for(i = 0; i < 176; i++) confdatabits[i] = info[i + 16];
|
||||||
|
for(i = 0; i < 7; i++) confdatabits[i + 176] = info[i];
|
||||||
|
|
||||||
|
CRCComputed = ComputeCrc9Bit(confdatabits, 183);
|
||||||
|
if (CRCExtracted == CRCComputed)
|
||||||
|
{
|
||||||
|
CRCCorrect = 1;
|
||||||
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
||||||
|
}
|
||||||
|
else state->data_block_crc_valid[slot][blockcounter] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//copy info to dmr_pdu_bits
|
//copy info to dmr_pdu_bits
|
||||||
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
||||||
|
|
||||||
@@ -395,27 +552,75 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC
|
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC
|
||||||
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 3); //EMB
|
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 3); //EMB
|
||||||
|
|
||||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified) - type 1
|
//dmr data header and multi block types (header, 1/2, 3/4, 1, UDT) - type 1
|
||||||
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors);
|
||||||
if (databurst == 0x07) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //1/2 Rate Data
|
|
||||||
if (databurst == 0x08) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //3/4 Rate Data
|
if (databurst == 0x08) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //3/4 Rate Data
|
||||||
//if (databurst == 0x0A) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Full Rate Data - Disabled until further testing can be done
|
if (databurst == 0x0A) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Full Rate Data
|
||||||
|
if (databurst == 0x07 && is_udt == 0) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //1/2 Rate Data
|
||||||
|
if (databurst == 0x07 && is_udt == 1) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 3); //UDT with 1/2 Rate Encoding
|
||||||
|
|
||||||
//is unified data handled like normal data, or control signalling data?
|
//control signalling types (CSBK, MBC)
|
||||||
if (databurst == 0x0B) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Unified Data
|
|
||||||
//if (databurst == 0x0B) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
|
||||||
|
|
||||||
//control signalling types (CSBK, MBC, UDT)
|
|
||||||
if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
//both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu
|
//both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu
|
||||||
if (databurst == 0x04)
|
if (databurst == 0x04)
|
||||||
{
|
{
|
||||||
state->data_block_counter[slot] = 0; //zero block counter before running header
|
state->data_block_counter[slot] = 0; //zero block counter before running header
|
||||||
|
state->data_header_valid[slot] = 1; //set valid header since we received one
|
||||||
dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
||||||
}
|
}
|
||||||
if (databurst == 0x05) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
if (databurst == 0x05) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
||||||
|
|
||||||
|
//Unified Single Data Block (USBD) -- Not to be confused with Unified Data Transport (UDT)
|
||||||
|
if (databurst == 0x0B)
|
||||||
|
{
|
||||||
|
//TODO: provide more robust handling at a later date -- ETSI TS 102 361-4 V1.11.1 (2021-01) 6.6.11.3
|
||||||
|
usbd_st = (uint8_t)ConvertBitIntoBytes(&DMR_PDU_bits[0], 4);
|
||||||
|
fprintf (stderr, "%s\n", KYEL);
|
||||||
|
fprintf (stderr, " Unified Single Block Data - ");
|
||||||
|
if (usbd_st == 0) fprintf (stderr, "Location Information Protocol");
|
||||||
|
else if (usbd_st > 8) fprintf (stderr, "Manufacturer Specific Service %d ", usbd_st);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (IrrecoverableErrors != 0 && databurst != 0x08 && databurst != 0x09) //&& databurst != 0x05
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf(stderr, " (FEC ERR)");
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR) ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
//print the unified PDU format here, if not slot idle
|
//print the unified PDU format here, if not slot idle
|
||||||
if (opts->payload == 1 && databurst != 0x09)
|
if (opts->payload == 1 && databurst != 0x09)
|
||||||
{
|
{
|
||||||
@@ -427,20 +632,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
fprintf (stderr, "[%02X]", DMR_PDU[i]);
|
fprintf (stderr, "[%02X]", DMR_PDU[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
//debug print
|
||||||
|
// if (dbsn) fprintf (stderr, " SN %X", dbsn);
|
||||||
|
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
// fprintf (stderr, "%s", KCYN);
|
fprintf (stderr, "%s", KNRM);
|
||||||
// fprintf (stderr, "\n Hex to Ascii - ");
|
|
||||||
// for (i = 0; i < 12; i++)
|
|
||||||
// {
|
|
||||||
// if (DMR_PDU[i] <= 0x7E && DMR_PDU[i] >=0x20)
|
|
||||||
// {
|
|
||||||
// fprintf (stderr, "%c", DMR_PDU[i]);
|
|
||||||
// }
|
|
||||||
// else fprintf (stderr, ".");
|
|
||||||
// }
|
|
||||||
|
|
||||||
fprintf (stderr,"%s", KNRM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
500
src/dmr_flco.c
500
src/dmr_flco.c
@@ -25,61 +25,272 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
|||||||
uint8_t so = 0;
|
uint8_t so = 0;
|
||||||
uint32_t target = 0;
|
uint32_t target = 0;
|
||||||
uint32_t source = 0;
|
uint32_t source = 0;
|
||||||
|
uint8_t capsite = 0;
|
||||||
int restchannel = -1;
|
int restchannel = -1;
|
||||||
int is_cap_plus = 0;
|
int is_cap_plus = 0;
|
||||||
int is_alias = 0;
|
int is_alias = 0;
|
||||||
int is_gps = 0;
|
int is_gps = 0;
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
|
|
||||||
|
//XPT 'Things'
|
||||||
pf = (uint8_t)(lc_bits[0]); //Protect Flag
|
int is_xpt = 0;
|
||||||
reserved = (uint8_t)(lc_bits[1]); //Reserved
|
uint8_t xpt_hand = 0; //handshake
|
||||||
|
uint8_t xpt_free = 0; //free repeater
|
||||||
|
uint8_t xpt_int = 0; //xpt channel to interrupt (channel/repeater call should occur on?)
|
||||||
|
uint8_t xpt_res_a = 0; //unknown values of other bits of the XPT LC
|
||||||
|
uint8_t xpt_res_b = 0; //unknown values of other bits of the XPT LC
|
||||||
|
uint8_t xpt_res_c = 0; //unknown values of other bits of the XPT LC
|
||||||
|
uint8_t target_hash[24]; //for XPT (and others if desired, get the hash and compare against SLC or XPT Status CSBKs)
|
||||||
|
uint8_t tg_hash = 0; //value of the hashed TG
|
||||||
|
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t unk = 0; //flag for unknown FLCO + FID combo
|
||||||
|
|
||||||
|
pf = (uint8_t)(lc_bits[0]); //Protect Flag -- Hytera XPT uses this to signify which TS the PDU is on
|
||||||
|
reserved = (uint8_t)(lc_bits[1]); //Reserved -- Hytera XPT G/I bit; 0 - Individual; 1 - Group;
|
||||||
flco = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //Full Link Control Opcode
|
flco = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //Full Link Control Opcode
|
||||||
fid = (uint8_t)ConvertBitIntoBytes(&lc_bits[8], 8); //Feature set ID (FID)
|
fid = (uint8_t)ConvertBitIntoBytes(&lc_bits[8], 8); //Feature set ID (FID)
|
||||||
so = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8); //Service Options
|
so = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8); //Service Options
|
||||||
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24); //Target or Talk Group
|
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24); //Target or Talk Group
|
||||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[48], 24);
|
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[48], 24);
|
||||||
|
|
||||||
//look for Cap+ on VLC header, then set source and/or rest channel appropriately
|
//read ahead a little to get this for the xpt flag
|
||||||
if (type == 1 && fid == 0x10 && flco == 0x04)
|
if (IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68)
|
||||||
{
|
{
|
||||||
is_cap_plus = 1;
|
sprintf (state->dmr_branding, "%s", " Hytera");
|
||||||
restchannel = (int)ConvertBitIntoBytes(&lc_bits[48], 8);
|
sprintf (state->dmr_branding_sub, "XPT ");
|
||||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16);
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//look at the dmr_branding_sub for the XPT string
|
||||||
|
//branding sub is set at CSBK(68-3A and 3B), SLCO 8, and here on 0x09
|
||||||
|
if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1;
|
||||||
|
|
||||||
|
//hytera XPT -- disable the pf flag, is used for TS value in some Hytera XPT PDUs
|
||||||
|
if (is_xpt) pf = 0;
|
||||||
|
|
||||||
|
//check protect flag
|
||||||
|
if (pf == 1)
|
||||||
|
{
|
||||||
|
if (type == 1) fprintf (stderr, "%s \n", KRED);
|
||||||
|
if (type == 2) fprintf (stderr, "%s \n", KRED);
|
||||||
|
if (type == 3) fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||||
|
fprintf (stderr, " Protected LC ");
|
||||||
|
goto END_FLCO;
|
||||||
|
}
|
||||||
|
|
||||||
if (IrrecoverableErrors == 0)
|
if (IrrecoverableErrors == 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (slot == 0) state->dmr_flco = flco;
|
||||||
|
else state->dmr_flcoR = flco;
|
||||||
|
|
||||||
|
//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) )
|
||||||
|
{
|
||||||
|
flco = flco - 0x10;
|
||||||
|
fid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
//Embedded Talker Alias Header Only (format and len storage)
|
//Embedded Talker Alias Header Only (format and len storage)
|
||||||
if (type == 3 && flco == 0x04)
|
if ( (fid == 0 || fid == 0x68) && type == 3 && flco == 0x04)
|
||||||
{
|
{
|
||||||
dmr_embedded_alias_header(opts, state, lc_bits);
|
dmr_embedded_alias_header(opts, state, lc_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Embedded Talker Alias Header (continuation) and Blocks
|
//Embedded Talker Alias Header (continuation) and Blocks
|
||||||
if (type == 3 && flco > 0x3 && flco < 0x08)
|
if ( (fid == 0 || fid == 0x68) && type == 3 && flco > 0x03 && flco < 0x08)
|
||||||
{
|
{
|
||||||
is_alias = 1;
|
is_alias = 1;
|
||||||
dmr_embedded_alias_blocks(opts, state, lc_bits);
|
dmr_embedded_alias_blocks(opts, state, lc_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Embedded GPS
|
//Embedded GPS
|
||||||
if (type == 3 && flco == 0x08)
|
if ( (fid == 0 || fid == 0x68) && fid == 0 && type == 3 && flco == 0x08)
|
||||||
{
|
{
|
||||||
is_gps = 1;
|
is_gps = 1;
|
||||||
if (fid == 0) dmr_embedded_gps(opts, state, lc_bits); //issues with fid 0x10 and flco 0x08
|
dmr_embedded_gps(opts, state, lc_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//look for Cap+ on VLC header, then set source and/or rest channel appropriately
|
||||||
|
if (type == 1 && fid == 0x10 && (flco == 0x04 || flco == 0x07) ) //0x07 appears to be a cap+ txi private call
|
||||||
|
{
|
||||||
|
is_cap_plus = 1;
|
||||||
|
capsite = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 4); //don't believe so
|
||||||
|
restchannel = (int)ConvertBitIntoBytes(&lc_bits[52], 4); //
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unknown CapMax/Moto Things
|
||||||
|
if (fid == 0x10 && (flco == 0x08 || flco == 0x28))
|
||||||
|
{
|
||||||
|
//NOTE: fid 0x10 and flco 0x08 (emb) produces a lot of 'zero' bytes
|
||||||
|
//this has been observed to happen often on CapMax systems, so I believe it could be some CapMax 'thing'
|
||||||
|
//Unknown Link Control - FLCO=0x08 FID=0x10 SVC=0xC1 or FLCO=0x08 FID=0x10 SVC=0xC0 <- probably no SVC bits in the lc
|
||||||
|
//flco 0x28 has also been observed lately but the tg and src values don't match
|
||||||
|
//another flco 0x10 does seem to match, so is probably capmax group call flco
|
||||||
|
if (type == 1) fprintf (stderr, "%s \n", KCYN);
|
||||||
|
if (type == 2) fprintf (stderr, "%s \n", KCYN);
|
||||||
|
if (type == 3) fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||||
|
fprintf (stderr, " Motorola");
|
||||||
|
unk = 1;
|
||||||
|
goto END_FLCO;
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.1.1.1 Terminator Data Link Control PDU - ETSI TS 102 361-3 V1.2.1 (2013-07)
|
||||||
|
if (type == 2 && flco == 0x30)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s \n", KRED);
|
||||||
|
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||||
|
fprintf (stderr, " Data Terminator (TD_LC) ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//reset data header format storage
|
||||||
|
state->data_header_format[slot] = 7;
|
||||||
|
//flag off data header validity
|
||||||
|
state->data_header_valid[slot] = 0;
|
||||||
|
//flag off conf data flag
|
||||||
|
state->data_conf_data[slot] = 0;
|
||||||
|
|
||||||
|
goto END_FLCO;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unknown Tait Things
|
||||||
|
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
|
||||||
|
if (type == 1) fprintf (stderr, "%s \n", KCYN);
|
||||||
|
if (type == 2) fprintf (stderr, "%s \n", KCYN);
|
||||||
|
if (type == 3) fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||||
|
fprintf (stderr, " Tait");
|
||||||
|
unk = 1;
|
||||||
|
goto END_FLCO;
|
||||||
|
}
|
||||||
|
|
||||||
|
//look for any Hytera XPT system, adjust TG to 16-bit allocation
|
||||||
|
//Groups use only 8 out of 16, but 16 always seems to be allocated
|
||||||
|
//private calls use 16-bit target values hashed to 8-bit in the site status csbk
|
||||||
|
//the TLC preceeds the VLC for a 'handshake' call setup in XPT
|
||||||
|
|
||||||
|
//truncate if XPT is set
|
||||||
|
if (is_xpt == 1)
|
||||||
|
{
|
||||||
|
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[32], 16); //16-bit allocation
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16); //16-bit allocation
|
||||||
|
|
||||||
|
//the crc8 hash is the value represented in the CSBK when dealing with private calls
|
||||||
|
for (int i = 0; i < 16; i++) target_hash[i] = lc_bits[32+i];
|
||||||
|
tg_hash = crc8 (target_hash, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
//XPT Call 'Grant/Alert' Setup Occurs in TLC with a flco 0x09
|
||||||
|
if (fid == 0x68 && flco == 0x09)
|
||||||
|
{
|
||||||
|
//The CSBK always uses an 8-bit TG/TGT; The White Papers (user manuals) say 8-bit TG and 16-bit SRC addressing
|
||||||
|
//private calls and indiv data calls use a hash of their 8 bit tgt values in the CSBK
|
||||||
|
xpt_int = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4); //This is consistent with an LCN value, not an LSN value
|
||||||
|
xpt_free = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 4); //24 and 4 on 0x09
|
||||||
|
xpt_hand = (uint8_t)ConvertBitIntoBytes(&lc_bits[28], 4); //handshake kind: 0 - ordinary; 1-2 Interrupts; 3-15 reserved;
|
||||||
|
|
||||||
|
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[32], 16); //16-bit allocation
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16); //16-bit allocation
|
||||||
|
|
||||||
|
//the bits that are left behind
|
||||||
|
xpt_res_a = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 4); //after the xpt_int channel, before the free repeater channel -- call being established = 7; call connected = 0; ??
|
||||||
|
xpt_res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 8); //where the first 8 bits of the SRC would be
|
||||||
|
|
||||||
|
//the crc8 hash is the value represented in the CSBK when dealing with private calls
|
||||||
|
for (int i = 0; i < 16; i++) target_hash[i] = lc_bits[32+i];
|
||||||
|
tg_hash = crc8 (target_hash, 16);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s \n", KGRN);
|
||||||
|
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
||||||
|
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X ", flco, fid);
|
||||||
|
fprintf (stderr, "TGT=%u SRC=%u ", target, source);
|
||||||
|
|
||||||
|
fprintf (stderr, "Hytera XPT ");
|
||||||
|
|
||||||
|
//Group ID ranges from 1 to 240; emergency group call ID ranges from 250 to 254; all call ID is 255.
|
||||||
|
if (reserved == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Group "); //according to observation
|
||||||
|
if (target > 248 && target < 255) fprintf (stderr, "Emergency ");
|
||||||
|
if (target == 255) fprintf (stderr, "All ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "Private "); //according to observation
|
||||||
|
fprintf (stderr, "Call Alert "); //Alert or Grant
|
||||||
|
|
||||||
|
//reorganized all the 'extra' data to a second line and added extra verbosity
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n ");
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "%s", KYEL);
|
||||||
|
//only display the hashed tgt value if its a private call and not a group call
|
||||||
|
if (reserved == 0 && opts->payload == 1) fprintf(stderr, "TGT Hash=%d; ", tg_hash);
|
||||||
|
if (opts->payload == 1) fprintf(stderr, "HSK=%X; ", xpt_hand);
|
||||||
|
//extra verbosity on handshake types found in the patent
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Handshake - ");
|
||||||
|
if (xpt_hand == 0) fprintf (stderr, "Ordinary; ");
|
||||||
|
else if (xpt_hand == 1) fprintf (stderr, "Callback/Alarm Interrupt; ");
|
||||||
|
else if (xpt_hand == 2) fprintf (stderr, "Release Channel Interrupt; ");
|
||||||
|
else fprintf (stderr, "Reserved; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (opts->payload == 1)
|
||||||
|
// {
|
||||||
|
// if (xpt_res_a == 0) fprintf (stderr, "Call Connected; ");
|
||||||
|
// if (xpt_res_a == 1) fprintf (stderr, "Data Call Request; ");
|
||||||
|
// if (xpt_res_a == 7) fprintf (stderr, "Voice Call Request; ");
|
||||||
|
// if (xpt_res_a == 2) fprintf (stderr, "Unknown Status; ");
|
||||||
|
// }
|
||||||
|
|
||||||
|
//logical repeater channel, not the logical slot value in the CSBK
|
||||||
|
if (opts->payload == 1) fprintf(stderr, "Call on LCN %d; ", xpt_int); //LCN channel call or 'interrupt' will occur on
|
||||||
|
// if (opts->payload == 1) fprintf(stderr, "RS A[%01X]B[%02X]C[%02X]; ", xpt_res_a, xpt_res_b, xpt_res_c); //leftover bits
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "Free LCN %d; ", xpt_free); //current free repeater LCN channel
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
|
||||||
|
//add string for ncurses terminal display
|
||||||
|
sprintf (state->dmr_site_parms, "Free LCN - %d ", xpt_free);
|
||||||
|
|
||||||
|
is_xpt = 1;
|
||||||
|
goto END_FLCO;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Hytera XPT 'Others' -- moved the patent opcodes here as well for now
|
||||||
|
if ( fid == 0x68 && (flco == 0x13 || flco == 0x31 || flco == 0x2E || flco == 0x2F) )
|
||||||
|
{
|
||||||
|
if (type == 1) fprintf (stderr, "%s \n", KCYN);
|
||||||
|
if (type == 2) fprintf (stderr, "%s \n", KCYN);
|
||||||
|
if (type == 3) fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||||
|
fprintf (stderr, " Hytera ");
|
||||||
|
unk = 1;
|
||||||
|
goto END_FLCO;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unknown other manufacturer or OTA ENC, etc.
|
||||||
|
//removed tait from the list, added hytera 0x08
|
||||||
|
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08)
|
||||||
|
{
|
||||||
|
if (type == 1) fprintf (stderr, "%s \n", KYEL);
|
||||||
|
if (type == 2) fprintf (stderr, "%s \n", KYEL);
|
||||||
|
if (type == 3) fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||||
|
fprintf (stderr, " Unknown LC ");
|
||||||
|
unk = 1;
|
||||||
|
goto END_FLCO;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//will want to continue to observe for different flco and fid combinations to find out their meaning
|
//will want to continue to observe for different flco and fid combinations to find out their meaning
|
||||||
//Standard Addressing/Cap+ Addressing (trying to avoid embedded alias and gps, etc)
|
|
||||||
if(IrrecoverableErrors == 0 && is_alias == 0 && is_gps == 0)
|
if(IrrecoverableErrors == 0 && is_alias == 0 && is_gps == 0)
|
||||||
{
|
{
|
||||||
//set overarching manufacturer in use when non-standard feature id set is up
|
//set overarching manufacturer in use when non-standard feature id set is up
|
||||||
//may not want to set moto 0x10 here either, lots of radios use that set as well
|
|
||||||
if (fid != 0) state->dmr_mfid = fid;
|
if (fid != 0) state->dmr_mfid = fid;
|
||||||
|
|
||||||
if (type != 2) //VLC and EMB, set our target, source, so, and fid per channel
|
if (type != 2) //VLC and EMB, set our target, source, so, and fid per channel
|
||||||
@@ -119,6 +330,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
|||||||
// state->lastsrc = 0;
|
// state->lastsrc = 0;
|
||||||
state->payload_algid = 0;
|
state->payload_algid = 0;
|
||||||
state->payload_mi = 0;
|
state->payload_mi = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
}
|
}
|
||||||
if (state->currentslot == 1)
|
if (state->currentslot == 1)
|
||||||
{
|
{
|
||||||
@@ -128,7 +340,9 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
|||||||
// state->lastsrcR = 0;
|
// state->lastsrcR = 0;
|
||||||
state->payload_algidR = 0;
|
state->payload_algidR = 0;
|
||||||
state->payload_miR = 0;
|
state->payload_miR = 0;
|
||||||
|
state->payload_keyidR = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -148,16 +362,40 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
|||||||
|
|
||||||
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
||||||
fprintf(stderr, "TGT=%u SRC=%u ", target, source);
|
fprintf(stderr, "TGT=%u SRC=%u ", target, source);
|
||||||
|
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);
|
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
|
||||||
|
|
||||||
if (flco == 3) //UU_V_Ch_Usr
|
//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)
|
||||||
|
|
||||||
|
if (fid == 0x68) sprintf (state->call_string[slot], " Hytera ");
|
||||||
|
|
||||||
|
else if (flco == 0x4 || flco == 0x5 || flco == 0x7 || flco == 0x23) //Cap+ Things
|
||||||
{
|
{
|
||||||
sprintf (state->call_string[slot], " Private");
|
sprintf (state->call_string[slot], " Cap+");
|
||||||
|
fprintf (stderr, "Cap+ ");
|
||||||
|
if (flco == 0x4)
|
||||||
|
{
|
||||||
|
strcat (state->call_string[slot], " Grp");
|
||||||
|
fprintf (stderr, "Group ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcat (state->call_string[slot], " Pri");
|
||||||
|
fprintf (stderr, "Private ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (flco == 0x3) //UU_V_Ch_Usr
|
||||||
|
{
|
||||||
|
sprintf (state->call_string[slot], " Private ");
|
||||||
fprintf (stderr, "Private ");
|
fprintf (stderr, "Private ");
|
||||||
}
|
}
|
||||||
else //Grp_V_Ch_Usr
|
else //Grp_V_Ch_Usr -- still valid on hytera VLC
|
||||||
{
|
{
|
||||||
sprintf (state->call_string[slot], " Group");
|
sprintf (state->call_string[slot], " Group ");
|
||||||
fprintf (stderr, "Group ");
|
fprintf (stderr, "Group ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,70 +417,71 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
|||||||
//REMUS! Uncomment Line Below if desired
|
//REMUS! Uncomment Line Below if desired
|
||||||
// else strcat (state->call_string[slot], " ");
|
// else strcat (state->call_string[slot], " ");
|
||||||
|
|
||||||
/* Check the "Service Option" bits */
|
//Motorola FID 0x10 Only
|
||||||
if(so & 0x30)
|
if (fid == 0x10)
|
||||||
{
|
{
|
||||||
/* Experimentally determined with DSD+,
|
/* Check the "Service Option" bits */
|
||||||
* is equal to 0x2, this is a TXI call */
|
if(so & 0x20)
|
||||||
if((so & 0x30) == 0x20)
|
|
||||||
{
|
{
|
||||||
//REMUS! Uncomment Line Below if desired
|
//REMUS! Uncomment Line Below if desired
|
||||||
// strcat (state->call_string[slot], " TXI");
|
// strcat (state->call_string[slot], " TXI");
|
||||||
fprintf(stderr, "TXI ");
|
fprintf(stderr, "TXI ");
|
||||||
}
|
}
|
||||||
else
|
if (so & 0x10)
|
||||||
{
|
{
|
||||||
//REMUS! Uncomment Line Below if desired
|
//REMUS! Uncomment Line Below if desired
|
||||||
// strcat (state->call_string[slot], " RES");
|
// strcat (state->call_string[slot], " RPT");
|
||||||
fprintf(stderr, "Reserved=%d ", (so & 0x30) >> 4);
|
fprintf(stderr, "RPT "); //Short way of saying the next SF's VC6 will be pre-empted/repeat frames for the TXI backwards channel
|
||||||
}
|
}
|
||||||
}
|
if(so & 0x08)
|
||||||
if(so & 0x08)
|
|
||||||
{
|
|
||||||
//REMUS! Uncomment Line Below if desired
|
|
||||||
// strcat (state->call_string[slot], "-BC ");
|
|
||||||
fprintf(stderr, "Broadcast ");
|
|
||||||
}
|
|
||||||
if(so & 0x04)
|
|
||||||
{
|
|
||||||
//REMUS! Uncomment Line Below if desired
|
|
||||||
// strcat (state->call_string[slot], "-OVCM ");
|
|
||||||
fprintf(stderr, "OVCM ");
|
|
||||||
}
|
|
||||||
if(so & 0x03)
|
|
||||||
{
|
|
||||||
if((so & 0x03) == 0x01)
|
|
||||||
{
|
{
|
||||||
//REMUS! Uncomment Line Below if desired
|
//REMUS! Uncomment Line Below if desired
|
||||||
// strcat (state->call_string[slot], "-P1");
|
// strcat (state->call_string[slot], "-BC ");
|
||||||
fprintf(stderr, "Priority 1 ");
|
fprintf(stderr, "Broadcast ");
|
||||||
}
|
}
|
||||||
else if((so & 0x03) == 0x02)
|
if(so & 0x04)
|
||||||
{
|
{
|
||||||
//REMUS! Uncomment Line Below if desired
|
//REMUS! Uncomment Line Below if desired
|
||||||
// strcat (state->call_string[slot], "-P2");
|
// strcat (state->call_string[slot], "-OVCM ");
|
||||||
fprintf(stderr, "Priority 2 ");
|
fprintf(stderr, "OVCM ");
|
||||||
}
|
}
|
||||||
else if((so & 0x03) == 0x03)
|
if(so & 0x03)
|
||||||
{
|
{
|
||||||
//REMUS! Uncomment Line Below if desired
|
if((so & 0x03) == 0x01)
|
||||||
// strcat (state->call_string[slot], "-P3");
|
{
|
||||||
fprintf(stderr, "Priority 3 ");
|
//REMUS! Uncomment Line Below if desired
|
||||||
}
|
// strcat (state->call_string[slot], "-P1");
|
||||||
else /* We should never go here */
|
fprintf(stderr, "Priority 1 ");
|
||||||
{
|
}
|
||||||
//REMUS! Uncomment Line Below if desired
|
else if((so & 0x03) == 0x02)
|
||||||
// strcat (state->call_string[slot], " ");
|
{
|
||||||
fprintf(stderr, "No Priority ");
|
//REMUS! Uncomment Line Below if desired
|
||||||
}
|
// strcat (state->call_string[slot], "-P2");
|
||||||
|
fprintf(stderr, "Priority 2 ");
|
||||||
|
}
|
||||||
|
else if((so & 0x03) == 0x03)
|
||||||
|
{
|
||||||
|
//REMUS! Uncomment Line Below if desired
|
||||||
|
// strcat (state->call_string[slot], "-P3");
|
||||||
|
fprintf(stderr, "Priority 3 ");
|
||||||
|
}
|
||||||
|
else /* We should never go here */
|
||||||
|
{
|
||||||
|
//REMUS! Uncomment Line Below if desired
|
||||||
|
// strcat (state->call_string[slot], " ");
|
||||||
|
fprintf(stderr, "No Priority ");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//should rework this back into the upper portion
|
||||||
|
if (fid == 0x68) fprintf (stderr, "Hytera ");
|
||||||
|
if (is_xpt) fprintf (stderr, "XPT ");
|
||||||
|
if (fid == 0x68 && flco == 0x00) fprintf (stderr, "Group ");
|
||||||
|
if (fid == 0x68 && flco == 0x03) fprintf (stderr, "Private ");
|
||||||
|
|
||||||
fprintf(stderr, "Call ");
|
fprintf(stderr, "Call ");
|
||||||
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
if (CRCCorrect == 1) ; //CRCCorrect 1 is good, else is bad CRC; no print on good
|
|
||||||
else if(IrrecoverableErrors == 0) fprintf(stderr, "(FEC/RAS)");
|
|
||||||
else fprintf(stderr, "(FEC/CRC ERR)");
|
|
||||||
|
|
||||||
//check Cap+ rest channel info if available and good fec
|
//check Cap+ rest channel info if available and good fec
|
||||||
if (is_cap_plus == 1)
|
if (is_cap_plus == 1)
|
||||||
@@ -250,7 +489,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
|||||||
if (restchannel != -1)
|
if (restchannel != -1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s ", KYEL);
|
fprintf (stderr, "%s ", KYEL);
|
||||||
fprintf (stderr, "Cap+ R-Ch %d", restchannel);
|
fprintf (stderr, "Rest LSN: %d", restchannel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,25 +507,81 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->K != 0 && fid == 0x10 && so & 0x40)
|
//BUGFIX: Include slot and algid so we don't accidentally print more than one loaded key
|
||||||
|
//this can happen on Missing PI header and LE when the keyloader has loaded a TG/Hash key and an RC4 key simultandeously
|
||||||
|
//subsequennt EMB would print two key values until call cleared out
|
||||||
|
|
||||||
|
if (state->K != 0 && fid == 0x10 && so & 0x40 && slot == 0 && state->payload_algid == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, "Key %lld ", state->K);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->K != 0 && fid == 0x10 && so & 0x40 && slot == 1 && state->payload_algidR == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, "Key %lld ", state->K);
|
fprintf (stderr, "Key %lld ", state->K);
|
||||||
fprintf (stderr, "%s ", KNRM);
|
fprintf (stderr, "%s ", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->K1 != 0 && fid == 0x68 && so & 0x40)
|
if (state->K1 != 0 && fid == 0x68 && so & 0x40 && slot == 0 && state->payload_algid == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
if (state->K2 != 0) fprintf (stderr, "\n ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, " Key %010llX ", state->K1);
|
fprintf (stderr, "Key %010llX ", state->K1);
|
||||||
if (state->K2 != 0) fprintf (stderr, "%016llX ", state->K2);
|
if (state->K2 != 0) fprintf (stderr, "%016llX ", state->K2);
|
||||||
if (state->K4 != 0) fprintf (stderr, "%016llX %016llX", state->K3, state->K4);
|
if (state->K4 != 0) fprintf (stderr, "%016llX %016llX", state->K3, state->K4);
|
||||||
fprintf (stderr, "%s ", KNRM);
|
fprintf (stderr, "%s ", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state->K1 != 0 && fid == 0x68 && so & 0x40 && slot == 1 && state->payload_algidR == 0)
|
||||||
|
{
|
||||||
|
if (state->K2 != 0) fprintf (stderr, "\n ");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, "Key %010llX ", state->K1);
|
||||||
|
if (state->K2 != 0) fprintf (stderr, "%016llX ", state->K2);
|
||||||
|
if (state->K4 != 0) fprintf (stderr, "%016llX %016llX", state->K3, state->K4);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == 0 && state->payload_algid == 0x21 && state->R != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, "Key %010llX ", state->R);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == 1 && state->payload_algidR == 0x21 && state->RR != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, "Key %010llX ", state->RR);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
END_FLCO:
|
||||||
|
|
||||||
|
//blank the call string here if its a TLC
|
||||||
|
if (type == 2) sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||||
|
|
||||||
|
if (unk == 1 || pf == 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " FLCO=0x%02X FID=0x%02X ", flco, fid);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(IrrecoverableErrors != 0)
|
||||||
|
{
|
||||||
|
if (type != 3) fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||||
|
fprintf (stderr, " FLCO FEC ERR ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//externalized dmr cach - tact and slco fragment handling
|
//externalized dmr cach - tact and slco fragment handling
|
||||||
@@ -540,8 +835,17 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
|||||||
//Con+
|
//Con+
|
||||||
uint8_t con_netid = (uint8_t)ConvertBitIntoBytes(&slco_bits[8], 8);
|
uint8_t con_netid = (uint8_t)ConvertBitIntoBytes(&slco_bits[8], 8);
|
||||||
uint8_t con_siteid = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 8);
|
uint8_t con_siteid = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 8);
|
||||||
|
|
||||||
//Cap+
|
//Cap+
|
||||||
uint8_t restchannel = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 8);
|
uint8_t capsite = (uint8_t)ConvertBitIntoBytes(&slco_bits[22], 3); //Seems more consistent
|
||||||
|
uint8_t restchannel = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 4);
|
||||||
|
uint8_t cap_reserved = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 2); //significant value?
|
||||||
|
|
||||||
|
//Hytera XPT
|
||||||
|
uint8_t xpt_free = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 4); //free repeater
|
||||||
|
//the next two per SDRTrunk, but only 0 values ever observed here
|
||||||
|
uint8_t xpt_pri = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 4); //priority repeater
|
||||||
|
uint8_t xpt_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 8); //priority TG hash
|
||||||
|
|
||||||
//initial line break
|
//initial line break
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
@@ -549,24 +853,20 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
|||||||
|
|
||||||
if (slco == 0x2) //C_SYS_Parms
|
if (slco == 0x2) //C_SYS_Parms
|
||||||
{
|
{
|
||||||
if (n != 0) fprintf (stderr, " C_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d - Reg Req: %d - CSC: %d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, reg, csc);
|
if (n != 0) fprintf (stderr, " SLC_C_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d - Reg Req: %d - CSC: %d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, reg, csc);
|
||||||
else fprintf (stderr, " C_SYS_PARMS - %s - Net ID: %d Site ID: %d - Reg Req: %d - CSC: %d ", model_str, net, site, reg, csc);
|
else fprintf (stderr, " SLC_C_SYS_PARMS - %s - Net ID: %d Site ID: %d - Reg Req: %d - Capacity Max ", model_str, net, site, reg);
|
||||||
|
|
||||||
//add string for ncurses terminal display
|
//add string for ncurses terminal display
|
||||||
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
||||||
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
||||||
|
|
||||||
//if using rigctl we can set an unknown cc frequency by polling rigctl for the current frequency
|
//if using rigctl we can set an unknown cc frequency by polling rigctl for the current frequency
|
||||||
|
|
||||||
if (opts->use_rigctl == 1 && state->p25_cc_freq == 0) //if not set from channel map 0
|
if (opts->use_rigctl == 1 && state->p25_cc_freq == 0) //if not set from channel map 0
|
||||||
{
|
{
|
||||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||||
}
|
}
|
||||||
|
|
||||||
//nullify any previous branding sub (bugfix for bad assignments or system type switching)
|
|
||||||
//sprintf(state->dmr_branding_sub, "%s", "");
|
|
||||||
|
|
||||||
//debug print
|
//debug print
|
||||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&slco_bits[4], 14);
|
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&slco_bits[4], 14);
|
||||||
//fprintf (stderr, "\n SYSCODE: %014b", syscode);
|
//fprintf (stderr, "\n SYSCODE: %014b", syscode);
|
||||||
@@ -575,15 +875,13 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
|||||||
}
|
}
|
||||||
else if (slco == 0x3) //P_SYS_Parms
|
else if (slco == 0x3) //P_SYS_Parms
|
||||||
{
|
{
|
||||||
fprintf (stderr, " P_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d - Comp CC: %d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, reg);
|
if (n != 0) fprintf (stderr, " SLC_P_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d - Comp CC: %d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, reg);
|
||||||
|
else fprintf (stderr, " SLC_P_SYS_PARMS - %s - Net ID: %d Site ID: %d - Capacity Max", model_str, net, site);
|
||||||
|
|
||||||
//add string for ncurses terminal display
|
//add string for ncurses terminal display
|
||||||
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
||||||
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
||||||
|
|
||||||
//nullify any previous branding sub (bugfix for bad assignments or system type switching)
|
|
||||||
//sprintf(state->dmr_branding_sub, "%s", "");
|
|
||||||
|
|
||||||
//debug print
|
//debug print
|
||||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&slco_bits[4], 14);
|
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&slco_bits[4], 14);
|
||||||
//fprintf (stderr, "\n SYSCODE: %014b", syscode);
|
//fprintf (stderr, "\n SYSCODE: %014b", syscode);
|
||||||
@@ -596,18 +894,12 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
|||||||
fprintf (stderr, " SLCO Activity Update TS1: %X Hash: %02X TS2: %X Hash: %02X", ts1_act, ts1_hash, ts2_act, ts2_hash); //102 361-2 7.1.3.2
|
fprintf (stderr, " SLCO Activity Update TS1: %X Hash: %02X TS2: %X Hash: %02X", ts1_act, ts1_hash, ts2_act, ts2_hash); //102 361-2 7.1.3.2
|
||||||
else if (slco == 0x9)
|
else if (slco == 0x9)
|
||||||
{
|
{
|
||||||
state->dmr_mfid = 0x10;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf (state->dmr_branding_sub, "%s", "Con+ ");
|
|
||||||
fprintf (stderr, " SLCO Connect Plus Voice Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
|
fprintf (stderr, " SLCO Connect Plus Voice Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
|
||||||
sprintf (state->dmr_site_parms, "%d-%d ", con_netid, con_siteid);
|
sprintf (state->dmr_site_parms, "%d-%d ", con_netid, con_siteid);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (slco == 0xA)
|
else if (slco == 0xA)
|
||||||
{
|
{
|
||||||
state->dmr_mfid = 0x10;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf (state->dmr_branding_sub, "%s", "Con+ ");
|
|
||||||
fprintf (stderr, " SLCO Connect Plus Control Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
|
fprintf (stderr, " SLCO Connect Plus Control Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
|
||||||
sprintf (state->dmr_site_parms, "%d-%d ", con_netid, con_siteid);
|
sprintf (state->dmr_site_parms, "%d-%d ", con_netid, con_siteid);
|
||||||
|
|
||||||
@@ -621,19 +913,24 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
|||||||
|
|
||||||
else if (slco == 0xF)
|
else if (slco == 0xF)
|
||||||
{
|
{
|
||||||
state->dmr_mfid = 0x10;
|
fprintf (stderr, " SLCO Capacity Plus Site: %d - Rest Channel %d - RS: %02X", capsite, restchannel, cap_reserved);
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
|
||||||
fprintf (stderr, " SLCO Capacity Plus Rest Channel %d", restchannel);
|
|
||||||
state->dmr_rest_channel = restchannel;
|
|
||||||
//assign to cc freq if available
|
//assign to cc freq if available
|
||||||
if (state->trunk_chan_map[restchannel] != 0)
|
if (state->trunk_chan_map[restchannel] != 0)
|
||||||
{
|
{
|
||||||
state->p25_cc_freq = state->trunk_chan_map[restchannel];
|
state->p25_cc_freq = state->trunk_chan_map[restchannel];
|
||||||
}
|
}
|
||||||
|
|
||||||
//nullify any previous TIII data (bugfix for bad assignments or system type switching)
|
}
|
||||||
sprintf(state->dmr_site_parms, "%s", "");
|
else if (slco == 0x08)
|
||||||
|
{
|
||||||
|
//The Priority Repeater and Priority Hash values stem from SDRTrunk, but I've never seen these values not be zeroes
|
||||||
|
fprintf (stderr, " SLCO Hytera XPT - Free LCN %d - PRI LCN %d - PRI HASH: %02X", xpt_free, xpt_pri, xpt_hash);
|
||||||
|
//NOTE: on really busy systems, this free repeater assignment can lag due to the 4 TS requirment to get SLC
|
||||||
|
// fprintf (stderr, " SLCO Hytera XPT - Free LCN %d ", xpt_free);
|
||||||
|
sprintf (state->dmr_branding_sub, "XPT ");
|
||||||
|
|
||||||
|
//add string for ncurses terminal display
|
||||||
|
sprintf (state->dmr_site_parms, "Free LCN - %d ", xpt_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
else fprintf (stderr, " SLCO Unknown - %d ", slco);
|
else fprintf (stderr, " SLCO Unknown - %d ", slco);
|
||||||
@@ -645,6 +942,7 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
|||||||
{
|
{
|
||||||
fprintf (stderr, "[%02X]", slco_bytes[i]);
|
fprintf (stderr, "[%02X]", slco_bytes[i]);
|
||||||
}
|
}
|
||||||
|
fprintf (stderr, "\n"); //if its a voice frame, we need the line break
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
@@ -685,6 +983,10 @@ void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_b
|
|||||||
uint8_t len = state->dmr_alias_len[slot];
|
uint8_t len = state->dmr_alias_len[slot];
|
||||||
uint8_t start; //starting position depends on context of block and format
|
uint8_t start; //starting position depends on context of block and format
|
||||||
|
|
||||||
|
//Cap Max Variation
|
||||||
|
uint8_t fid = (uint8_t)ConvertBitIntoBytes(&lc_bits[8], 8);
|
||||||
|
if (fid == 0x10) block = block - 0x10; //CapMax adds 0x10 to its FLCO (block num) in its Embedded Aliasing
|
||||||
|
|
||||||
//there is some issue with the next three lines of code that prevent proper assignments, not sure what.
|
//there is some issue with the next three lines of code that prevent proper assignments, not sure what.
|
||||||
//if (block > 4) start = 16;
|
//if (block > 4) start = 16;
|
||||||
//else if (block == 4 && format > 0) start = 23; //8-bit and 16-bit chars
|
//else if (block == 4 && format > 0) start = 23; //8-bit and 16-bit chars
|
||||||
|
|||||||
334
src/dmr_le.c
334
src/dmr_le.c
@@ -13,6 +13,23 @@ void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc,
|
|||||||
{
|
{
|
||||||
|
|
||||||
uint8_t slot = state->currentslot;
|
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
|
//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][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
|
||||||
@@ -34,6 +51,12 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|||||||
uint64_t go_test = 0;
|
uint64_t go_test = 0;
|
||||||
uint64_t mi_corrected = 0;
|
uint64_t mi_corrected = 0;
|
||||||
uint64_t go_corrected = 0;
|
uint64_t go_corrected = 0;
|
||||||
|
|
||||||
|
uint8_t mi_bits[36];
|
||||||
|
memset (mi_bits, 0, sizeof(mi_bits));
|
||||||
|
uint8_t mi_crc_cmp = 0;
|
||||||
|
uint8_t mi_crc_ext = 1;
|
||||||
|
uint8_t mi_crc_ok = 0;
|
||||||
|
|
||||||
mi_test = (state->late_entry_mi_fragment[slot][1][0] << 32L) | (state->late_entry_mi_fragment[slot][2][0] << 28) | (state->late_entry_mi_fragment[slot][3][0] << 24) |
|
mi_test = (state->late_entry_mi_fragment[slot][1][0] << 32L) | (state->late_entry_mi_fragment[slot][2][0] << 28) | (state->late_entry_mi_fragment[slot][3][0] << 24) |
|
||||||
(state->late_entry_mi_fragment[slot][1][1] << 20) | (state->late_entry_mi_fragment[slot][2][1] << 16) | (state->late_entry_mi_fragment[slot][3][1] << 12) |
|
(state->late_entry_mi_fragment[slot][1][1] << 20) | (state->late_entry_mi_fragment[slot][2][1] << 16) | (state->late_entry_mi_fragment[slot][3][1] << 12) |
|
||||||
@@ -60,12 +83,22 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|||||||
mi_corrected |= mi_go_bits[i];
|
mi_corrected |= mi_go_bits[i];
|
||||||
go_corrected = go_corrected << 1;
|
go_corrected = go_corrected << 1;
|
||||||
go_corrected |= mi_go_bits[i+12];
|
go_corrected |= mi_go_bits[i+12];
|
||||||
|
//manipulate for crc check
|
||||||
|
mi_bits[i+(j*12)] = mi_go_bits[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int mi_final = 0;
|
int mi_final = 0;
|
||||||
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
|
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
mi_crc_ext = (uint8_t)ConvertBitIntoBytes(&mi_bits[32], 4);
|
||||||
|
mi_crc_cmp = crc4(mi_bits, 32);
|
||||||
|
if (mi_crc_ext == mi_crc_cmp)
|
||||||
|
mi_crc_ok = 1;
|
||||||
|
|
||||||
|
//debug -- working now
|
||||||
|
// fprintf (stderr, " LE MI: %09llX; CRC EXT: %X; CRC CMP: %X; \n", mi_corrected, mi_crc_ext, mi_crc_cmp);
|
||||||
|
|
||||||
if (g[0] && g[1] && g[2])
|
if (g[0] && g[1] && g[2])
|
||||||
{
|
{
|
||||||
if (slot == 0 && state->payload_algid != 0)
|
if (slot == 0 && state->payload_algid != 0)
|
||||||
@@ -73,9 +106,18 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->payload_mi != mi_final)
|
if (state->payload_mi != mi_final)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KCYN);
|
fprintf (stderr, "%s", KCYN);
|
||||||
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
|
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X ", state->payload_mi, mi_final);
|
||||||
state->payload_mi = mi_final;
|
state->payload_mi = mi_final;
|
||||||
|
if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)");
|
||||||
|
else fprintf (stderr, "(CRC ERR)");
|
||||||
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
//run afterwards, or le verification won't match up properly
|
||||||
|
if (state->payload_algid != 0x21)
|
||||||
|
{
|
||||||
|
LFSR64 (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slot == 1 && state->payload_algidR != 0)
|
if (slot == 1 && state->payload_algidR != 0)
|
||||||
@@ -83,9 +125,18 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->payload_miR != mi_final)
|
if (state->payload_miR != mi_final)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KCYN);
|
fprintf (stderr, "%s", KCYN);
|
||||||
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
|
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X ", state->payload_miR, mi_final);
|
||||||
state->payload_miR = mi_final;
|
state->payload_miR = mi_final;
|
||||||
|
if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)");
|
||||||
|
else fprintf (stderr, "(CRC ERR)");
|
||||||
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
//run afterwards, or le verification won't match up properly
|
||||||
|
if (state->payload_algidR != 0x21)
|
||||||
|
{
|
||||||
|
LFSR64 (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,11 +155,13 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
|||||||
state->DMRvcL = 0;
|
state->DMRvcL = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algid >= 0x21)
|
if (state->payload_algid == 0x21)
|
||||||
{
|
{
|
||||||
LFSR (state);
|
LFSR (state);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
|
||||||
|
|
||||||
}
|
}
|
||||||
if (state->currentslot == 1)
|
if (state->currentslot == 1)
|
||||||
{
|
{
|
||||||
@@ -119,11 +172,13 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
|||||||
state->DMRvcR = 0;
|
state->DMRvcR = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algidR >= 0x21)
|
if (state->payload_algidR == 0x21)
|
||||||
{
|
{
|
||||||
LFSR (state);
|
LFSR (state);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -134,6 +189,273 @@ void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
|
|||||||
state->dropR = 256;
|
state->dropR = 256;
|
||||||
state->DMRvcL = 0;
|
state->DMRvcL = 0;
|
||||||
state->DMRvcR = 0;
|
state->DMRvcR = 0;
|
||||||
state->payload_miP = 0;
|
state->payload_miP = 0;
|
||||||
|
state->payload_miN = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//handle Single Burst (Voice Burst F) or Reverse Channel Signalling
|
||||||
|
void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t sbrc_interleaved[32];
|
||||||
|
uint8_t sbrc_return[32];
|
||||||
|
uint8_t sbrc_retcrc[32]; //crc significant bits of the return for SB
|
||||||
|
memset (sbrc_interleaved, 0, sizeof(sbrc_interleaved));
|
||||||
|
memset (sbrc_return, 0, sizeof(sbrc_return));
|
||||||
|
memset (sbrc_retcrc, 0, sizeof(sbrc_retcrc));
|
||||||
|
uint32_t irr_err = 0;
|
||||||
|
uint32_t sbrc_hex = 0;
|
||||||
|
uint16_t crc_extracted = 7777;
|
||||||
|
uint16_t crc_computed = 9999;
|
||||||
|
uint8_t crc7_okay = 0; //RC
|
||||||
|
uint8_t crc3_okay = 0; //TXI
|
||||||
|
uint8_t txi = 0; //SEE: https://patents.google.com/patent/US8271009B2
|
||||||
|
|
||||||
|
//NOTE: Any previous mentions to Cap+ in this area may have been in error,
|
||||||
|
//The signalling observed here was actually TXI information, not Cap+ Specifically
|
||||||
|
|
||||||
|
//check to see if this a TXI system
|
||||||
|
if (slot == 0 && state->dmr_so & 0x20) txi = 1;
|
||||||
|
if (slot == 1 && state->dmr_soR & 0x20) txi = 1;
|
||||||
|
|
||||||
|
// 9.3.2 Pre-emption and power control Indicator (PI)
|
||||||
|
// 0 - The embedded signalling carries information associated to the same logical channel or the Null embedded message
|
||||||
|
// 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];
|
||||||
|
//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
|
||||||
|
if (power == 1) irr_err = BPTC_16x2_Extract_Data(sbrc_interleaved, sbrc_return, 1);
|
||||||
|
//bad emb burst, never set a valid power indicator value (probably 9)
|
||||||
|
if (power > 1) 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];
|
||||||
|
|
||||||
|
//RC Channel CRC 7 Mask = 0x7A; CRC bits are used as privacy indicators on
|
||||||
|
//Single Voice Burst F (see below), other moto values seem to exist there as well -- See TXI patent
|
||||||
|
if (power == 1) //RC
|
||||||
|
{
|
||||||
|
crc_extracted = 0;
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
crc_extracted = crc_extracted << 1;
|
||||||
|
crc_extracted = crc_extracted | sbrc_return[i+4];
|
||||||
|
}
|
||||||
|
crc_extracted = crc_extracted ^ 0x7A;
|
||||||
|
crc_computed = crc7((uint8_t *) sbrc_return, 7);
|
||||||
|
if (crc_extracted == crc_computed) crc7_okay = 1;
|
||||||
|
}
|
||||||
|
else //if (txi == 1) //if TXI -- but TXI systems also carry the non-crc protected ENC identifiers
|
||||||
|
{
|
||||||
|
crc_extracted = 0;
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
crc_extracted = crc_extracted << 1;
|
||||||
|
crc_extracted = crc_extracted | sbrc_return[i]; //first 3 most significant bits
|
||||||
|
}
|
||||||
|
crc_computed = crc3((uint8_t *) sbrc_retcrc, 8); //working now seems consistent as well
|
||||||
|
if (crc_extracted == crc_computed) crc3_okay = 1;
|
||||||
|
// fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
|
||||||
|
}
|
||||||
|
// else //crc_okay = 0; //CRC invalid / not available when is ENC Identifiers (I really hate that)
|
||||||
|
|
||||||
|
for(i = 0; i < 11; i++)
|
||||||
|
{
|
||||||
|
sbrc_hex = sbrc_hex << 1;
|
||||||
|
sbrc_hex |= sbrc_return[i] & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide the sb/rc behind the payload printer, won't be useful to most people
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
if (power == 0) fprintf (stderr, " SB: ");
|
||||||
|
if (power == 1) fprintf (stderr, " RC: ");
|
||||||
|
for(i = 0; i < 11; i++)
|
||||||
|
fprintf (stderr, "%d", sbrc_return[i]);
|
||||||
|
fprintf (stderr, " - %03X; ", sbrc_hex);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
// if (crc_okay == 0) //forego this since the crc can vary or not be used at all
|
||||||
|
// {
|
||||||
|
// fprintf (stderr, "%s", KRED);
|
||||||
|
// fprintf (stderr, " (CRC ERR)");
|
||||||
|
// fprintf (stderr, "%s", KNRM);
|
||||||
|
// fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
|
||||||
|
// }
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sbrc_opcode = sbrc_hex & 0x7; //opcode and alg the same bits, but the alg is present when CRC is bad (I know they are limited on bits, but I hate that idea)
|
||||||
|
uint8_t alg = sbrc_hex & 0x7; //SEE: https://patents.google.com/patent/EP2347540B1/en
|
||||||
|
uint8_t key = (sbrc_hex >> 3) & 0xFF;
|
||||||
|
uint8_t txi_delay = (sbrc_hex >> 3) & 0x1F; //middle five are the 'delay' value on a TXI system
|
||||||
|
|
||||||
|
//Note: AES-256 Key 1 will pass a CRC3 due to its bit arrangement vs the CRC poly 1101
|
||||||
|
// if (irr_err == 0 && sbrc_hex = 0xD) crc3_okay = 0; //SB: 00000001101
|
||||||
|
|
||||||
|
//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?)
|
||||||
|
|
||||||
|
if (opts->dmr_le == 1) //this will now require a user to switch it on or off until more testing/figuring can be done
|
||||||
|
{
|
||||||
|
if (irr_err != 0) fprintf (stderr, "\n%s SLOT %d SB/RC (FEC ERR) %d %s \n", KRED, slot, irr_err, KNRM);
|
||||||
|
if (irr_err == 0)
|
||||||
|
{
|
||||||
|
if (sbrc_hex == 0) ; //NULL, do nothing
|
||||||
|
|
||||||
|
//else if (placeholder for future conditions)
|
||||||
|
// {
|
||||||
|
// placeholder for future conditions
|
||||||
|
// }
|
||||||
|
|
||||||
|
else if (crc7_okay == 1)
|
||||||
|
{
|
||||||
|
//do something with the reverse channel information
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the call is interruptable (TXI) and the crc3 is okay and TXI Opcode
|
||||||
|
else if (crc3_okay == 1 && (sbrc_opcode == 0 || sbrc_opcode == 3) )
|
||||||
|
{
|
||||||
|
//opcodes -- 0 (NULL), BR Delay (3)
|
||||||
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " TXI Op: %X -", sbrc_opcode);
|
||||||
|
if (sbrc_opcode == 0) fprintf (stderr, " Null; ");
|
||||||
|
else if (sbrc_opcode == 3)
|
||||||
|
{
|
||||||
|
if (txi_delay != 0)
|
||||||
|
fprintf (stderr, " BR Delay: %d - %d ms;", txi_delay, txi_delay * 30); //could also indicate number of superframes until next VC6 pre-emption
|
||||||
|
else fprintf (stderr, "BR Delay: Irrelevant / Send at any time;");
|
||||||
|
|
||||||
|
//alignment of inbound backwards channel and outbound burst
|
||||||
|
if (txi_delay == 2) fprintf (stderr, " SF3, Burst E;"); //E is inbound
|
||||||
|
if (txi_delay == 4) fprintf (stderr, " SF3, Burst D;"); //D is inbound
|
||||||
|
if (txi_delay == 6) fprintf (stderr, " SF3, Burst C;"); //C is inbound
|
||||||
|
if (txi_delay == 8) fprintf (stderr, " SF3, Burst B;"); //B is inbound
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " Unk; ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n"); //only during payload
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (sbrc_opcode != 0 && sbrc_opcode != 3) //all that should be left in this field is the potential ENC identifiers
|
||||||
|
{
|
||||||
|
|
||||||
|
if (slot == 0) //may not need the state->errs anymore //&& state->errs < 3
|
||||||
|
{
|
||||||
|
if (state->dmr_so & 0x40 && key != 0 && state->payload_keyid == 0)
|
||||||
|
{
|
||||||
|
//if we aren't forcing a particular alg or privacy key set
|
||||||
|
if (state->M == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " Slot 1");
|
||||||
|
fprintf (stderr, " DMR LE SB ALG ID: 0x%02X KEY ID: 0x%02X", alg + 0x20, key);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||||
|
state->payload_keyid = key;
|
||||||
|
state->payload_algid = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == 1) //may not need the state->errs anymore //&& state->errsR < 3
|
||||||
|
{
|
||||||
|
if (state->dmr_soR & 0x40 && key != 0 && state->payload_keyidR == 0)
|
||||||
|
{
|
||||||
|
//if we aren't forcing a particular alg or privacy key set
|
||||||
|
if (state->M == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " Slot 2");
|
||||||
|
fprintf (stderr, " DMR LE SB ALG ID: 0x%02X KEY ID: 0x%02X", alg + 0x20, key);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||||
|
state->payload_keyidR = key;
|
||||||
|
state->payload_algidR = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SBRC_END:
|
||||||
|
|
||||||
|
//'DSP' output to file -- only RC, or RC and SB?
|
||||||
|
if (power == 1 && opts->use_dsp_output == 1 && sbrc_hex != 0) //if not NULL
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 99 ", slot+1); //'99' is RC designation value
|
||||||
|
int k = 0;
|
||||||
|
for (i = 0; i < 24; i++) //12 bytes, SB or RC
|
||||||
|
{
|
||||||
|
//check to see if k++ starts at zero, or at 1
|
||||||
|
int sbrc_nib = (state->dmr_embedded_signalling[slot][5][k++] << 3) | (state->dmr_embedded_signalling[slot][5][k++] << 2) | (state->dmr_embedded_signalling[slot][5][k++] << 1) | (state->dmr_embedded_signalling[slot][5][k++] << 0);
|
||||||
|
fprintf (pFile, "%X", sbrc_nib);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crc3(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint8_t crc=0;
|
||||||
|
unsigned int K = 3;
|
||||||
|
//x^3+x+1
|
||||||
|
uint8_t poly[4] = {1,1,0,1};
|
||||||
|
uint8_t buf[256];
|
||||||
|
if (len+K > sizeof(buf)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset (buf, 0, sizeof(buf));
|
||||||
|
for (unsigned int i=0; i<len; i++){
|
||||||
|
buf[i] = bits[i];
|
||||||
|
}
|
||||||
|
for (unsigned int i=0; i<len; i++)
|
||||||
|
if (buf[i])
|
||||||
|
for (unsigned int j=0; j<K+1; j++)
|
||||||
|
buf[i+j] ^= poly[j];
|
||||||
|
for (unsigned int i=0; i<K; i++){
|
||||||
|
crc = (crc << 1) + buf[len + i];
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crc4(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint8_t crc=0;
|
||||||
|
unsigned int K = 4;
|
||||||
|
//x^4+x+1
|
||||||
|
uint8_t poly[5] = {1,0,0,1,1};
|
||||||
|
uint8_t buf[256];
|
||||||
|
if (len+K > sizeof(buf)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset (buf, 0, sizeof(buf));
|
||||||
|
for (unsigned int i=0; i<len; i++){
|
||||||
|
buf[i] = bits[i];
|
||||||
|
}
|
||||||
|
for (unsigned int i=0; i<len; i++)
|
||||||
|
if (buf[i])
|
||||||
|
for (unsigned int j=0; j<K+1; j++)
|
||||||
|
buf[i+j] ^= poly[j];
|
||||||
|
for (unsigned int i=0; i<K; i++){
|
||||||
|
crc = (crc << 1) + buf[len + i];
|
||||||
|
}
|
||||||
|
return crc ^ 0xF; //invert
|
||||||
|
}
|
||||||
|
|||||||
133
src/dmr_ms.c
133
src/dmr_ms.c
@@ -50,6 +50,11 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
uint8_t internalslot;
|
uint8_t internalslot;
|
||||||
uint8_t vc;
|
uint8_t vc;
|
||||||
|
|
||||||
|
//assign as nonsensical numbers
|
||||||
|
uint8_t cc = 25;
|
||||||
|
uint8_t power = 9; //power and pre-emption indicator
|
||||||
|
uint8_t lcss = 9;
|
||||||
|
|
||||||
//dummy bits to pass to dburst for link control
|
//dummy bits to pass to dburst for link control
|
||||||
uint8_t dummy_bits[196];
|
uint8_t dummy_bits[196];
|
||||||
memset (dummy_bits, 0, sizeof (dummy_bits));
|
memset (dummy_bits, 0, sizeof (dummy_bits));
|
||||||
@@ -82,12 +87,16 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
//so getDibit doesn't know to invert it before it gets here
|
//so getDibit doesn't know to invert it before it gets here
|
||||||
|
|
||||||
for (j = 0; j < 6; j++) {
|
for (j = 0; j < 6; j++) {
|
||||||
state->dmrburstL = 16;
|
state->dmrburstL = 16;
|
||||||
|
|
||||||
|
memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
memset (ambe_fr2, 0, sizeof(ambe_fr2));
|
||||||
|
memset (ambe_fr3, 0, sizeof(ambe_fr3));
|
||||||
|
|
||||||
for(i = 0; i < 12; i++)
|
for(i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
cachdata[i] = dibit;
|
cachdata[i] = dibit;
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
@@ -119,7 +128,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+12] = dibit;
|
state->dmr_stereo_payload[i+12] = dibit;
|
||||||
|
|
||||||
@@ -144,7 +153,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+48] = dibit;
|
state->dmr_stereo_payload[i+48] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -171,7 +180,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
sync[i] = (dibit | 1) + 48;
|
sync[i] = (dibit | 1) + 48;
|
||||||
|
|
||||||
// load the superframe to do embedded signal processing
|
// load the superframe to do embedded signal processing
|
||||||
if(vc > 1 && vc < 6) //grab on vc1 values 2-5 B C D and E
|
if(vc > 1) //grab on vc1 values 2-5 B C D and E
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 0
|
state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
@@ -180,17 +189,41 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sync[24] = 0;
|
sync[24] = 0;
|
||||||
|
|
||||||
|
//test for RC sync pattern in MS sourced audio
|
||||||
|
if ( strcmp (sync, DMR_RC_DATA_SYNC) == 0)
|
||||||
|
{
|
||||||
|
state->dmr_ms_rc = 1;
|
||||||
|
fprintf (stderr, "%s ", getTime());
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
||||||
|
|
||||||
|
fprintf (stderr, "| RC ");
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
|
||||||
|
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
|
||||||
|
|
||||||
EmbeddedSignallingOk = -1;
|
EmbeddedSignallingOk = -1;
|
||||||
if(QR_16_7_6_decode(EmbeddedSignalling))
|
if(QR_16_7_6_decode(EmbeddedSignalling))
|
||||||
{
|
{
|
||||||
EmbeddedSignallingOk = 1;
|
EmbeddedSignallingOk = 1;
|
||||||
|
cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||||
|
power = EmbeddedSignalling[4];
|
||||||
|
lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+90] = dibit;
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -214,7 +247,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+108] = dibit;
|
state->dmr_stereo_payload[i+108] = dibit;
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -227,6 +260,20 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 10 ", state->currentslot+1); //0x10 for "voice burst", forced to slot 1
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
state->dmr_ms_mode = 1;
|
state->dmr_ms_mode = 1;
|
||||||
|
|
||||||
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
||||||
@@ -238,22 +285,28 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||||
|
|
||||||
if (state->directmode == 0)
|
if (state->dmr_ms_rc == 0)
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
if (state->directmode == 0)
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
}
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
else
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
{
|
}
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
|
else
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vc == 6)
|
if (vc == 6)
|
||||||
{
|
{
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||||
|
//check the single burst/reverse channel opportunity
|
||||||
|
dmr_sbrc (opts, state, power);
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
dmr_alg_refresh (opts, state);
|
dmr_alg_refresh (opts, state);
|
||||||
}
|
}
|
||||||
@@ -268,6 +321,12 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
state->last_vc_sync_time = time(NULL);
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
|
||||||
vc++;
|
vc++;
|
||||||
|
|
||||||
|
//reset emb components
|
||||||
|
cc = 25;
|
||||||
|
power = 9; //power and pre-emption indicator
|
||||||
|
lcss = 9;
|
||||||
|
|
||||||
//this is necessary because we need to skip and collect dibits, not just skip them
|
//this is necessary because we need to skip and collect dibits, not just skip them
|
||||||
if (vc > 6) goto END;
|
if (vc > 6) goto END;
|
||||||
|
|
||||||
@@ -292,7 +351,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if (opts->inverted_dmr == 1)
|
if (opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
|
||||||
@@ -315,7 +374,11 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24];
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr2[4][24];
|
||||||
char ambe_fr3[4][24];
|
char ambe_fr3[4][24];
|
||||||
char ambe_fr4[4][24]; //shim for crappy tdma direct, need more samples to see if this is really a necessary shim or not
|
char ambe_fr4[4][24];
|
||||||
|
|
||||||
|
memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
memset (ambe_fr2, 0, sizeof(ambe_fr2));
|
||||||
|
memset (ambe_fr3, 0, sizeof(ambe_fr3));
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
//memcpy of ambe_fr for late entry
|
||||||
char m1[4][24];
|
char m1[4][24];
|
||||||
@@ -379,7 +442,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = state->dmr_stereo_payload[i];
|
dibit = state->dmr_stereo_payload[i];
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
cachdata[i] = dibit;
|
cachdata[i] = dibit;
|
||||||
}
|
}
|
||||||
@@ -398,7 +461,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = state->dmr_stereo_payload[i+12];
|
dibit = state->dmr_stereo_payload[i+12];
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
state->dmr_stereo_payload[i+12] = dibit;
|
state->dmr_stereo_payload[i+12] = dibit;
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -425,7 +488,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = state->dmr_stereo_payload[i+48];
|
dibit = state->dmr_stereo_payload[i+48];
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
@@ -443,8 +506,9 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -469,8 +533,9 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
|
state->dmr_stereo_payload[i+108] = dibit;
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -481,6 +546,20 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 10 ", state->currentslot+1); //0x10 for "voice burst", force to slot 1
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", getTime());
|
fprintf (stderr, "%s ", getTime());
|
||||||
if (opts->inverted_dmr == 0)
|
if (opts->inverted_dmr == 0)
|
||||||
{
|
{
|
||||||
@@ -517,7 +596,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//collect the mi fragment
|
//collect the mi fragment
|
||||||
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
|
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
|
||||||
|
|
||||||
//errors due to skipping other slot
|
//errors due to skipping other slot
|
||||||
// cach_err = dmr_cach (opts, state, cachdata);
|
// cach_err = dmr_cach (opts, state, cachdata);
|
||||||
@@ -558,7 +637,7 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
dibit_p++;
|
dibit_p++;
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -566,7 +645,7 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
|
|||||||
for (i = 0; i < 54; i++)
|
for (i = 0; i < 54; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
state->dmr_stereo_payload[i+90] = dibit;
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
244
src/dmr_pdu.c
244
src/dmr_pdu.c
@@ -44,9 +44,9 @@ void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR
|
|||||||
uint8_t padding = state->data_header_padding[slot];
|
uint8_t padding = state->data_header_padding[slot];
|
||||||
uint8_t lrrp_conf = 0;
|
uint8_t lrrp_conf = 0;
|
||||||
|
|
||||||
//consider disabling on prop_head data blocks if bad/false decodes
|
//check for more available flag info, etc on these prior to running
|
||||||
//if (state->data_p_head[slot] == 0)
|
if (DMR_PDU[0] == 0x01) dmr_locn (opts, state, block_len, DMR_PDU);
|
||||||
lrrp_conf = dmr_lrrp (opts, state, block_len, DMR_PDU);
|
else dmr_lrrp (opts, state, block_len, DMR_PDU);
|
||||||
|
|
||||||
//maybe one day we will have more things to do here
|
//maybe one day we will have more things to do here
|
||||||
state->data_conf_data[slot] = 0; //flag off confirmed data after processing it
|
state->data_conf_data[slot] = 0; //flag off confirmed data after processing it
|
||||||
@@ -57,7 +57,7 @@ void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR
|
|||||||
//The contents of this function are mostly my reversed engineered efforts by observing DSDPlus output and matching data bytes
|
//The contents of this function are mostly my reversed engineered efforts by observing DSDPlus output and matching data bytes
|
||||||
//combined with a few external sources such as OK-DMR for some token values and extra data values (rad and alt)
|
//combined with a few external sources such as OK-DMR for some token values and extra data values (rad and alt)
|
||||||
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
|
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
|
||||||
uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
uint16_t message_len = 0;
|
uint16_t message_len = 0;
|
||||||
@@ -82,12 +82,15 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
|
|
||||||
//lat-long-radius-alt
|
//lat-long-radius-alt
|
||||||
uint32_t lat = 0;
|
uint32_t lat = 0;
|
||||||
uint8_t lat_sign = 0;
|
|
||||||
uint32_t lon = 0;
|
uint32_t lon = 0;
|
||||||
uint16_t rad = 0;
|
uint16_t rad = 0; //or Azimuth in Circle 3D?
|
||||||
uint8_t alt = 0;
|
uint8_t alt = 0;
|
||||||
double lat_unit = (double)180/(double)4294967295;
|
double lat_unit = (double)180/(double)4294967295;
|
||||||
double lon_unit = (double)360/(double)4294967295;
|
double lon_unit = (double)360/(double)4294967295;
|
||||||
|
double lat_fin = 0.0; //calculated values
|
||||||
|
double lon_fin = 0.0; //calculated values
|
||||||
|
int lat_sign = 1; //positive 1, or negative 1
|
||||||
|
int lon_sign = 1; //positive 1, or negative 1
|
||||||
|
|
||||||
//speed/velocity
|
//speed/velocity
|
||||||
uint16_t vel = 0;
|
uint16_t vel = 0;
|
||||||
@@ -96,28 +99,19 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
|
|
||||||
//track, direction, degrees
|
//track, direction, degrees
|
||||||
uint8_t degrees = 0;
|
uint8_t degrees = 0;
|
||||||
uint8_t deg_set = 0;
|
uint8_t deg_set = 0;
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
// sprintf (deg_glyph, "%s", "d");
|
||||||
|
|
||||||
//triggered information report
|
//triggered information report
|
||||||
uint8_t report = 0;
|
uint8_t report = 0;
|
||||||
uint8_t pot_report = 0; //potential report by finding 0x0D and backtracking a few bytes
|
uint8_t pot_report = 0; //potential report by finding 0x0D and backtracking a few bytes
|
||||||
|
|
||||||
uint8_t is_p_head = 0;
|
|
||||||
|
|
||||||
//shim for getting LRRP out of some prop head data blocks
|
|
||||||
if (state->data_p_head[slot] == 1)
|
|
||||||
{
|
|
||||||
if (DMR_PDU[1] != 0x10) goto LRRP_END; //check to see if FID is 0x10
|
|
||||||
i = 0;
|
|
||||||
message_len = blocks * block_len;
|
|
||||||
source = state->dmr_lrrp_source[state->currentslot];
|
|
||||||
is_p_head = 1;
|
|
||||||
}
|
|
||||||
else i = 12;
|
|
||||||
|
|
||||||
|
|
||||||
//start looking for tokens (using my best understanding of the ok-dmr library xml and python files)
|
//start looking for tokens (using my best understanding of the ok-dmr library xml and python files)
|
||||||
for (i; i < ( (blocks*block_len) - (padding+4) ); i++)
|
for (i = 0; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
switch(DMR_PDU[i]){
|
switch(DMR_PDU[i]){
|
||||||
@@ -174,20 +168,22 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
//RESULT TOKEN or Request ID!
|
//RESULT TOKEN or Request ID!
|
||||||
|
case 0x22: //same comment as below, observed followed the message len
|
||||||
case 0x23: //this value has been seen after the 0x0D message len, appears to be a 2-byte value, was tripping a false on time stamp
|
case 0x23: //this value has been seen after the 0x0D message len, appears to be a 2-byte value, was tripping a false on time stamp
|
||||||
i += 2;
|
i += 2;
|
||||||
break;
|
break;
|
||||||
//answer and report tokens
|
//answer and report tokens
|
||||||
case 0x51: //circle-2d
|
case 0x51: //circle-2d
|
||||||
case 0x54: //circle-3d
|
case 0x54: //circle-3d
|
||||||
case 0x55: //circle-3d <- false positives on one sample, so either an earlier token occurred (that we don't have) or just random data?
|
case 0x55: //circle-3d
|
||||||
if (message_len > 0 && lat == 0)
|
if (message_len > 0 && lat == 0)
|
||||||
{
|
{
|
||||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
rad = (DMR_PDU[i+9] << 8) + DMR_PDU[i+10];
|
rad = (DMR_PDU[i+9] << 8) + DMR_PDU[i+10];
|
||||||
i += 10; //double check
|
i += 10;
|
||||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
||||||
else lat = 0;
|
else lat = 0;
|
||||||
}
|
}
|
||||||
@@ -210,13 +206,14 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x66: //point-2d
|
case 0x66: //point-2d
|
||||||
if (message_len > 0 && lat == 0)
|
if (message_len > 0 && lat == 0)
|
||||||
{
|
{
|
||||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
i += 8; //check
|
i += 8;
|
||||||
lrrp_confidence++;
|
lrrp_confidence++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -224,12 +221,13 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
case 0x6A: //point-3d
|
case 0x6A: //point-3d
|
||||||
if (message_len > 0 && lat == 0)
|
if (message_len > 0 && lat == 0)
|
||||||
{
|
{
|
||||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
alt = DMR_PDU[i+9];
|
alt = DMR_PDU[i+9];
|
||||||
i += 9; //check
|
i += 9;
|
||||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
if (lat > 0 && lon > 0) lrrp_confidence++;
|
||||||
else lat = 0;
|
else lat = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -273,7 +271,7 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
||||||
if (lrrp_confidence >= 4) //find the sweet magical number
|
if (lrrp_confidence >= 3) //find the sweet magical number
|
||||||
{
|
{
|
||||||
//now we can open our lrrp file and write to it as well
|
//now we can open our lrrp file and write to it as well
|
||||||
FILE * pFile; //file pointer
|
FILE * pFile; //file pointer
|
||||||
@@ -285,9 +283,10 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
if (!year) fprintf (pFile, "%s\t", getDateL() ); //current date, only add this IF no included timestamp in LRRP data?
|
if (!year) fprintf (pFile, "%s\t", getDateL() ); //current date, only add this IF no included timestamp in LRRP data?
|
||||||
if (!year) fprintf (pFile, "%s\t", getTimeL() ); //current timestamp, only add this IF no included timestamp in LRRP data?
|
if (!year) fprintf (pFile, "%s\t", getTimeL() ); //current timestamp, only add this IF no included timestamp in LRRP data?
|
||||||
if (year) fprintf (pFile, "%04d/%02d/%02d\t%02d:%02d:%02d\t", year, month, day, hour, minute, second); //add timestamp from decoded audio if available
|
if (year) fprintf (pFile, "%04d/%02d/%02d\t%02d:%02d:%02d\t", year, month, day, hour, minute, second); //add timestamp from decoded audio if available
|
||||||
//write data header source (or destination?) if not available in lrrp data
|
//write data header source if not available in lrrp data
|
||||||
if (!source) fprintf (pFile, "%08lld\t", state->dmr_lrrp_source[state->currentslot]); //source address from data header
|
if (!source) fprintf (pFile, "%08lld\t", state->dmr_lrrp_source[state->currentslot]); //source address from data header
|
||||||
if (source) fprintf (pFile, "%08d\t", source); //add source form decoded audio if available, else its from the header
|
if (source) fprintf (pFile, "%08d\t", source); //add source form decoded audio if available, else its from the header
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pot_report)
|
if (pot_report)
|
||||||
@@ -327,31 +326,45 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
if (lat)
|
if (lat)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
if (lat_sign) //print a '-' in front of the value
|
//need to check these new calcs for accuracy accross the globe, both lat and lon
|
||||||
fprintf (stderr, " LRRP - Lat: -%.5lf", (double)lat * lat_unit);
|
|
||||||
else fprintf (stderr, " LRRP - Lat: %.5lf", (double)lat * lat_unit);
|
//two's compliment-ish testing on these bytes
|
||||||
fprintf (stderr, " Lon: %.5lf", (double)lon * lon_unit);
|
if (lat & 0x80000000) //8
|
||||||
if (lat_sign)
|
{
|
||||||
fprintf (stderr, " (-%.5lf, %.5lf)", (double)lat * lat_unit , (double)lon * lon_unit);
|
lat = lat & 0x7FFFFFFF;
|
||||||
else fprintf (stderr, " (%.5lf, %.5lf)", (double)lat * lat_unit , (double)lon * lon_unit);
|
lat_sign = -1;
|
||||||
|
// lat = 0x80000000 - lat; //not sure why this doesn't work here like it does on lon, extra bit?
|
||||||
|
}
|
||||||
|
if (lon & 0x80000000)
|
||||||
|
{
|
||||||
|
lon = lon & 0x7FFFFFFF;
|
||||||
|
lon_sign = -1;
|
||||||
|
lon = 0x80000000 - lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
lat_fin = (double)lat * lat_unit * lat_sign;
|
||||||
|
lon_fin = (double)lon * lon_unit * lon_sign;
|
||||||
|
|
||||||
|
fprintf (stderr, " LRRP - Lat: %.5lf", lat_fin);
|
||||||
|
fprintf (stderr, " Lon: %.5lf", lon_fin);
|
||||||
|
fprintf (stderr, " (%.5lf, %.5lf)", lat_fin , lon_fin);
|
||||||
|
|
||||||
if (opts->lrrp_file_output == 1)
|
if (opts->lrrp_file_output == 1)
|
||||||
{
|
{
|
||||||
if (lat_sign) fprintf (pFile, "-");
|
fprintf (pFile, "%.5lf\t", lat_fin);
|
||||||
fprintf (pFile, "%.5lf\t", (double)lat * lat_unit);
|
fprintf (pFile, "%.5lf\t", lon_fin);
|
||||||
fprintf (pFile, "%.5lf\t", (double)lon * lon_unit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (rad)
|
if (rad)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " LRRP - Radius: %d", rad); //unsure of 'units' or calculation for radius (meters?)
|
fprintf (stderr, " LRRP - Radius: %dm", rad); //unsure of 'units' or calculation for radius (meters?)
|
||||||
}
|
}
|
||||||
if (alt)
|
if (alt)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " LRRP - Altitude: %d", alt); //unsure of 'units' or calculation for alt (meters?)
|
fprintf (stderr, " LRRP - Altitude: %dm", alt); //unsure of 'units' or calculation for alt (meters?)
|
||||||
}
|
}
|
||||||
if (vel_set)
|
if (vel_set)
|
||||||
{
|
{
|
||||||
@@ -362,7 +375,7 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
if (deg_set)
|
if (deg_set)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " LRRP - Track: %d Degrees", degrees);
|
fprintf (stderr, " LRRP - Track: %d%s", degrees, deg_glyph);
|
||||||
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,20 +386,17 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
fclose (pFile);
|
fclose (pFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
//save to array for ncurses
|
//save to string for ncurses
|
||||||
if (!source) source = state->dmr_lrrp_source[state->currentslot];
|
if (!source) source = state->dmr_lrrp_source[state->currentslot];
|
||||||
char sign[8];
|
|
||||||
char velstr[20];
|
char velstr[20];
|
||||||
char degstr[20];
|
char degstr[20];
|
||||||
char lrrpstr[100];
|
char lrrpstr[100];
|
||||||
sprintf (lrrpstr, "%s", "");
|
sprintf (lrrpstr, "%s", "");
|
||||||
sprintf (velstr, "%s", "");
|
sprintf (velstr, "%s", "");
|
||||||
sprintf (degstr, "%s", "");
|
sprintf (degstr, "%s", "");
|
||||||
if (lat_sign) sprintf (sign, "%s", "-");
|
if (lat) sprintf (lrrpstr, "LRRP %0d (%lf, %lf)", source, lat_fin, lon_fin);
|
||||||
else sprintf (sign, "%s", "");
|
|
||||||
if (lat) sprintf (lrrpstr, "LRRP %0d (%s%lf, %lf)", source, sign, (double)lat * lat_unit, (double)lon * lon_unit);
|
|
||||||
if (vel_set) sprintf (velstr, " %.4lf km/h", velocity * 3.6);
|
if (vel_set) sprintf (velstr, " %.4lf km/h", velocity * 3.6);
|
||||||
if (deg_set) sprintf (degstr, " %d deg", degrees);
|
if (deg_set) sprintf (degstr, " %d%s ", degrees, deg_glyph);
|
||||||
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -399,16 +409,126 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||||
}
|
}
|
||||||
|
|
||||||
// else //debug only, disabled
|
|
||||||
// {
|
|
||||||
// fprintf (stderr, "\n");
|
|
||||||
// fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
|
||||||
// }
|
|
||||||
|
|
||||||
LRRP_END:
|
LRRP_END:
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
return (lrrp_confidence);
|
|
||||||
|
void dmr_locn (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t blocks = state->data_header_blocks[slot];
|
||||||
|
uint8_t padding = state->data_header_padding[slot];
|
||||||
|
uint8_t source = state->dmr_lrrp_source[slot];
|
||||||
|
|
||||||
|
//flags if certain data type is present
|
||||||
|
uint8_t time = 0;
|
||||||
|
uint8_t lat = 0;
|
||||||
|
uint8_t lon = 0;
|
||||||
|
|
||||||
|
//date-time variables
|
||||||
|
uint8_t hour = 0;
|
||||||
|
uint8_t minute = 0;
|
||||||
|
uint8_t second = 0;
|
||||||
|
uint8_t year = 0;
|
||||||
|
uint8_t month = 0;
|
||||||
|
uint8_t day = 0;
|
||||||
|
|
||||||
|
//need to experiment best way to do this portion
|
||||||
|
uint8_t lat_deg = 0;
|
||||||
|
uint8_t lat_min = 0;
|
||||||
|
uint16_t lat_sec = 0;
|
||||||
|
|
||||||
|
uint8_t lon_deg = 0;
|
||||||
|
uint8_t lon_min = 0;
|
||||||
|
uint16_t lon_sec = 0;
|
||||||
|
|
||||||
|
char lat_ord[2];
|
||||||
|
char lon_ord[2];
|
||||||
|
sprintf (lat_ord, "%s", "N");
|
||||||
|
sprintf (lon_ord, "%s", "E");
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
// sprintf (deg_glyph, "%s", "d");
|
||||||
|
|
||||||
|
//more strings...
|
||||||
|
char locnstr[50];
|
||||||
|
char latstr[75];
|
||||||
|
char lonstr[75];
|
||||||
|
sprintf (locnstr, "%s", " ");
|
||||||
|
sprintf (latstr, "%s", " ");
|
||||||
|
sprintf (lonstr, "%s", " ");
|
||||||
|
|
||||||
|
//TODO: conversion to decimal format
|
||||||
|
//DD = d + (min/60) + (sec/3600)
|
||||||
|
//N is positive, E is positive?? Assuming its like a 2D plane
|
||||||
|
int lat_sign = 1; //positive 1 or negative 1
|
||||||
|
int lon_sign = 1; //positive 1 or negative 1
|
||||||
|
double lat_fin = 0;
|
||||||
|
double lon_fin = 0;
|
||||||
|
|
||||||
|
//start looking for specific bytes corresponding to 'letters' A (time), NSEW (ordinal directions), etc
|
||||||
|
for (i = 0; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||||
|
{
|
||||||
|
switch(DMR_PDU[i]){
|
||||||
|
case 0x41: //A -- time and date
|
||||||
|
time = 1;
|
||||||
|
hour = ((DMR_PDU[i+1] - 0x30) << 4) | (DMR_PDU[i+2] - 0x30);
|
||||||
|
minute = ((DMR_PDU[i+3] - 0x30) << 4) | (DMR_PDU[i+4] - 0x30);
|
||||||
|
second = ((DMR_PDU[i+5] - 0x30) << 4) | (DMR_PDU[i+6] - 0x30);
|
||||||
|
//think this is in day, mon, year format
|
||||||
|
day = ((DMR_PDU[i+7] - 0x30) << 4) | (DMR_PDU[i+8] - 0x30);
|
||||||
|
month = ((DMR_PDU[i+9] - 0x30) << 4) | (DMR_PDU[i+10] - 0x30);
|
||||||
|
year = ((DMR_PDU[i+11] - 0x30) << 4) | (DMR_PDU[i+12] - 0x30);
|
||||||
|
i += 12;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x53: //S -- South
|
||||||
|
sprintf (lat_ord, "%s", "S");
|
||||||
|
lat_sign = -1;
|
||||||
|
case 0x4E: //N -- North
|
||||||
|
lat = 1;
|
||||||
|
lat_deg = ((DMR_PDU[i+1] - 0x30) << 4) | (DMR_PDU[i+2] - 0x30);
|
||||||
|
lat_min = ((DMR_PDU[i+3] - 0x30) << 4) | (DMR_PDU[i+4] - 0x30);
|
||||||
|
lat_sec = ((DMR_PDU[i+6] - 0x30) << 12) | ((DMR_PDU[i+7] - 0x30) << 8) | ((DMR_PDU[i+8] - 0x30) << 4) | ((DMR_PDU[i+9] - 0x30) << 0);
|
||||||
|
i += 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x57: //W -- West
|
||||||
|
sprintf (lon_ord, "%s", "W");
|
||||||
|
lon_sign = -1;
|
||||||
|
case 0x45: //E -- East
|
||||||
|
lon = 1;
|
||||||
|
lon_deg = ((DMR_PDU[i+1] - 0x30) << 8) | ((DMR_PDU[i+2] - 0x30) << 4) | ((DMR_PDU[i+3] - 0x30) << 0);
|
||||||
|
lon_min = ((DMR_PDU[i+4] - 0x30) << 4) | (DMR_PDU[i+5] - 0x30);
|
||||||
|
lon_sec = ((DMR_PDU[i+7] - 0x30) << 12) | ((DMR_PDU[i+8] - 0x30) << 8) | ((DMR_PDU[i+9] - 0x30) << 4) | ((DMR_PDU[i+10] - 0x30) << 0);
|
||||||
|
i += 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//do nothing
|
||||||
|
break;
|
||||||
|
|
||||||
|
} //end switch
|
||||||
|
|
||||||
|
} //for i
|
||||||
|
|
||||||
|
if (lat && lon)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, "\n LOCN Report - Source: [%d]\n", source);
|
||||||
|
if (time) fprintf (stderr, " 20%02X/%02X/%02X %02X:%02X:%02X ", year, month, day, hour, minute, second);
|
||||||
|
fprintf (stderr, "Lat: %s %02X%s%02X\"%04X' Lon: %s %02X%s%02X\"%04X' ", lat_ord, lat_deg, deg_glyph, lat_min, lat_sec, lon_ord, lon_deg, deg_glyph, lon_min, lon_sec);
|
||||||
|
|
||||||
|
//string manip for ncurses terminal display
|
||||||
|
sprintf (locnstr, "LOCN %d ", source);
|
||||||
|
sprintf (latstr, "%s %02X%s%02X\"%04X' ", lat_ord, lat_deg, deg_glyph, lat_min, lat_sec);
|
||||||
|
sprintf (lonstr, "%s %02X%s%02X\"%04X' ", lon_ord, lon_deg, deg_glyph, lon_min, lon_sec);
|
||||||
|
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", locnstr, latstr, lonstr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
150
src/dmr_pi.c
150
src/dmr_pi.c
@@ -15,79 +15,78 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
|||||||
|
|
||||||
if((IrrecoverableErrors == 0))
|
if((IrrecoverableErrors == 0))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
|
state->payload_algid = PI_BYTE[0];
|
||||||
if (0 == 0)
|
state->payload_keyid = PI_BYTE[2];
|
||||||
{
|
|
||||||
state->payload_algid = PI_BYTE[0];
|
|
||||||
state->payload_keyid = PI_BYTE[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||||
if (state->payload_algid < 0x26)
|
if (state->payload_algid < 0x26)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s ", KYEL);
|
fprintf (stderr, "%s ", KYEL);
|
||||||
fprintf (stderr, "\n Slot 1");
|
fprintf (stderr, "\n Slot 1");
|
||||||
fprintf (stderr, " DMR PI Header ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algid, state->payload_keyid, state->payload_mi);
|
fprintf (stderr, " DMR PI H- ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algid, state->payload_keyid, state->payload_mi);
|
||||||
|
|
||||||
|
//Anytone RC4 Shim
|
||||||
|
if (state->payload_algid == 0x01)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Anytone (0x01)");
|
||||||
|
state->payload_algid = 0x21;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (state->payload_algid != 0x21)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
LFSR64 (state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algid >= 0x26)
|
if (state->payload_algid >= 0x26)
|
||||||
{
|
{
|
||||||
if (0 == 0)
|
state->payload_algid = 0;
|
||||||
{
|
state->payload_keyid = 0;
|
||||||
state->payload_algid = 0;
|
state->payload_mi = 0;
|
||||||
state->payload_keyid = 0;
|
|
||||||
state->payload_mi = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->currentslot == 1)
|
if (state->currentslot == 1)
|
||||||
{
|
{
|
||||||
if (0 == 0)
|
|
||||||
{
|
|
||||||
state->payload_algidR = PI_BYTE[0];
|
|
||||||
state->payload_keyidR = PI_BYTE[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
state->payload_algidR = PI_BYTE[0];
|
||||||
|
state->payload_keyidR = PI_BYTE[2];
|
||||||
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||||
if (state->payload_algidR < 0x26)
|
if (state->payload_algidR < 0x26)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s ", KYEL);
|
fprintf (stderr, "%s ", KYEL);
|
||||||
fprintf (stderr, "\n Slot 2");
|
fprintf (stderr, "\n Slot 2");
|
||||||
fprintf (stderr, " DMR PI Header ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algidR, state->payload_keyidR, state->payload_miR);
|
fprintf (stderr, " DMR PI H- ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algidR, state->payload_keyidR, state->payload_miR);
|
||||||
|
|
||||||
|
//Anytone RC4 Shim
|
||||||
|
if (state->payload_algidR == 0x01)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Anytone (0x01)");
|
||||||
|
state->payload_algidR = 0x21;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (state->payload_algidR != 0x21)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
LFSR64 (state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algidR >= 0x26)
|
if (state->payload_algidR >= 0x26)
|
||||||
{
|
{
|
||||||
if (0== 0)
|
state->payload_algidR = 0;
|
||||||
{
|
state->payload_keyidR = 0;
|
||||||
state->payload_algidR = 0;
|
state->payload_miR = 0;
|
||||||
state->payload_keyidR = 0;
|
|
||||||
state->payload_miR = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if((IrrecoverableErrors == 0) && CRCCorrect)
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " (PI CRC Okay)");
|
|
||||||
}
|
}
|
||||||
else if((IrrecoverableErrors == 0))
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " (PI FEC Okay)");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, (" (PI FEC ERR)"));
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LFSR(dsd_state * state)
|
void LFSR(dsd_state * state)
|
||||||
@@ -110,27 +109,64 @@ void LFSR(dsd_state * state)
|
|||||||
|
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
if (1 == 1)
|
fprintf (stderr, "%s", KYEL);
|
||||||
{
|
fprintf (stderr, " Slot 1");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||||
fprintf (stderr, " Slot 1");
|
fprintf(stderr, " MI: 0x%08X", lfsr);
|
||||||
fprintf (stderr, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
fprintf (stderr, "%s", KNRM);
|
||||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
state->payload_mi = lfsr;
|
state->payload_mi = lfsr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->currentslot == 1)
|
if (state->currentslot == 1)
|
||||||
{
|
{
|
||||||
if (1 == 1)
|
|
||||||
{
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, " Slot 2");
|
||||||
fprintf (stderr, " Slot 2");
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
||||||
fprintf (stderr, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
fprintf(stderr, " MI: 0x%08X", lfsr);
|
||||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
fprintf (stderr, "%s", KNRM);
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
state->payload_miR = lfsr;
|
state->payload_miR = lfsr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LFSR64(dsd_state * state)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
unsigned long long int lfsr = 0;
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
lfsr = (uint64_t) state->payload_mi;
|
||||||
|
}
|
||||||
|
else lfsr = (uint64_t) state->payload_miR;
|
||||||
|
|
||||||
|
uint8_t cnt = 0;
|
||||||
|
|
||||||
|
for(cnt=0;cnt<32;cnt++)
|
||||||
|
{
|
||||||
|
unsigned long long int bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
|
||||||
|
lfsr = (lfsr << 1) | bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Slot 1");
|
||||||
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||||
|
fprintf (stderr, " MI: 0x%016llX", lfsr);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->payload_mi = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Slot 2");
|
||||||
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
||||||
|
fprintf (stderr, " MI: 0x%016llX", lfsr);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->payload_miR = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
118
src/dmr_utils.c
118
src/dmr_utils.c
@@ -4,7 +4,7 @@
|
|||||||
//Original Source - https://github.com/LouisErigHerve/dsd
|
//Original Source - https://github.com/LouisErigHerve/dsd
|
||||||
|
|
||||||
//Additional Functions
|
//Additional Functions
|
||||||
//Hamming17123, crc8, crc8ok functions
|
//Hamming17123, crc7, crc8, crc8ok functions
|
||||||
//Original Souce - https://github.com/boatbod/op25
|
//Original Souce - https://github.com/boatbod/op25
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
@@ -121,6 +121,31 @@ bool crc8_ok(uint8_t bits[], unsigned int len)
|
|||||||
return (crc == crc8(bits,len));
|
return (crc == crc8(bits,len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t crc7(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint8_t crc=0;
|
||||||
|
unsigned int K = 7;
|
||||||
|
//G7(x) = x7 + x5 + x2 + x + 1 check poly below for correct (dmr rc crc7)
|
||||||
|
uint8_t poly[8] = {1,0,1,0,0,1,1,1}; // crc7 poly
|
||||||
|
uint8_t buf[256];
|
||||||
|
if (len+K > sizeof(buf)) {
|
||||||
|
// fprintf (stderr, "crc8: buffer length %u exceeds maximum %lu\n", len+K, sizeof(buf));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset (buf, 0, sizeof(buf));
|
||||||
|
for (unsigned int i=0; i<len; i++){
|
||||||
|
buf[i] = bits[i];
|
||||||
|
}
|
||||||
|
for (unsigned int i=0; i<len; i++)
|
||||||
|
if (buf[i])
|
||||||
|
for (unsigned int j=0; j<K+1; j++)
|
||||||
|
buf[i+j] ^= poly[j];
|
||||||
|
for (unsigned int i=0; i<K; i++){
|
||||||
|
crc = (crc << 1) + buf[len + i];
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief : This function compute the CRC-CCITT of the DMR data
|
* @brief : This function compute the CRC-CCITT of the DMR data
|
||||||
* by using the polynomial x^16 + x^12 + x^5 + 1
|
* by using the polynomial x^16 + x^12 + x^5 + 1
|
||||||
@@ -300,15 +325,15 @@ uint8_t ComputeCrc5Bit(uint8_t * DMRData)
|
|||||||
return CRC;
|
return CRC;
|
||||||
} /* End ComputeCrc5Bit() */
|
} /* End ComputeCrc5Bit() */
|
||||||
|
|
||||||
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
uint64_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
||||||
{
|
{
|
||||||
uint32_t Output = 0;
|
uint64_t Output = 0;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
for(i = 0; i < BitLength; i++)
|
for(i = 0; i < BitLength; i++)
|
||||||
{
|
{
|
||||||
Output <<= 1;
|
Output <<= 1;
|
||||||
Output |= (uint32_t)(BufferIn[i] & 1);
|
Output |= (uint64_t)(BufferIn[i] & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Output;
|
return Output;
|
||||||
@@ -329,4 +354,87 @@ uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
|||||||
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||||
*
|
*
|
||||||
* @return The 9 bit CRC
|
* @return The 9 bit CRC
|
||||||
*/
|
*/
|
||||||
|
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||||
|
/* Polynomial x^9 + x^6 + x^4 + x^3 + 1
|
||||||
|
* Normal = 0x059
|
||||||
|
* Reciprocal = 0x134
|
||||||
|
* Reversed reciprocal = 0x12C */
|
||||||
|
uint16_t Polynome = 0x059;
|
||||||
|
for(i = 0; i < NbData; i++)
|
||||||
|
{
|
||||||
|
if(((CRC >> 8) & 1) ^ (DMRData[i] & 1))
|
||||||
|
{
|
||||||
|
CRC = (CRC << 1) ^ Polynome;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRC <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Conserve only the 9 LSBs */
|
||||||
|
CRC &= 0x01FF;
|
||||||
|
|
||||||
|
/* Invert the CRC */
|
||||||
|
CRC ^= 0x01FF;
|
||||||
|
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrc9Bit() */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the CRC-32 of the DMR data
|
||||||
|
* by using the 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
|
||||||
|
*
|
||||||
|
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
||||||
|
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
||||||
|
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
||||||
|
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||||
|
*
|
||||||
|
* @param NbData : The number of bit to compute
|
||||||
|
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
||||||
|
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
||||||
|
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||||
|
*
|
||||||
|
* @return The 32 bit CRC
|
||||||
|
*/
|
||||||
|
uint32_t ComputeCrc32Bit(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//for whatever reason, we get the CRC returned in a reversed byte order (MSO LSO b***s***)
|
||||||
|
uint32_t a, b, c, d;
|
||||||
|
a = b = c = d = 0;
|
||||||
|
a = (CRC & 0xFF) << 24;
|
||||||
|
b = (CRC & 0xFF00) >> 8;
|
||||||
|
b = b << 16;
|
||||||
|
c = (CRC & 0xFF0000) >> 16;
|
||||||
|
c = c << 8;
|
||||||
|
d = (CRC & 0xFF000000) >> 24;
|
||||||
|
|
||||||
|
CRC = a + b + c + d;
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrc32Bit() */
|
||||||
|
|||||||
261
src/dpmr_voice.c
261
src/dpmr_voice.c
@@ -24,11 +24,18 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
uint8_t CCH[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
uint8_t CCH[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
||||||
uint8_t CCHDescrambled[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
uint8_t CCHDescrambled[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
||||||
uint8_t CCHDeInterleaved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
uint8_t CCHDeInterleaved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
||||||
uint8_t CCHDataHammingCorrected[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48] = {0};
|
uint8_t CCHDataHammingCorrected[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48];
|
||||||
uint8_t CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
|
uint8_t CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||||
uint8_t CCHDataCRCComputed[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
|
|
||||||
|
memset (CCHDataHammingCorrected, 1, sizeof(CCHDataHammingCorrected));
|
||||||
|
memset (CCHDataCRC, 1, sizeof(CCHDataCRC));
|
||||||
|
|
||||||
|
uint8_t CCHDataCRCComputed[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||||
|
memset (CCHDataCRCComputed, 0, sizeof(CCHDataCRCComputed));
|
||||||
|
|
||||||
uint8_t CC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE / 2][24] = {0};
|
uint8_t CC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE / 2][24] = {0};
|
||||||
char ambe_fr[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][4][24] = {0};
|
char ambe_fr[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][4][24];
|
||||||
|
memset (ambe_fr, 0, sizeof (ambe_fr));
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
uint32_t ScramblerLFSR = 0;
|
uint32_t ScramblerLFSR = 0;
|
||||||
bool correctable = true;
|
bool correctable = true;
|
||||||
@@ -56,11 +63,6 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
uint8_t CalledID[8] = {0};
|
uint8_t CalledID[8] = {0};
|
||||||
uint8_t CallingID[8] = {0};
|
uint8_t CallingID[8] = {0};
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "VOICE e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First CCH (Control CHannel) - 72 bit */
|
/* First CCH (Control CHannel) - 72 bit */
|
||||||
k = 0;
|
k = 0;
|
||||||
|
|
||||||
@@ -76,7 +78,6 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
CCH[0][(i * 2)] = (1 & (dibit >> 1)); // bit 1
|
CCH[0][(i * 2)] = (1 & (dibit >> 1)); // bit 1
|
||||||
CCH[0][(i * 2) + 1] = (1 & dibit); // bit 0
|
CCH[0][(i * 2) + 1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
||||||
@@ -148,9 +149,6 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
||||||
|
|
||||||
//Sync issues occur when FS2 occurs immediatly after the 4th rep, so cutting of 4 early to gain constant resync
|
|
||||||
//probably can't resync fast enough when the next sync starts immediately after the 4th rep of 36 dibits
|
|
||||||
//consider writing a method that will loop this entire process based on the frame numbering found inside the CCH info, is that possible?
|
|
||||||
k = 0;
|
k = 0;
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
@@ -177,16 +175,10 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
x++;
|
x++;
|
||||||
y++;
|
y++;
|
||||||
z++;
|
z++;
|
||||||
//should be fixed now with the frame_sync t_max setup
|
|
||||||
//no more early breaking required
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the Hamming (12,8) error code detection
|
|
||||||
* Not needed because it has already been done
|
|
||||||
* in the "InitAllFecFunction()" function */
|
|
||||||
//Hamming_12_8_init();
|
|
||||||
|
|
||||||
/* Decoding all CCH (Control CHannel) */
|
/* Decoding all CCH (Control CHannel) */
|
||||||
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
|
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
|
||||||
{
|
{
|
||||||
@@ -207,22 +199,8 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
if(HammingCorrectable[i][j] == false) correctable = false;
|
if(HammingCorrectable[i][j] == false) correctable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(correctable)
|
if(correctable) HammingOk[i] = 1;
|
||||||
{
|
else HammingOk[i] = 0;
|
||||||
|
|
||||||
//fprintf(stderr, "CCH data corrected successfully\n");
|
|
||||||
|
|
||||||
|
|
||||||
HammingOk[i] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
//fprintf(stderr, "ERROR !!! CCH data contains uncorrectable bit\n");
|
|
||||||
|
|
||||||
|
|
||||||
HammingOk[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reconstitute the 7 bit CRC */
|
/* Reconstitute the 7 bit CRC */
|
||||||
CCHDataCRC[i] = 0;
|
CCHDataCRC[i] = 0;
|
||||||
@@ -237,24 +215,8 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
/* Compute the 7 bit CRC */
|
/* Compute the 7 bit CRC */
|
||||||
CCHDataCRCComputed[i] = CRC7BitdPMR(CCHDataHammingCorrected[i], 41);
|
CCHDataCRCComputed[i] = CRC7BitdPMR(CCHDataHammingCorrected[i], 41);
|
||||||
|
|
||||||
if(CCHDataCRC[i] == CCHDataCRCComputed[i])
|
if(CCHDataCRC[i] == CCHDataCRCComputed[i]) CrcOk[i] = 1;
|
||||||
{
|
else CrcOk[i] = 0;
|
||||||
|
|
||||||
//fprintf(stderr, "Good CCH CRCs ! CRC received = 0x%02X, CRC computed = 0x%02X\n",
|
|
||||||
// CCHDataCRC[i], CCHDataCRCComputed[i]);
|
|
||||||
|
|
||||||
|
|
||||||
CrcOk[i] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
//fprintf(stderr, "ERROR !!! CCH CRCs are different ! CRC received = 0x%02X, CRC computed = 0x%02X\n",
|
|
||||||
// CCHDataCRC[i], CCHDataCRCComputed[i]);
|
|
||||||
|
|
||||||
|
|
||||||
CrcOk[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill the frame number */
|
/* Fill the frame number */
|
||||||
CCH_FrameNumber[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][0], 2);
|
CCH_FrameNumber[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][0], 2);
|
||||||
@@ -300,45 +262,18 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
CallingID[7] = '\0';
|
CallingID[7] = '\0';
|
||||||
|
|
||||||
|
|
||||||
/* Print Hamming debug info */
|
|
||||||
//fprintf(stderr, " | ");
|
|
||||||
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "Hamming[%u]=", i);
|
|
||||||
for(j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
//if(HammingCorrectable[i][j]) fprintf(stderr, "1");
|
|
||||||
//else fprintf(stderr, "0");
|
|
||||||
}
|
|
||||||
//fprintf(stderr, " HammingOk[%u]=%u", i, HammingOk[i]);
|
|
||||||
//fprintf(stderr, " CrcOk[%u]=%u", i, CrcOk[i]);
|
|
||||||
//fprintf(stderr, " Bit[%u]=", i);
|
|
||||||
//for(j = 0; j < 7; j++)
|
|
||||||
//{
|
|
||||||
// if(CCHDataHammingCorrected[i][j + 41]) fprintf(stderr, "1");
|
|
||||||
// else fprintf(stderr, "0");
|
|
||||||
//}
|
|
||||||
//fprintf(stderr, " CRCrec=0x%02X CRCcomp=0x%02X", CCHDataCRC[i], CCHDataCRCComputed[i]);
|
|
||||||
//if(i < (NB_OF_DPMR_VOICE_FRAME_TO_DECODE - 1)) fprintf(stderr, " - ");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2)
|
|
||||||
/* To determine the part of the payload frame, we need to check the
|
/* To determine the part of the payload frame, we need to check the
|
||||||
* Hamming code result, the CRC and the frame number */
|
* Hamming code result, the CRC and the frame number */
|
||||||
|
|
||||||
// if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 0)) ||
|
// if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 0)) ||
|
||||||
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 1)))
|
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 1)))
|
||||||
|
|
||||||
if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 0)) ||
|
if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 0)) ||
|
||||||
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 1)))
|
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 1)))
|
||||||
{
|
{
|
||||||
/* First part of the super frame */
|
/* First part of the super frame */
|
||||||
PartOfSuperFrame = 1;
|
PartOfSuperFrame = 1;
|
||||||
|
|
||||||
|
|
||||||
//fprintf(stderr, "\nFirst part of the Super Frame");
|
|
||||||
|
|
||||||
|
|
||||||
/* The next part will normally be the second part */
|
/* The next part will normally be the second part */
|
||||||
opts->dPMR_next_part_of_superframe = 2;
|
opts->dPMR_next_part_of_superframe = 2;
|
||||||
|
|
||||||
@@ -366,20 +301,19 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
/* BUGFIX : Copy in all case the TG ID */
|
/* BUGFIX : Copy in all case the TG ID */
|
||||||
strcpy((char *)state->dPMRVoiceFS2Frame.CalledID, (char *)CalledID);
|
strcpy((char *)state->dPMRVoiceFS2Frame.CalledID, (char *)CalledID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To determine the part of the payload frame, we need to check the
|
/* To determine the part of the payload frame, we need to check the
|
||||||
* Hamming code result, the CRC and the frame number */
|
* Hamming code result, the CRC and the frame number */
|
||||||
|
|
||||||
// else if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 2)) ||
|
// else if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 2)) ||
|
||||||
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 3)))
|
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 3)))
|
||||||
|
|
||||||
else if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 2)) ||
|
else if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 2)) ||
|
||||||
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 3)))
|
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 3)))
|
||||||
{
|
{
|
||||||
/* Second part of the super frame */
|
/* Second part of the super frame */
|
||||||
PartOfSuperFrame = 2;
|
PartOfSuperFrame = 2;
|
||||||
|
|
||||||
|
|
||||||
//fprintf(stderr, "\nSecond part of the Super Frame");
|
|
||||||
|
|
||||||
|
|
||||||
/* The next part will normally be the first part */
|
/* The next part will normally be the first part */
|
||||||
opts->dPMR_next_part_of_superframe = 1;
|
opts->dPMR_next_part_of_superframe = 1;
|
||||||
|
|
||||||
@@ -392,7 +326,9 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
CallingID[7] = '\0';
|
CallingID[7] = '\0';
|
||||||
|
|
||||||
/* Determine if the calling SRC ID is correct */
|
/* Determine if the calling SRC ID is correct */
|
||||||
|
|
||||||
//if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1])
|
//if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1])
|
||||||
|
|
||||||
if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) &&
|
if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) &&
|
||||||
(CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1])))
|
(CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1])))
|
||||||
{
|
{
|
||||||
@@ -420,132 +356,143 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
/* Set the next part of the superframe to receive */
|
/* Set the next part of the superframe to receive */
|
||||||
if(opts->dPMR_next_part_of_superframe == 1)
|
if(opts->dPMR_next_part_of_superframe == 1)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
//fprintf(stderr, "Supposed first part of the Super Frame\n");
|
|
||||||
|
|
||||||
|
|
||||||
/* Set the next part as the second part */
|
/* Set the next part as the second part */
|
||||||
opts->dPMR_next_part_of_superframe = 2;
|
opts->dPMR_next_part_of_superframe = 2;
|
||||||
}
|
}
|
||||||
else if(opts->dPMR_next_part_of_superframe == 2)
|
else if(opts->dPMR_next_part_of_superframe == 2)
|
||||||
{
|
{
|
||||||
|
|
||||||
//fprintf(stderr, "Supposed second part of the Super Frame\n");
|
|
||||||
|
|
||||||
|
|
||||||
/* Set the next part as the first part */
|
/* Set the next part as the first part */
|
||||||
opts->dPMR_next_part_of_superframe = 1;
|
opts->dPMR_next_part_of_superframe = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
//fprintf(stderr, "Unknown part of the Super Frame\n");
|
|
||||||
|
|
||||||
|
|
||||||
/* Unknown current part, set to 0 */
|
/* Unknown current part, set to 0 */
|
||||||
opts->dPMR_next_part_of_superframe = 0;
|
opts->dPMR_next_part_of_superframe = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
|
|
||||||
{
|
|
||||||
//only print the CC and src/tg info if CRC is good
|
|
||||||
/* Display the color code */
|
|
||||||
//fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Unknown color code, display "??" */
|
|
||||||
//fprintf(stderr, " | Color Code=?? ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display the destination ID (talk group - TG) */
|
/* Display the destination ID (talk group - TG) */
|
||||||
//fprintf(stderr, "| TG=%s", CalledID);
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
if(state->dPMRVoiceFS2Frame.CalledIDOk)
|
if(state->dPMRVoiceFS2Frame.CalledIDOk)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf (stderr, "%s", KGRN);
|
||||||
fprintf(stderr, "\n| TG=%s", CalledID);
|
fprintf(stderr, " TG=%s", CalledID);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
//strcpy (state->dpmr_target_id, CalledID); //HERE HERE
|
|
||||||
state->dpmr_target_id = CalledID;
|
//check other as well before assigning
|
||||||
//fprintf(stderr, " (CRC OK) ");
|
if(state->dPMRVoiceFS2Frame.CallingIDOk) sprintf (state->dpmr_target_id, "%s", CalledID);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf(stderr, " TG=(CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display the source ID */
|
||||||
|
if(state->dPMRVoiceFS2Frame.CallingIDOk)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf(stderr, " Src=%s", CallingID);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//check other as well before assigning
|
||||||
|
if(state->dPMRVoiceFS2Frame.CalledIDOk) sprintf (state->dpmr_caller_id, "%s", CallingID);
|
||||||
|
|
||||||
|
//stashed channel code in here, more likely to be correct, but no guarantee
|
||||||
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
|
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf (stderr, "%s", KGRN);
|
||||||
fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
|
fprintf(stderr, " Channel Code=%02d", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0];
|
|
||||||
|
|
||||||
|
//check other as well before assigning
|
||||||
|
if(state->dPMRVoiceFS2Frame.CalledIDOk) state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
//fprintf(stderr, " (CRC ERR) ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display the source ID */
|
|
||||||
//fprintf(stderr, "| Src=%s", CallingID);
|
|
||||||
if(state->dPMRVoiceFS2Frame.CallingIDOk)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf(stderr, "| Src=%s", CallingID);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
//strcpy (state->dpmr_caller_id, CallingID);
|
|
||||||
state->dpmr_caller_id = CallingID;
|
|
||||||
|
|
||||||
//fprintf(stderr, " (CRC OK) \n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf(stderr, " (CRC ERR) \n");
|
fprintf(stderr, " Src=(CRC ERR) Channel Code =(CRC ERR)");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2) */
|
if (state->dPMRVoiceFS2Frame.Version[0] == 3)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " Scrambler");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
if (state->R != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Key %05lld ", state->R );
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Play only voice frames by first looking to see if there is a TCH voice in CommunicationMode,
|
//Play only voice frames by first looking to see if there is a TCH voice in CommunicationMode,
|
||||||
//then sending it to be processed by processMbeFrame, its much cleaner this way
|
|
||||||
short start = 0;
|
short start = 0;
|
||||||
short end = 4;
|
short end = 4;
|
||||||
|
|
||||||
|
//not really even sure why I made this loop
|
||||||
|
//I probably should actually read the manual again
|
||||||
for (short o = 0; o < 2; o++)
|
for (short o = 0; o < 2; o++)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//reset scrambler key seed on frame 0
|
||||||
|
if (state->dPMRVoiceFS2Frame.FrameNumbering[o] == 0) state->payload_miN = 0;
|
||||||
|
|
||||||
//depending on first or second TCH, set start and end variables appropriately
|
//depending on first or second TCH, set start and end variables appropriately
|
||||||
if (o == 1)
|
if (o == 1)
|
||||||
{
|
{
|
||||||
start = 4;
|
start = 4;
|
||||||
end = 8;
|
end = 8;
|
||||||
//add extra line break on second repitition only when dumping payload to console
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//fprintf (stderr, "\nCommunication Mode: %d\n", state->dPMRVoiceFS2Frame.CommunicationMode[o]);
|
|
||||||
//fprintf (stderr, "Encryption Mode: %d\n", state->dPMRVoiceFS2Frame.Version[o]);
|
//this is used so we can simply pass the voice here to the
|
||||||
//fprintf (stderr, "Superframe Number: %d", state->dPMRVoiceFS2Frame.FrameNumbering[o]); //should be 1-4, but may be same number on TCH 1 or TCH 2
|
//nxdn scrambler by telling mbe these are nxdn frames and to
|
||||||
|
//apply the same descramble method to them if necessary
|
||||||
|
int realsynctype = state->synctype;
|
||||||
|
|
||||||
|
// fprintf (stderr, "\nCommunication Mode: %d\n", state->dPMRVoiceFS2Frame.CommunicationMode[o]);
|
||||||
|
// fprintf (stderr, "Encryption Mode: %d\n", state->dPMRVoiceFS2Frame.Version[o]); //shows as 3 (manufacturer specific, 5.16) on the dPMR scrambler values received
|
||||||
|
// fprintf (stderr, "Superframe Number: %d", state->dPMRVoiceFS2Frame.FrameNumbering[o]);
|
||||||
|
|
||||||
if((state->dPMRVoiceFS2Frame.CommunicationMode[o] == 0) ||
|
if((state->dPMRVoiceFS2Frame.CommunicationMode[o] == 0) ||
|
||||||
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 1) ||
|
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 1) ||
|
||||||
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 5))
|
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 5))
|
||||||
{
|
{
|
||||||
|
|
||||||
//There are 4 AMBE voice samples per TCH
|
//check to see if we are using scrambler
|
||||||
|
if (state->dPMRVoiceFS2Frame.Version[o] == 3) //!= 0
|
||||||
|
{
|
||||||
|
state->synctype = 28; //fake it as nxdn
|
||||||
|
state->nxdn_cipher_type = 0x01; //turn on nxdn scrambler cipher
|
||||||
|
state->dmr_encL = 1; //flag on enc bit here so we can mute if no key provided
|
||||||
|
}
|
||||||
|
|
||||||
|
//flag back off if key is provided
|
||||||
|
if (state->R != 0) state->dmr_encL = 0;
|
||||||
|
|
||||||
|
//There are 4 AMBE voice frames per TCH
|
||||||
for(i = start; i < end; i++)
|
for(i = start; i < end; i++)
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr[i], NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr[i], NULL);
|
||||||
} //End for(i = start; i < end); i++)
|
}
|
||||||
} //if comms and not encrypted
|
|
||||||
}//End for TCH for o loop
|
|
||||||
|
|
||||||
|
//set the correct sync type again and flag off the cipher
|
||||||
|
state->synctype = realsynctype;
|
||||||
|
state->nxdn_cipher_type = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
//keep code below, could be handy cheat sheet for other info
|
//keep code below, could be handy cheat sheet for other info
|
||||||
#ifdef dPMR_PRINT_DEBUG_INFO
|
#ifdef dPMR_PRINT_DEBUG_INFO
|
||||||
|
|||||||
129
src/dsd_audio.c
129
src/dsd_audio.c
@@ -26,9 +26,11 @@ void closePulseOutput (dsd_opts * opts)
|
|||||||
{
|
{
|
||||||
pa_simple_free (opts->pulse_digi_dev_out);
|
pa_simple_free (opts->pulse_digi_dev_out);
|
||||||
if (opts->dmr_stereo == 1)
|
if (opts->dmr_stereo == 1)
|
||||||
{
|
|
||||||
pa_simple_free (opts->pulse_digi_dev_outR);
|
pa_simple_free (opts->pulse_digi_dev_outR);
|
||||||
}
|
|
||||||
|
if (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) //EDACS analog calls and/or monitoring source analog audio
|
||||||
|
pa_simple_free (opts->pulse_raw_dev_out);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void closePulseInput (dsd_opts * opts)
|
void closePulseInput (dsd_opts * opts)
|
||||||
@@ -47,10 +49,9 @@ void openPulseOutput(dsd_opts * opts)
|
|||||||
tt.channels = opts->pulse_digi_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse
|
tt.channels = opts->pulse_digi_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse
|
||||||
tt.rate = opts->pulse_digi_rate_out; //48000, switches to 8000 when using RTL dongle
|
tt.rate = opts->pulse_digi_rate_out; //48000, switches to 8000 when using RTL dongle
|
||||||
|
|
||||||
if (opts->monitor_input_audio == 1)
|
//reconfigured to open when using edacs or raw analog monitor so we can have a analog audio out that runs at 48k1 and not 8k1 float/short
|
||||||
{
|
if (opts->frame_provoice == 1 || opts->monitor_input_audio == 1)
|
||||||
opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME3", PA_STREAM_PLAYBACK, NULL, "Raw Audio Out", &ss, NULL, NULL, NULL);
|
opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME3", PA_STREAM_PLAYBACK, NULL, "Analog", &ss, 0, NULL, NULL);
|
||||||
}
|
|
||||||
|
|
||||||
//tt
|
//tt
|
||||||
pa_channel_map* left = 0; //NULL and 0 are same in this context
|
pa_channel_map* left = 0; //NULL and 0 are same in this context
|
||||||
@@ -444,17 +445,18 @@ playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
if (state->audio_out_idx > opts->delay)
|
if (state->audio_out_idx > opts->delay)
|
||||||
{
|
{
|
||||||
// output synthesized speech to sound card
|
if (opts->audio_out_type == 5) //OSS
|
||||||
if(opts->audio_out_type == 2)
|
|
||||||
{
|
|
||||||
//go F yourself PA
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
//Test just sending it straight on since I think I've figured out the STDIN and RTL for Stereo
|
//OSS
|
||||||
pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works.
|
result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2));
|
||||||
state->audio_out_idx = 0;
|
state->audio_out_idx = 0;
|
||||||
}
|
}
|
||||||
|
else if (opts->audio_out_type == 0)
|
||||||
|
{
|
||||||
|
pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL);
|
||||||
|
state->audio_out_idx = 0;
|
||||||
|
}
|
||||||
|
else state->audio_out_idx = 0; //failsafe for audio_out == 0
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -478,15 +480,18 @@ playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->audio_out_idxR > opts->delay)
|
if (state->audio_out_idxR > opts->delay)
|
||||||
{
|
{
|
||||||
// output synthesized speech to sound card
|
// output synthesized speech to sound card
|
||||||
if(opts->audio_out_type == 2)
|
if (opts->audio_out_type == 5) //OSS
|
||||||
{
|
|
||||||
//go F yourself PA
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works.
|
//OSS
|
||||||
|
result = write (opts->audio_out_fdR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2));
|
||||||
state->audio_out_idxR = 0;
|
state->audio_out_idxR = 0;
|
||||||
}
|
}
|
||||||
|
else if (opts->audio_out_type == 0)
|
||||||
|
{
|
||||||
|
pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL);
|
||||||
|
state->audio_out_idxR = 0;
|
||||||
|
}
|
||||||
|
else state->audio_out_idxR = 0; //failsafe for audio_out == 0
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,6 +546,9 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
const char ch = '.';
|
const char ch = '.';
|
||||||
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
||||||
|
|
||||||
|
//if no extension set, give default of .wav -- bugfix for github issue #105
|
||||||
|
// if (extension == NULL) extension = ".wav";
|
||||||
|
|
||||||
// get info of device/file
|
// get info of device/file
|
||||||
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
||||||
{
|
{
|
||||||
@@ -558,6 +566,7 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 8;
|
opts->audio_in_type = 8;
|
||||||
@@ -567,13 +576,13 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
opts->audio_in_file_info->seekable=0;
|
opts->audio_in_file_info->seekable=0;
|
||||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||||
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
|
||||||
if(opts->tcp_file_in == NULL)
|
if(opts->tcp_file_in == NULL)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
|
else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 6;
|
opts->audio_in_type = 6;
|
||||||
@@ -592,12 +601,84 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
}
|
}
|
||||||
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
opts->audio_in_type = 3;
|
opts->audio_in_type = 3;
|
||||||
|
#elif AERO_BUILD
|
||||||
|
opts->audio_in_type = 5;
|
||||||
|
sprintf (opts->audio_in_dev, "/dev/dsp");
|
||||||
|
#else
|
||||||
|
opts->audio_in_type = 0;
|
||||||
|
sprintf (opts->audio_in_dev, "pulse");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
|
else if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 0;
|
opts->audio_in_type = 0;
|
||||||
}
|
}
|
||||||
|
else if((strncmp(opts->audio_in_dev, "/dev/dsp", 8) == 0))
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if no extension set, treat as named pipe or extensionless wav file -- bugfix for github issue #105
|
||||||
|
else if (extension == NULL)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 2;
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate = opts->wav_sample_rate;
|
||||||
|
opts->audio_in_file_info->channels = 1;
|
||||||
|
opts->audio_in_file_info->seekable = 0;
|
||||||
|
opts->audio_in_file_info->format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
|
opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||||
|
|
||||||
|
if (opts->audio_in_file == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't open file/pipe with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//test .rrc files with hardset wav file settings
|
||||||
|
else if (strncmp(extension, ".rrc", 3) == 0)
|
||||||
|
{
|
||||||
|
//debug
|
||||||
|
fprintf (stderr, "Opening M17 .rrc headless wav file\n");
|
||||||
|
|
||||||
|
opts->audio_in_type = 2;
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate = 48000;
|
||||||
|
opts->audio_in_file_info->channels = 1;
|
||||||
|
opts->audio_in_file_info->seekable = 0;
|
||||||
|
opts->audio_in_file_info->format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
|
opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||||
|
|
||||||
|
if(opts->audio_in_file == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't open %s with libsndfile: %s\n", opts->audio_in_dev, sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: test .sym files as symbol capture .bin files
|
||||||
|
else if (strncmp(extension, ".sym", 3) == 0)
|
||||||
|
{
|
||||||
|
struct stat stat_buf;
|
||||||
|
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Error, couldn't open bin file %s\n", opts->audio_in_dev);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
||||||
|
opts->audio_in_type = 4; //symbol capture bin files
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (strncmp(extension, ".bin", 3) == 0)
|
else if (strncmp(extension, ".bin", 3) == 0)
|
||||||
{
|
{
|
||||||
struct stat stat_buf;
|
struct stat stat_buf;
|
||||||
@@ -645,9 +726,11 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
|
|
||||||
}
|
}
|
||||||
//open pulse audio if no bin or wav
|
//open pulse audio if no bin or wav
|
||||||
else
|
else //seems this condition is never met
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 0; //not sure if this works or needs to openPulse here
|
//opts->audio_in_type = 5; //not sure if this works or needs to openPulse here
|
||||||
|
fprintf(stderr, "Error, couldn't open input file.\n");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opts->split == 1)
|
if (opts->split == 1)
|
||||||
|
|||||||
@@ -265,31 +265,33 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
|
|
||||||
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
|
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
|
||||||
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
|
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
|
||||||
(state->synctype == 17) || (state->synctype == 29) || (state->synctype == 36) )
|
(state->synctype == 17) || (state->synctype == 29) || (state->synctype == 31) ||
|
||||||
|
(state->synctype == 36) || (state->synctype == 99) )
|
||||||
|
|
||||||
{
|
{
|
||||||
// 1 -P25p1
|
// 1 -P25p1
|
||||||
// 3 -X2-TDMA (inverted signal voice frame)
|
// 3 -X2-TDMA (inverted signal voice frame)
|
||||||
// 5 -X2-TDMA (inverted signal data frame)
|
// 5 -X2-TDMA (inverted signal data frame)
|
||||||
// 9 -NXDN (inverted voice frame)
|
// 9 -M17 LSR
|
||||||
// 11 -DMR (inverted signal voice frame)
|
// 11 -DMR (inverted signal voice frame)
|
||||||
// 13 -DMR (inverted signal data frame)
|
// 13 -DMR (inverted signal data frame)
|
||||||
// 17 -NXDN (inverted data frame)
|
// 17 -M17 STR
|
||||||
// 29 -NXDN (inverted FSW)
|
// 29 -NXDN (inverted FSW)
|
||||||
|
// 31 -YSF
|
||||||
// 36 -P25p2
|
// 36 -P25p2
|
||||||
|
// 99 -M17 Preamble
|
||||||
|
|
||||||
int valid;
|
int valid;
|
||||||
int dibit;
|
int dibit;
|
||||||
|
|
||||||
valid = 0;
|
valid = 0;
|
||||||
|
|
||||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
//testing again, either on Voice channels only (when tuned) or with trunk disabled
|
||||||
//honestly can't say I've seen any improvement when using heuristics, but is detrimental to playing back multiple p25 samples
|
if (state->synctype == 1 && (opts->p25_is_tuned == 1 || opts->p25_trunk == 0) && opts->use_heuristics == 1)
|
||||||
if (state->synctype == 1 && opts->p25_trunk == 0)
|
{
|
||||||
{
|
// Use the P25p1 heuristics if available
|
||||||
// Use the P25 heuristics if available
|
valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||||
//valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (valid == 0)
|
if (valid == 0)
|
||||||
{
|
{
|
||||||
@@ -336,25 +338,26 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
// 0 +P25p1
|
// 0 +P25p1
|
||||||
// 2 +X2-TDMA (non inverted signal data frame)
|
// 2 +X2-TDMA (non inverted signal data frame)
|
||||||
// 4 +X2-TDMA (non inverted signal voice frame)
|
// 4 +X2-TDMA (non inverted signal voice frame)
|
||||||
// 8 +NXDN (non inverted voice frame)
|
// 8 +M17 LSF
|
||||||
// 10 +DMR (non inverted signal data frame)
|
// 10 +DMR (non inverted signal data frame)
|
||||||
// 12 +DMR (non inverted signal voice frame)
|
// 12 +DMR (non inverted signal voice frame)
|
||||||
// 16 +NXDN (non inverted data frame)
|
// 16 +M17 STR
|
||||||
// 28 +NXND (FSW)
|
// 28 +NXND (FSW)
|
||||||
|
// 30 +YSF
|
||||||
// 35 +p25p2
|
// 35 +p25p2
|
||||||
|
// 98 +M17 Preamble
|
||||||
|
|
||||||
int valid;
|
int valid;
|
||||||
int dibit;
|
int dibit;
|
||||||
|
|
||||||
valid = 0;
|
valid = 0;
|
||||||
|
|
||||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
//testing again, either on Voice channels only (when tuned) or with trunk disabled
|
||||||
//honestly can't say I've seen any improvement when using heuristics, but is detrimental to playing back multiple p25 samples
|
if (state->synctype == 0 && (opts->p25_is_tuned == 1 || opts->p25_trunk == 0) && opts->use_heuristics == 1)
|
||||||
if (state->synctype == 0 && opts->p25_trunk == 0)
|
{
|
||||||
{
|
// Use the P25p1 heuristics if available
|
||||||
// Use the P25 heuristics if available
|
valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||||
//valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (valid == 0)
|
if (valid == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
241
src/dsd_file.c
241
src/dsd_file.c
@@ -34,7 +34,11 @@ char * getTimeF(void) //get pretty hh:mm:ss timestamp
|
|||||||
}
|
}
|
||||||
|
|
||||||
char * getDateF(void) {
|
char * getDateF(void) {
|
||||||
char datename[120]; //bug in 32-bit Ubuntu when using date in filename, date is garbage text
|
#ifdef AERO_BUILD
|
||||||
|
char datename[80];
|
||||||
|
#else
|
||||||
|
char datename[99];
|
||||||
|
#endif
|
||||||
char * curr2;
|
char * curr2;
|
||||||
struct tm * to;
|
struct tm * to;
|
||||||
time_t t;
|
time_t t;
|
||||||
@@ -45,8 +49,7 @@ char * getDateF(void) {
|
|||||||
return curr2;
|
return curr2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
||||||
saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
@@ -56,36 +59,22 @@ saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|||||||
fputc (err, opts->mbe_out_f);
|
fputc (err, opts->mbe_out_f);
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "\n IMBE ");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 11; i++)
|
for (i = 0; i < 11; i++)
|
||||||
{
|
{
|
||||||
b = 0;
|
b = 0;
|
||||||
|
|
||||||
for (j = 0; j < 8; j++)
|
for (j = 0; j < 8; j++)
|
||||||
{
|
{
|
||||||
b = b << 1;
|
b = b << 1;
|
||||||
b = b + imbe_d[k];
|
b = b + imbe_d[k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
fputc (b, opts->mbe_out_f);
|
||||||
{
|
}
|
||||||
//fprintf (stderr, "%02X", b);
|
|
||||||
}
|
|
||||||
fputc (b, opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
|
||||||
}
|
|
||||||
fflush (opts->mbe_out_f);
|
fflush (opts->mbe_out_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||||
saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
@@ -95,42 +84,49 @@ saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
fputc (err, opts->mbe_out_f);
|
fputc (err, opts->mbe_out_f);
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
for (i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
//fprintf(stderr, "\n AMBE ");
|
b = 0;
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
b = b << 1;
|
||||||
|
b = b + ambe_d[k];
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
fputc (b, opts->mbe_out_f);
|
||||||
}
|
}
|
||||||
//for (i = 0; i < 6; i++)
|
|
||||||
for (i = 0; i < 6; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
|
|
||||||
{
|
|
||||||
b = 0;
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
b = b << 1;
|
|
||||||
b = b + ambe_d[k];
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
|
|
||||||
{
|
|
||||||
//fprintf (stderr, "%02X", b);
|
|
||||||
}
|
|
||||||
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit? value will be either 0x00 or 0x80?
|
|
||||||
{
|
|
||||||
//fprintf (stderr, "%02X", b & 0x80); //7th octet should only contain 1 bit?
|
|
||||||
}
|
|
||||||
|
|
||||||
fputc (b, opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
|
||||||
}
|
|
||||||
b = ambe_d[48];
|
b = ambe_d[48];
|
||||||
fputc (b, opts->mbe_out_f);
|
fputc (b, opts->mbe_out_f);
|
||||||
fflush (opts->mbe_out_f);
|
fflush (opts->mbe_out_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void saveAmbe2450DataR (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||||
PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d) //for P25P1 and ProVoice
|
{
|
||||||
|
int i, j, k;
|
||||||
|
unsigned char b;
|
||||||
|
unsigned char err;
|
||||||
|
|
||||||
|
err = (unsigned char) state->errs2R;
|
||||||
|
fputc (err, opts->mbe_out_fR);
|
||||||
|
|
||||||
|
k = 0;
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
b = 0;
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
b = b << 1;
|
||||||
|
b = b + ambe_d[k];
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
fputc (b, opts->mbe_out_fR);
|
||||||
|
}
|
||||||
|
b = ambe_d[48];
|
||||||
|
fputc (b, opts->mbe_out_fR);
|
||||||
|
fflush (opts->mbe_out_fR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d) //for P25P1 and ProVoice
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
@@ -165,8 +161,7 @@ PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d) //for P25P1 and
|
|||||||
//fprintf (stderr, "\n");
|
//fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||||
PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
@@ -333,14 +328,21 @@ openMbeInFile (dsd_opts * opts, dsd_state * state)
|
|||||||
cookie[2] = fgetc (opts->mbe_in_f);
|
cookie[2] = fgetc (opts->mbe_in_f);
|
||||||
cookie[3] = fgetc (opts->mbe_in_f);
|
cookie[3] = fgetc (opts->mbe_in_f);
|
||||||
cookie[4] = 0;
|
cookie[4] = 0;
|
||||||
|
//ambe+2
|
||||||
if (strstr (cookie, ".amb") != NULL)
|
if (strstr (cookie, ".amb") != NULL)
|
||||||
{
|
{
|
||||||
state->mbe_file_type = 1;
|
state->mbe_file_type = 1;
|
||||||
}
|
}
|
||||||
|
//p1 and pv
|
||||||
else if (strstr (cookie, ".imb") != NULL)
|
else if (strstr (cookie, ".imb") != NULL)
|
||||||
{
|
{
|
||||||
state->mbe_file_type = 0;
|
state->mbe_file_type = 0;
|
||||||
}
|
}
|
||||||
|
//d-star ambe
|
||||||
|
else if (strstr (cookie, ".dmb") != NULL)
|
||||||
|
{
|
||||||
|
state->mbe_file_type = 2;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state->mbe_file_type = -1;
|
state->mbe_file_type = -1;
|
||||||
@@ -349,7 +351,7 @@ openMbeInFile (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//much simpler version
|
//slot 1
|
||||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
if (opts->mbe_out == 1)
|
if (opts->mbe_out == 1)
|
||||||
@@ -360,48 +362,67 @@ void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
fclose (opts->mbe_out_f);
|
fclose (opts->mbe_out_f);
|
||||||
opts->mbe_out_f = NULL;
|
opts->mbe_out_f = NULL;
|
||||||
opts->mbe_out = 0;
|
opts->mbe_out = 0;
|
||||||
fprintf (stderr, "\nClosing MBE out file.");
|
fprintf (stderr, "\nClosing MBE out file 1.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
//slot 2
|
||||||
openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
void closeMbeOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
if (opts->mbe_outR == 1)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_fR != NULL)
|
||||||
|
{
|
||||||
|
fflush (opts->mbe_out_fR);
|
||||||
|
fclose (opts->mbe_out_fR);
|
||||||
|
opts->mbe_out_fR = NULL;
|
||||||
|
opts->mbe_outR = 0;
|
||||||
|
fprintf (stderr, "\nClosing MBE out file 2.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i, j;
|
int i, j;
|
||||||
char ext[5];
|
char ext[5];
|
||||||
|
|
||||||
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
//phase 1 and provoice
|
||||||
{
|
if ( (state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15) )
|
||||||
sprintf (ext, ".imb");
|
{
|
||||||
}
|
sprintf (ext, ".imb");
|
||||||
else
|
}
|
||||||
{
|
//d-star
|
||||||
sprintf (ext, ".amb");
|
else if ( (state->synctype == 6) || (state->synctype == 7) || (state->synctype == 18) || (state->synctype == 19) )
|
||||||
}
|
{
|
||||||
|
sprintf (ext, ".dmb"); //new dstar file extension to make it read in and process properly
|
||||||
|
}
|
||||||
|
//dmr, nxdn, phase 2, x2-tdma
|
||||||
|
else sprintf (ext, ".amb");
|
||||||
|
|
||||||
// reset talkgroup id buffer
|
//reset talkgroup id buffer
|
||||||
for (i = 0; i < 12; i++)
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < 25; j++)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 25; j++)
|
state->tg[j][i] = 0;
|
||||||
{
|
|
||||||
state->tg[j][i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state->tgcount = 0;
|
state->tgcount = 0;
|
||||||
|
|
||||||
sprintf (opts->mbe_out_file, "%s %s%s", getDateF(), getTimeF(), ext);
|
sprintf (opts->mbe_out_file, "%s %s S1%s", getDateF(), getTimeF(), ext);
|
||||||
|
|
||||||
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
|
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
|
||||||
|
|
||||||
opts->mbe_out_f = fopen (opts->mbe_out_path, "w");
|
opts->mbe_out_f = fopen (opts->mbe_out_path, "w");
|
||||||
if (opts->mbe_out_f == NULL)
|
if (opts->mbe_out_f == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Error, couldn't open %s\n", opts->mbe_out_path);
|
fprintf (stderr,"\nError, couldn't open %s for slot 1\n", opts->mbe_out_path);
|
||||||
}
|
}
|
||||||
else opts->mbe_out = 1;
|
else opts->mbe_out = 1;
|
||||||
|
|
||||||
@@ -411,8 +432,54 @@ openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
fflush (opts->mbe_out_f);
|
fflush (opts->mbe_out_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void openMbeOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
openWavOutFile (dsd_opts * opts, dsd_state * state)
|
{
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
char ext[5];
|
||||||
|
|
||||||
|
//phase 1 and provoice
|
||||||
|
if ( (state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15) )
|
||||||
|
{
|
||||||
|
sprintf (ext, ".imb");
|
||||||
|
}
|
||||||
|
//d-star
|
||||||
|
else if ( (state->synctype == 6) || (state->synctype == 7) || (state->synctype == 18) || (state->synctype == 19) )
|
||||||
|
{
|
||||||
|
sprintf (ext, ".dmb"); //new dstar file extension to make it read in and process properly
|
||||||
|
}
|
||||||
|
//dmr, nxdn, phase 2, x2-tdma
|
||||||
|
else sprintf (ext, ".amb");
|
||||||
|
|
||||||
|
//reset talkgroup id buffer
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < 25; j++)
|
||||||
|
{
|
||||||
|
state->tg[j][i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state->tgcount = 0;
|
||||||
|
|
||||||
|
sprintf (opts->mbe_out_fileR, "%s %s S2%s", getDateF(), getTimeF(), ext);
|
||||||
|
|
||||||
|
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_fileR);
|
||||||
|
|
||||||
|
opts->mbe_out_fR = fopen (opts->mbe_out_path, "w");
|
||||||
|
if (opts->mbe_out_fR == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"\nError, couldn't open %s for slot 2\n", opts->mbe_out_path);
|
||||||
|
}
|
||||||
|
else opts->mbe_outR = 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
fprintf (opts->mbe_out_fR, "%s", ext);
|
||||||
|
|
||||||
|
fflush (opts->mbe_out_fR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void openWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
SF_INFO info;
|
SF_INFO info;
|
||||||
|
|||||||
@@ -1,8 +1,36 @@
|
|||||||
|
//M17 Filter -- RRC Alpha = 0.5 S/s 48000 with 81 taps
|
||||||
|
#define M17ZEROS 80 //ONLY SEE 79 THOUGH
|
||||||
|
float m17gain = 10.0f;
|
||||||
|
static float m17xv[M17ZEROS+1];
|
||||||
|
float m17coeffs[81] =
|
||||||
|
{
|
||||||
|
-0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491,
|
||||||
|
0.01071805138282269, 0.01505751553351295, 0.01679337935001369, 0.015256245142156299,
|
||||||
|
0.01042830577908502, 0.003031522725559901, -0.0055333532968188165, -0.013403099825723372,
|
||||||
|
-0.018598682349642525, -0.01944761739590459, -0.015005271935951746, -0.0053887880354343935,
|
||||||
|
0.008056525910253532, 0.022816244158307273, 0.035513467692208076, 0.04244131815783876,
|
||||||
|
0.04025481153629372, 0.02671818654865632, 0.0013810216516704976, -0.03394615682795165,
|
||||||
|
-0.07502635967975885, -0.11540977897637611, -0.14703962203941534, -0.16119995609538576,
|
||||||
|
-0.14969512896336504, -0.10610329539459686, -0.026921412469634916, 0.08757875030779196,
|
||||||
|
0.23293327870303457, 0.4006012210123992, 0.5786324696325503, 0.7528286479934068,
|
||||||
|
0.908262741447522, 1.0309661131633199, 1.1095611856548013, 1.1366197723675815,
|
||||||
|
1.1095611856548013, 1.0309661131633199, 0.908262741447522, 0.7528286479934068,
|
||||||
|
0.5786324696325503, 0.4006012210123992, 0.23293327870303457, 0.08757875030779196,
|
||||||
|
-0.026921412469634916, -0.10610329539459686, -0.14969512896336504, -0.16119995609538576,
|
||||||
|
-0.14703962203941534, -0.11540977897637611, -0.07502635967975885, -0.03394615682795165,
|
||||||
|
0.0013810216516704976, 0.02671818654865632, 0.04025481153629372, 0.04244131815783876,
|
||||||
|
0.035513467692208076, 0.022816244158307273, 0.008056525910253532, -0.0053887880354343935,
|
||||||
|
-0.015005271935951746, -0.01944761739590459, -0.018598682349642525, -0.013403099825723372,
|
||||||
|
-0.0055333532968188165, 0.003031522725559901, 0.01042830577908502, 0.015256245142156299,
|
||||||
|
0.01679337935001369, 0.01505751553351295, 0.01071805138282269, 0.004891777252042491,
|
||||||
|
-0.001125978562075172, -0.006136551625729697, -0.009265784007800534
|
||||||
|
};
|
||||||
|
|
||||||
// Original DMR filter
|
// Original DMR filter
|
||||||
#define NZEROS 60
|
#define NZEROS 60
|
||||||
float ngain = 7.423339364f;
|
float ngain = 7.423339364f;
|
||||||
static float xv[NZEROS+1];
|
static float xv[NZEROS+1];
|
||||||
float xcoeffs[] =
|
float xcoeffs[61] =
|
||||||
{ -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f,
|
{ -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f,
|
||||||
-0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f,
|
-0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f,
|
||||||
+0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f,
|
+0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f,
|
||||||
@@ -27,7 +55,7 @@ float xcoeffs[] =
|
|||||||
float nxgain = 15.95930463f;
|
float nxgain = 15.95930463f;
|
||||||
static float nxv[NXZEROS+1];
|
static float nxv[NXZEROS+1];
|
||||||
|
|
||||||
float nxcoeffs[] =
|
float nxcoeffs[135] =
|
||||||
{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
||||||
+0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
+0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
||||||
-0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
-0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
||||||
@@ -74,7 +102,7 @@ const float dmrgain = 6.82973073748f;
|
|||||||
static float dxv[DMRNZEROS+1];
|
static float dxv[DMRNZEROS+1];
|
||||||
|
|
||||||
// DMR filter F4EXB - root raised cosine alpha=0.7 Ts = 6650 S/s Fc = 48kHz
|
// DMR filter F4EXB - root raised cosine alpha=0.7 Ts = 6650 S/s Fc = 48kHz
|
||||||
const float dmrcoeffs[] =
|
const float dmrcoeffs[61] =
|
||||||
{0.0301506278, 0.0269200615, 0.0159662432, -0.0013114705, -0.0216605133,
|
{0.0301506278, 0.0269200615, 0.0159662432, -0.0013114705, -0.0216605133,
|
||||||
-0.0404938748, -0.0528141756, -0.0543747957, -0.0428325003, -0.0186176083,
|
-0.0404938748, -0.0528141756, -0.0543747957, -0.0428325003, -0.0186176083,
|
||||||
0.0147202645, 0.0508418571, 0.0816392577, 0.0988113688, 0.0957187780,
|
0.0147202645, 0.0508418571, 0.0816392577, 0.0988113688, 0.0957187780,
|
||||||
@@ -98,7 +126,7 @@ static float dpmrxv[NXZEROS+1];
|
|||||||
const float dpmrgain = 14.6083498224f;
|
const float dpmrgain = 14.6083498224f;
|
||||||
|
|
||||||
// dPMR filter - root raised cosine alpha=0.2 Ts = 3325 S/s Fc = 48 kHz - zero at boundaries appears to be slightly better
|
// dPMR filter - root raised cosine alpha=0.2 Ts = 3325 S/s Fc = 48 kHz - zero at boundaries appears to be slightly better
|
||||||
const float dpmrcoeffs[] =
|
const float dpmrcoeffs[135] =
|
||||||
{-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816,
|
{-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816,
|
||||||
0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307,
|
0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307,
|
||||||
0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763,
|
0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763,
|
||||||
@@ -156,6 +184,11 @@ dpmr_filter(short sample)
|
|||||||
return dsd_input_filter(sample, 4);
|
return dsd_input_filter(sample, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
short m17_filter (short sample)
|
||||||
|
{
|
||||||
|
return dsd_input_filter(sample, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
short
|
short
|
||||||
dsd_input_filter(short sample, int mode)
|
dsd_input_filter(short sample, int mode)
|
||||||
@@ -189,6 +222,14 @@ dsd_input_filter(short sample, int mode)
|
|||||||
coeffs = (float *)dpmrcoeffs;
|
coeffs = (float *)dpmrcoeffs;
|
||||||
zeros = DPMRNZEROS;
|
zeros = DPMRNZEROS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
gain = m17gain;
|
||||||
|
v = m17xv;
|
||||||
|
coeffs = (float *)m17coeffs;
|
||||||
|
zeros = M17ZEROS;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|||||||
417
src/dsd_frame.c
417
src/dsd_frame.c
@@ -87,8 +87,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
//NXDN FSW
|
//NXDN FSW
|
||||||
if ((state->synctype == 28) || (state->synctype == 29))
|
if ((state->synctype == 28) || (state->synctype == 29))
|
||||||
{
|
{
|
||||||
nxdn_frame (opts, state);
|
//MBEout restored, is not handled internally by nxdn_frame.c
|
||||||
//no MBEout...use symbol capture bin instead!
|
nxdn_frame (opts, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 6) || (state->synctype == 7))
|
else if ((state->synctype == 6) || (state->synctype == 7))
|
||||||
@@ -107,7 +107,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
{
|
{
|
||||||
openMbeOutFile (opts, state);
|
openMbeOutFile (opts, state); //dstar ambe files are read back incorrectly, need to change file extension to differentiate when doing a playback
|
||||||
}
|
}
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
processDSTAR (opts, state);
|
processDSTAR (opts, state);
|
||||||
@@ -128,9 +128,9 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
{
|
{
|
||||||
openMbeOutFile (opts, state);
|
openMbeOutFile (opts, state); //dstar ambe files are read back incorrectly, need to change file extension to differentiate when doing a playback
|
||||||
}
|
}
|
||||||
sprintf (state->fsubtype, " DATA ");
|
sprintf (state->fsubtype, " DATA ");
|
||||||
processDSTAR_HD (opts, state);
|
processDSTAR_HD (opts, state);
|
||||||
return;
|
return;
|
||||||
@@ -141,9 +141,9 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
//print manufacturer strings to branding, disabled 0x10 moto other systems can use that fid set
|
//print manufacturer strings to branding, disabled 0x10 moto other systems can use that fid set
|
||||||
//0x06 is trident, but when searching, apparently, they developed con+, but was bought by moto?
|
//0x06 is trident, but when searching, apparently, they developed con+, but was bought by moto?
|
||||||
if (state->dmr_mfid == 0x10) ; //sprintf (state->dmr_branding, "%s", "Motorola");
|
if (state->dmr_mfid == 0x10) ; //sprintf (state->dmr_branding, "%s", "Motorola");
|
||||||
else if (state->dmr_mfid == 0x68) sprintf (state->dmr_branding, "%s", "Hytera");
|
else if (state->dmr_mfid == 0x68) sprintf (state->dmr_branding, "%s", " Hytera");
|
||||||
else if (state->dmr_mfid == 0x58) sprintf (state->dmr_branding, "%s", "Tait");
|
else if (state->dmr_mfid == 0x58) sprintf (state->dmr_branding, "%s", " Tait");
|
||||||
|
|
||||||
//disabling these due to random data decodes setting an odd mfid, could be legit, but only for that one packet?
|
//disabling these due to random data decodes setting an odd mfid, could be legit, but only for that one packet?
|
||||||
//or, its just a decode error somewhere
|
//or, its just a decode error somewhere
|
||||||
@@ -164,89 +164,79 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
//disable so radio id doesn't blink in and out during ncurses and aggressive_framesync
|
//disable so radio id doesn't blink in and out during ncurses and aggressive_framesync
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
//state->lastsrc = 0;
|
|
||||||
//state->lasttg = 0;
|
|
||||||
//state->lastsrcR = 0;
|
|
||||||
//state->lasttgR = 0;
|
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
if (opts->verbose > 0)
|
||||||
{
|
{
|
||||||
if (opts->verbose > 0)
|
level = (int) state->max / 164;
|
||||||
{
|
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||||
level = (int) state->max / 164;
|
|
||||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ( (state->synctype == 11) || (state->synctype == 12) || (state->synctype == 32) ) //DMR Voice Modes
|
if ( (state->synctype == 11) || (state->synctype == 12) || (state->synctype == 32) ) //DMR Voice Modes
|
||||||
|
{
|
||||||
|
|
||||||
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
|
if (opts->dmr_stereo == 0 && state->synctype < 32)
|
||||||
{
|
{
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL) && opts->dmr_stereo == 0)
|
sprintf (state->slot1light, " slot1 ");
|
||||||
{
|
sprintf (state->slot2light, " slot2 ");
|
||||||
openMbeOutFile (opts, state);
|
//we can safely open MBE on any MS or mono handling
|
||||||
}
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state);
|
||||||
if (opts->dmr_stereo == 0 && state->synctype < 32) // -T option for DMR (TDMA) stereo
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, " slot1 ");
|
|
||||||
sprintf (state->slot2light, " slot2 ");
|
|
||||||
dmrMSBootstrap (opts, state);
|
|
||||||
}
|
|
||||||
if (opts->dmr_mono == 1 && state->synctype == 32)
|
|
||||||
{
|
|
||||||
// fprintf (stderr, "%s", KRED);
|
|
||||||
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
|
||||||
// fprintf (stderr, "%s", KNRM);
|
|
||||||
dmrMSBootstrap (opts, state);
|
|
||||||
}
|
|
||||||
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
|
|
||||||
{
|
|
||||||
state->dmr_stereo = 1; //set the state to 1 when handling pure voice frames
|
|
||||||
if (state->synctype > 31 )
|
|
||||||
{
|
|
||||||
dmrMSBootstrap (opts, state); //bootstrap into MS Bootstrap (voice only)
|
|
||||||
}
|
|
||||||
else dmrBSBootstrap (opts, state); //bootstrap into BS Bootstrap
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (opts->dmr_mono == 1 && state->synctype == 32)
|
||||||
|
{
|
||||||
|
//we can safely open MBE on any MS or mono handling
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state);
|
||||||
|
}
|
||||||
|
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
|
||||||
|
{
|
||||||
|
state->dmr_stereo = 1; //set the state to 1 when handling pure voice frames
|
||||||
|
if (state->synctype > 31 )
|
||||||
|
{
|
||||||
|
//we can safely open MBE on any MS or mono handling
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state); //bootstrap into MS Bootstrap (voice only)
|
||||||
|
}
|
||||||
|
else dmrBSBootstrap (opts, state); //bootstrap into BS Bootstrap
|
||||||
|
}
|
||||||
|
}
|
||||||
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
|
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
|
||||||
{
|
{
|
||||||
dmrMSData (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
// if (opts->dmr_stereo == 0)
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
// {
|
if (opts->p25_trunk == 0) dmrMSData (opts, state);
|
||||||
// closeMbeOutFile (opts, state);
|
|
||||||
// state->err_str[0] = 0;
|
|
||||||
// fprintf (stderr, "%s", KRED);
|
|
||||||
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
|
||||||
// fprintf (stderr, "%s", KNRM);
|
|
||||||
// }
|
|
||||||
// if (opts->dmr_stereo == 1)
|
|
||||||
// {
|
|
||||||
// dmrMSData (opts, state);
|
|
||||||
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (opts->dmr_stereo == 0)
|
if (opts->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
state->err_str[0] = 0;
|
state->err_str[0] = 0;
|
||||||
sprintf (state->slot1light, " slot1 ");
|
sprintf (state->slot1light, " slot1 ");
|
||||||
sprintf (state->slot2light, " slot2 ");
|
sprintf (state->slot2light, " slot2 ");
|
||||||
state->payload_miP = 0; //zero out when switching back to data sync
|
|
||||||
dmr_data_sync (opts, state);
|
dmr_data_sync (opts, state);
|
||||||
}
|
}
|
||||||
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
|
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
|
||||||
if (opts->dmr_stereo == 1)
|
if (opts->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
state->dmr_stereo = 0; //set the state to zero for handling pure data frames
|
state->dmr_stereo = 0; //set the state to zero for handling pure data frames
|
||||||
sprintf (state->slot1light, " slot1 ");
|
sprintf (state->slot1light, " slot1 ");
|
||||||
sprintf (state->slot2light, " slot2 ");
|
sprintf (state->slot2light, " slot2 ");
|
||||||
state->payload_miP = 0; //zero out when switching back to data sync
|
|
||||||
dmr_data_sync (opts, state);
|
dmr_data_sync (opts, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if ((state->synctype >= 2) && (state->synctype <= 5))
|
//X2-TDMA
|
||||||
|
else if ((state->synctype >= 2) && (state->synctype <= 5))
|
||||||
{
|
{
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
@@ -256,15 +246,15 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
if ((state->synctype == 3) || (state->synctype == 4))
|
if ((state->synctype == 3) || (state->synctype == 4))
|
||||||
{
|
{
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
{
|
{
|
||||||
openMbeOutFile (opts, state);
|
openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
processX2TDMAvoice (opts, state);
|
processX2TDMAvoice (opts, state);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
state->err_str[0] = 0;
|
state->err_str[0] = 0;
|
||||||
processX2TDMAdata (opts, state);
|
processX2TDMAdata (opts, state);
|
||||||
}
|
}
|
||||||
@@ -272,21 +262,18 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
else if ((state->synctype == 14) || (state->synctype == 15))
|
||||||
{
|
{
|
||||||
//state->nac = 0;
|
|
||||||
//state->lastsrc = 0;
|
|
||||||
//state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
if (opts->verbose > 0)
|
||||||
{
|
{
|
||||||
if (opts->verbose > 0)
|
level = (int) state->max / 164;
|
||||||
{
|
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||||
level = (int) state->max / 164;
|
|
||||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
{
|
{
|
||||||
openMbeOutFile (opts, state);
|
openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
processProVoice (opts, state);
|
processProVoice (opts, state);
|
||||||
return;
|
return;
|
||||||
@@ -294,32 +281,32 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
//edacs
|
//edacs
|
||||||
else if ((state->synctype == 37) || (state->synctype == 38))
|
else if ((state->synctype == 37) || (state->synctype == 38))
|
||||||
{
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
edacs (opts, state);
|
edacs (opts, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//ysf
|
//ysf
|
||||||
else if ((state->synctype == 30) || (state->synctype == 31))
|
else if ((state->synctype == 30) || (state->synctype == 31))
|
||||||
{
|
{
|
||||||
//Do stuff
|
//relocate MBEout to inside frame handling -- Not Working Currently for YSF
|
||||||
//fprintf(stderr, "YSF Sync! \n");
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
//openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
//sprintf(state->fsubtype, " VOICE ");
|
|
||||||
processYSF(opts, state);
|
processYSF(opts, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//M17
|
||||||
|
else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) || (state->synctype == 99) || (state->synctype == 98))
|
||||||
|
{
|
||||||
|
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
|
||||||
|
processM17LSF(opts, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
//P25 P2
|
//P25 P2
|
||||||
else if ((state->synctype == 35) || (state->synctype == 36))
|
else if ((state->synctype == 35) || (state->synctype == 36))
|
||||||
{
|
{
|
||||||
//Do stuff
|
//relocate MBEout to inside frame handling
|
||||||
//fprintf(stderr, "YSF Sync! \n");
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
//openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
//sprintf(state->fsubtype, " VOICE ");
|
|
||||||
processP2(opts, state);
|
processP2(opts, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -328,6 +315,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
/* dPMR Frame Sync 1 */
|
/* dPMR Frame Sync 1 */
|
||||||
fprintf(stderr, "dPMR Frame Sync 1 ");
|
fprintf(stderr, "dPMR Frame Sync 1 ");
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 21) || (state->synctype == 25))
|
else if ((state->synctype == 21) || (state->synctype == 25))
|
||||||
{
|
{
|
||||||
@@ -346,12 +334,12 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
{
|
{
|
||||||
openMbeOutFile (opts, state);
|
openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
sprintf(state->fsubtype, " VOICE ");
|
sprintf(state->fsubtype, " VOICE ");
|
||||||
processdPMRvoice (opts, state);
|
processdPMRvoice (opts, state);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -360,11 +348,13 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
/* dPMR Frame Sync 3 */
|
/* dPMR Frame Sync 3 */
|
||||||
fprintf(stderr, "dPMR Frame Sync 3 ");
|
fprintf(stderr, "dPMR Frame Sync 3 ");
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 23) || (state->synctype == 27))
|
else if ((state->synctype == 23) || (state->synctype == 27))
|
||||||
{
|
{
|
||||||
/* dPMR Frame Sync 4 */
|
/* dPMR Frame Sync 4 */
|
||||||
fprintf(stderr, "dPMR Frame Sync 4 ");
|
fprintf(stderr, "dPMR Frame Sync 4 ");
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
//dPMR
|
//dPMR
|
||||||
else //P25
|
else //P25
|
||||||
@@ -463,15 +453,15 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
// Header Data Unit
|
// Header Data Unit
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," HDU\n");
|
fprintf (stderr," HDU\n");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
openMbeOutFile (opts, state);
|
if (opts->mbe_out_f == NULL) openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||||
state->lastp25type = 2;
|
state->lastp25type = 2;
|
||||||
state->dmrburstL = 25;
|
state->dmrburstL = 25;
|
||||||
@@ -483,26 +473,22 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
// Logical Link Data Unit 1
|
// Logical Link Data Unit 1
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," LDU1 ");
|
fprintf (stderr," LDU1 ");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f == NULL)
|
||||||
{
|
{
|
||||||
if (opts->mbe_out_f == NULL)
|
openMbeOutFile (opts, state);
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
state->lastp25type = 1;
|
state->lastp25type = 1;
|
||||||
state->dmrburstL = 26;
|
state->dmrburstL = 26;
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
sprintf (state->fsubtype, " LDU1 ");
|
sprintf (state->fsubtype, " LDU1 ");
|
||||||
state->numtdulc = 0;
|
state->numtdulc = 0;
|
||||||
if (state->payload_algid == 0x81)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
processLDU1 (opts, state);
|
processLDU1 (opts, state);
|
||||||
}
|
}
|
||||||
@@ -514,27 +500,27 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->lastp25type != 1)
|
if (state->lastp25type != 1)
|
||||||
{
|
{
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr,"\n Ignoring LDU2 not preceeded by LDU1\n");
|
fprintf (stderr,"\n Ignoring LDU2 not preceeded by LDU1\n");
|
||||||
}
|
}
|
||||||
state->lastp25type = 0;
|
state->lastp25type = 0;
|
||||||
sprintf (state->fsubtype, " ");
|
sprintf (state->fsubtype, " ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," LDU2 ");
|
fprintf (stderr," LDU2 ");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f == NULL)
|
||||||
{
|
{
|
||||||
if (opts->mbe_out_f == NULL)
|
openMbeOutFile (opts, state);
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
state->lastp25type = 2;
|
state->lastp25type = 2;
|
||||||
sprintf (state->fsubtype, " LDU2 ");
|
sprintf (state->fsubtype, " LDU2 ");
|
||||||
state->numtdulc = 0;
|
state->numtdulc = 0;
|
||||||
@@ -546,25 +532,25 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
// Terminator with subsequent Link Control
|
// Terminator with subsequent Link Control
|
||||||
state->dmrburstL = 28;
|
state->dmrburstL = 28;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," TDULC\n");
|
fprintf (stderr," TDULC\n");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||||
state->lasttg = 0;
|
// state->lasttg = 0;
|
||||||
state->lastsrc = 0;
|
// state->lastsrc = 0;
|
||||||
state->lastp25type = 0;
|
state->lastp25type = 0;
|
||||||
state->err_str[0] = 0;
|
state->err_str[0] = 0;
|
||||||
sprintf (state->fsubtype, " TDULC ");
|
sprintf (state->fsubtype, " TDULC ");
|
||||||
state->numtdulc++;
|
state->numtdulc++;
|
||||||
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
|
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
|
||||||
{
|
{
|
||||||
resumeScan (opts, state);
|
resumeScan (opts, state);
|
||||||
}
|
}
|
||||||
processTDULC (opts, state);
|
processTDULC (opts, state);
|
||||||
state->err_str[0] = 0;
|
state->err_str[0] = 0;
|
||||||
}
|
}
|
||||||
@@ -573,14 +559,14 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
// Terminator without subsequent Link Control
|
// Terminator without subsequent Link Control
|
||||||
state->dmrburstL = 28;
|
state->dmrburstL = 28;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," TDU\n");
|
fprintf (stderr," TDU\n");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||||
state->lasttg = 0;
|
state->lasttg = 0;
|
||||||
state->lastsrc = 0;
|
state->lastsrc = 0;
|
||||||
@@ -594,120 +580,59 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
state->dmrburstL = 29;
|
state->dmrburstL = 29;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," TSBK");
|
fprintf (stderr," TSBK");
|
||||||
}
|
}
|
||||||
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
if (opts->resume > 0)
|
if (opts->resume > 0)
|
||||||
{
|
{
|
||||||
resumeScan (opts, state);
|
resumeScan (opts, state);
|
||||||
}
|
}
|
||||||
state->lasttg = 0;
|
state->lasttg = 0;
|
||||||
state->lastsrc = 0;
|
state->lastsrc = 0;
|
||||||
state->lastp25type = 3;
|
state->lastp25type = 3;
|
||||||
sprintf (state->fsubtype, " TSBK ");
|
sprintf (state->fsubtype, " TSBK ");
|
||||||
|
|
||||||
// Now processing NID
|
|
||||||
processTSBK(opts, state);
|
processTSBK(opts, state);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (strcmp (duid, "30") == 0)
|
else if (strcmp (duid, "30") == 0)
|
||||||
{
|
{
|
||||||
|
state->dmrburstL = 29;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," PDU\n");
|
fprintf (stderr," MBF\n"); //multi block format PDU
|
||||||
}
|
}
|
||||||
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
if (opts->resume > 0)
|
if (opts->resume > 0)
|
||||||
{
|
{
|
||||||
resumeScan (opts, state);
|
resumeScan (opts, state);
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastp25type = 4;
|
state->lastp25type = 4;
|
||||||
sprintf (state->fsubtype, " PDU ");
|
sprintf (state->fsubtype, " MBF ");
|
||||||
}
|
|
||||||
// try to guess based on previous frame if unknown type
|
|
||||||
else if (state->lastp25type == 1)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr,"(LDU2) ");
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//state->lastp25type = 0;
|
|
||||||
// Guess that the state is LDU2
|
|
||||||
state->lastp25type = 2;
|
|
||||||
sprintf (state->fsubtype, "(LDU2) ");
|
|
||||||
state->numtdulc = 0;
|
|
||||||
processLDU2 (opts, state);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 2)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr,"(LDU1) ");
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//state->lastp25type = 0;
|
|
||||||
// Guess that the state is LDU1
|
|
||||||
state->lastp25type = 1;
|
|
||||||
sprintf (state->fsubtype, "(LDU1) ");
|
|
||||||
state->numtdulc = 0;
|
|
||||||
processLDU1 (opts, state);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 3)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr," (TSBK)\n");
|
|
||||||
}
|
|
||||||
//state->lastp25type = 0;
|
|
||||||
// Guess that the state is TSBK
|
|
||||||
state->lastp25type = 3;
|
|
||||||
sprintf (state->fsubtype, "(TSBK) ");
|
|
||||||
|
|
||||||
// Now processing NID
|
processMPDU(opts, state);
|
||||||
skipDibit (opts, state, 328-25);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 4)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr," (PDU)\n");
|
|
||||||
}
|
|
||||||
state->lastp25type = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
state->lastp25type = 0;
|
||||||
|
sprintf (state->fsubtype, " ");
|
||||||
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
state->lastp25type = 0;
|
printFrameInfo (opts, state);
|
||||||
sprintf (state->fsubtype, " ");
|
// fprintf (stderr," duid:%s *Unknown DUID*\n", duid);
|
||||||
if (opts->errorbars == 1)
|
fprintf (stderr," duid:%s \n", duid); //DUID ERR
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr," duid:%s *Unknown DUID*\n", duid); //prints on dPMR frame 3
|
|
||||||
// fprintf (stderr, "\n"); //prints on dPMR frame 3
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,39 +18,6 @@
|
|||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
|
||||||
//borrowed from LEH/DSDcc for 'improved NXDN sync detection'
|
|
||||||
int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr)
|
|
||||||
{
|
|
||||||
|
|
||||||
int Compare = -1;
|
|
||||||
size_t i = 0;
|
|
||||||
int err = 0;
|
|
||||||
int BreakBeforeEnd = 0;
|
|
||||||
|
|
||||||
if(s1 && s2)
|
|
||||||
{
|
|
||||||
for(i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
if(((s1[i] & 0xFF) != '\0') && ((s2[i] & 0xFF) != '\0'))
|
|
||||||
{
|
|
||||||
if((s1[i] & 0xFF) != (s2[i] & 0xFF)) err++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BreakBeforeEnd = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err <= MaxErr) && (BreakBeforeEnd == 0))
|
|
||||||
{
|
|
||||||
Compare = 0;
|
|
||||||
}
|
|
||||||
} /* End if(s1 && s2) */
|
|
||||||
|
|
||||||
return Compare;
|
|
||||||
} /* End strncmperr() */
|
|
||||||
|
|
||||||
char * getTime(void) //get pretty hh:mm:ss timestamp
|
char * getTime(void) //get pretty hh:mm:ss timestamp
|
||||||
{
|
{
|
||||||
time_t t = time(NULL);
|
time_t t = time(NULL);
|
||||||
@@ -67,7 +34,11 @@ char * getTime(void) //get pretty hh:mm:ss timestamp
|
|||||||
}
|
}
|
||||||
|
|
||||||
char * getDate(void) {
|
char * getDate(void) {
|
||||||
char datename[32];
|
#ifdef AERO_BUILD
|
||||||
|
char datename[80];
|
||||||
|
#else
|
||||||
|
char datename[99];
|
||||||
|
#endif
|
||||||
char * curr2;
|
char * curr2;
|
||||||
struct tm * to;
|
struct tm * to;
|
||||||
time_t t;
|
time_t t;
|
||||||
@@ -114,16 +85,16 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
* 5 = -X2-TDMA (inverted signal data frame)
|
* 5 = -X2-TDMA (inverted signal data frame)
|
||||||
* 6 = +D-STAR
|
* 6 = +D-STAR
|
||||||
* 7 = -D-STAR
|
* 7 = -D-STAR
|
||||||
* 8 = +NXDN (non inverted voice frame)
|
* 8 = +M17 STR (non inverted stream frame)
|
||||||
* 9 = -NXDN (inverted voice frame)
|
* 9 = -M17 STR (inverted stream frame)
|
||||||
* 10 = +DMR (non inverted signal data frame)
|
* 10 = +DMR (non inverted signal data frame)
|
||||||
* 11 = -DMR (inverted signal voice frame)
|
* 11 = -DMR (inverted signal voice frame)
|
||||||
* 12 = +DMR (non inverted signal voice frame)
|
* 12 = +DMR (non inverted signal voice frame)
|
||||||
* 13 = -DMR (inverted signal data frame)
|
* 13 = -DMR (inverted signal data frame)
|
||||||
* 14 = +ProVoice
|
* 14 = +ProVoice
|
||||||
* 15 = -ProVoice
|
* 15 = -ProVoice
|
||||||
* 16 = +NXDN (non inverted data frame)
|
* 16 = +M17 LSF (non inverted link frame)
|
||||||
* 17 = -NXDN (inverted data frame)
|
* 17 = -M17 LSF (inverted link frame)
|
||||||
* 18 = +D-STAR_HD
|
* 18 = +D-STAR_HD
|
||||||
* 19 = -D-STAR_HD
|
* 19 = -D-STAR_HD
|
||||||
* 20 = +dPMR Frame Sync 1
|
* 20 = +dPMR Frame Sync 1
|
||||||
@@ -150,7 +121,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
//start control channel hunting if using trunking, time needs updating on each successful sync
|
//start control channel hunting if using trunking, time needs updating on each successful sync
|
||||||
//will need to assign frequencies to a CC array for P25 since that isn't imported from CSV
|
//will need to assign frequencies to a CC array for P25 since that isn't imported from CSV
|
||||||
if (state->dmr_rest_channel == -1 && opts->p25_is_tuned == 0 && opts->p25_trunk == 1 && ( (time(NULL) - state->last_cc_sync_time) > (opts->trunk_hangtime + 2) ) )
|
if (state->dmr_rest_channel == -1 && opts->p25_is_tuned == 0 && opts->p25_trunk == 1 && ( (time(NULL) - state->last_cc_sync_time) > (opts->trunk_hangtime + 0) ) ) //was 3, go to hangtime value
|
||||||
{
|
{
|
||||||
|
|
||||||
//test to switch back to 10/4 P1 QPSK for P25 FDMA CC
|
//test to switch back to 10/4 P1 QPSK for P25 FDMA CC
|
||||||
@@ -163,10 +134,23 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
//start going through the lcn/frequencies CC/signal hunting
|
//start going through the lcn/frequencies CC/signal hunting
|
||||||
fprintf (stderr, "Control Channel Signal Lost. Searching for Control Channel.\n");
|
fprintf (stderr, "Control Channel Signal Lost. Searching for Control Channel.\n");
|
||||||
//make sure our current roll value doesn't exceed value of frequencies imported
|
//make sure our current roll value doesn't exceed value of frequencies imported
|
||||||
if (state->lcn_freq_roll > state->lcn_freq_count)
|
if (state->lcn_freq_roll >= state->lcn_freq_count) //fixed this to skip the extra wait out at the end of the list
|
||||||
{
|
{
|
||||||
state->lcn_freq_roll = 0; //reset to zero
|
state->lcn_freq_roll = 0; //reset to zero
|
||||||
}
|
}
|
||||||
|
//roll an extra value up if the current is the same as what's already loaded -- faster hunting on Cap+, etc
|
||||||
|
if (state->lcn_freq_roll != 0)
|
||||||
|
{
|
||||||
|
if (state->trunk_lcn_freq[state->lcn_freq_roll-1] == state->trunk_lcn_freq[state->lcn_freq_roll])
|
||||||
|
{
|
||||||
|
state->lcn_freq_roll++;
|
||||||
|
//check roll again if greater than expected, then go back to zero
|
||||||
|
if (state->lcn_freq_roll >= state->lcn_freq_count)
|
||||||
|
{
|
||||||
|
state->lcn_freq_roll = 0; //reset to zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
//check that we have a non zero value first, then tune next frequency
|
//check that we have a non zero value first, then tune next frequency
|
||||||
if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0)
|
if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0)
|
||||||
{
|
{
|
||||||
@@ -176,13 +160,15 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
||||||
}
|
}
|
||||||
//rtludp
|
//rtl
|
||||||
if (opts->audio_in_type == 3)
|
if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr, "Tuning to Control Channel Frequency: %.06lf MHz\n",
|
fprintf (stderr, "Tuning to Frequency: %.06lf MHz\n",
|
||||||
(double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000);
|
(double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -194,12 +180,13 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
char synctest[25];
|
char synctest[25];
|
||||||
char synctest12[13]; //dPMR
|
char synctest12[13]; //dPMR
|
||||||
char synctest10[11]; //NXDN FSW only
|
char synctest10[11]; //NXDN FSW only
|
||||||
char synctest19[20]; //NXDN Preamble + FSW
|
|
||||||
char synctest18[19];
|
char synctest18[19];
|
||||||
char synctest32[33];
|
char synctest32[33];
|
||||||
char synctest20[21]; //YSF
|
char synctest20[21]; //YSF
|
||||||
char synctest21[22]; //P25 S-OEMI (SACCH)
|
char synctest21[22]; //P25 S-OEMI (SACCH)
|
||||||
char synctest48[49]; //EDACS
|
char synctest48[49]; //EDACS
|
||||||
|
char synctest8[9]; //M17
|
||||||
|
char synctest16[17]; //M17 Preamble
|
||||||
char modulation[8];
|
char modulation[8];
|
||||||
char *synctest_p;
|
char *synctest_p;
|
||||||
int symboltest_pos; //symbol test position, match to synctest_pos
|
int symboltest_pos; //symbol test position, match to synctest_pos
|
||||||
@@ -213,19 +200,27 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
t_max = 10;
|
t_max = 10;
|
||||||
}
|
}
|
||||||
//else if dPMR
|
//dPMR
|
||||||
else if (opts->frame_dpmr == 1)
|
else if (opts->frame_dpmr == 1)
|
||||||
{
|
{
|
||||||
t_max = 12; //based on Frame_Sync_2 pattern
|
t_max = 12; //based on Frame_Sync_2 pattern
|
||||||
}
|
}
|
||||||
//if Phase 2 (or YSF in future), then only 20
|
else if (opts->frame_m17 == 1)
|
||||||
else if (state->lastsynctype == 35 || state->lastsynctype == 36) //P2
|
|
||||||
{
|
{
|
||||||
t_max = 20;
|
t_max = 8;
|
||||||
|
}
|
||||||
|
else if (state->lastsynctype == 30 || state->lastsynctype == 31 )
|
||||||
|
{
|
||||||
|
t_max = 20; //20 on YSF
|
||||||
|
}
|
||||||
|
//if Phase 2, then only 19
|
||||||
|
else if (state->lastsynctype == 35 || state->lastsynctype == 36 )
|
||||||
|
{
|
||||||
|
t_max = 19; //Phase 2 S-ISCH is only 19
|
||||||
}
|
}
|
||||||
else t_max = 24; //24 for everything else
|
else t_max = 24; //24 for everything else
|
||||||
|
|
||||||
int lbuf[t_max], lbuf2[t_max];
|
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 lsum;
|
int lsum;
|
||||||
char spectrum[64];
|
char spectrum[64];
|
||||||
//init the lbuf
|
//init the lbuf
|
||||||
@@ -433,6 +428,16 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
state->minref = state->min;
|
state->minref = state->min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
goto SYNC_TEST_END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strncpy (synctest, (synctest_p - 23), 24);
|
strncpy (synctest, (synctest_p - 23), 24);
|
||||||
if (opts->frame_p25p1 == 1)
|
if (opts->frame_p25p1 == 1)
|
||||||
{
|
{
|
||||||
@@ -543,51 +548,138 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//YSF sync
|
//YSF sync
|
||||||
strncpy(synctest20, (synctest_p - 19), 20); //double check make sure this is right
|
strncpy(synctest20, (synctest_p - 19), 20);
|
||||||
if(opts->frame_ysf == 1) //(opts->frame_ysf == 1
|
if(opts->frame_ysf == 1)
|
||||||
{
|
{
|
||||||
if (0 == 0) //opts->inverted_ysf == 0
|
if (strcmp(synctest20, FUSION_SYNC) == 0)
|
||||||
{
|
{
|
||||||
if (strcmp(synctest20, FUSION_SYNC) == 0)
|
printFrameSync (opts, state, "+YSF ", synctest_pos + 1, modulation);
|
||||||
{
|
state->carrier = 1;
|
||||||
state->carrier = 1;
|
state->offset = synctest_pos;
|
||||||
state->offset = synctest_pos;
|
state->max = ((state->max) + lmax) / 2;
|
||||||
state->max = ((state->max) + lmax) / 2;
|
state->min = ((state->min) + lmin) / 2;
|
||||||
state->min = ((state->min) + lmin) / 2;
|
opts->inverted_ysf = 0;
|
||||||
fprintf (stderr, "\nYSF FUSION SYNC \n");
|
state->lastsynctype = 30;
|
||||||
opts->inverted_ysf = 0; //should we set this here?
|
return (30);
|
||||||
state->lastsynctype = 30;
|
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (30);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else if (strcmp(synctest20, INV_FUSION_SYNC) == 0)
|
||||||
if(opts->frame_ysf == 1) //(opts->frame_ysf == 1
|
|
||||||
{
|
|
||||||
if (0 == 0) //opts->inverted_ysf == 1
|
|
||||||
{
|
{
|
||||||
if (strcmp(synctest20, INV_FUSION_SYNC) == 0)
|
printFrameSync (opts, state, "-YSF ", synctest_pos + 1, modulation);
|
||||||
{
|
state->carrier = 1;
|
||||||
state->carrier = 1;
|
state->offset = synctest_pos;
|
||||||
state->offset = synctest_pos;
|
state->max = ((state->max) + lmax) / 2;
|
||||||
state->max = ((state->max) + lmax) / 2;
|
state->min = ((state->min) + lmin) / 2;
|
||||||
state->min = ((state->min) + lmin) / 2;
|
opts->inverted_ysf = 1;
|
||||||
fprintf (stderr, "\nINVERTED YSF FUSION SYNC \n");
|
state->lastsynctype = 31;
|
||||||
opts->inverted_ysf = 1; //should we set this here?
|
return (31);
|
||||||
state->lastsynctype = 31;
|
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (31);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//end YSF sync
|
//end YSF sync
|
||||||
|
|
||||||
|
//M17 Sync -- Just STR and LSF for now
|
||||||
|
strncpy(synctest16, (synctest_p - 15), 16);
|
||||||
|
strncpy(synctest8, (synctest_p - 7), 8);
|
||||||
|
if(opts->frame_m17 == 1)
|
||||||
|
{
|
||||||
|
//preambles will skip dibits in an attempt to prime the
|
||||||
|
//demodulator but not attempt any decoding
|
||||||
|
if (strcmp(synctest8, M17_PRE) == 0)
|
||||||
|
{
|
||||||
|
if (opts->inverted_m17 == 0)
|
||||||
|
{
|
||||||
|
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_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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//end M17
|
||||||
|
|
||||||
//P25 P2 sync S-ISCH VCH
|
//P25 P2 sync S-ISCH VCH
|
||||||
strncpy(synctest20, (synctest_p - 19), 20);
|
strncpy(synctest20, (synctest_p - 19), 20);
|
||||||
if(opts->frame_p25p2 == 1)
|
if(opts->frame_p25p2 == 1)
|
||||||
@@ -618,10 +710,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr, " P2 Missing Parameters ");
|
fprintf (stderr, " P2 Missing Parameters ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (35); //35
|
return (35); //35
|
||||||
}
|
}
|
||||||
@@ -657,10 +745,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
state->lastsynctype = 36; //36
|
state->lastsynctype = 36; //36
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (36); //36
|
return (36); //36
|
||||||
}
|
}
|
||||||
@@ -692,10 +776,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
printFrameSync (opts, state, "+dPMR ", synctest_pos + 1, modulation);
|
printFrameSync (opts, state, "+dPMR ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 21;
|
state->lastsynctype = 21;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (21);
|
return (21);
|
||||||
}
|
}
|
||||||
if(strcmp(synctest12, DPMR_FRAME_SYNC_3) == 0)
|
if(strcmp(synctest12, DPMR_FRAME_SYNC_3) == 0)
|
||||||
@@ -728,10 +808,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
state->lastsynctype = 25;
|
state->lastsynctype = 25;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (25);
|
return (25);
|
||||||
}
|
}
|
||||||
if(strcmp(synctest12, INV_DPMR_FRAME_SYNC_3) == 0)
|
if(strcmp(synctest12, INV_DPMR_FRAME_SYNC_3) == 0)
|
||||||
@@ -770,20 +846,12 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
//state->firstframe = 1;
|
//state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 33; //33
|
state->lastsynctype = 33; //33
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (33); //33
|
return (33); //33
|
||||||
}
|
}
|
||||||
else //inverted MS voice frame
|
else //inverted MS voice frame
|
||||||
{
|
{
|
||||||
sprintf(state->ftype, "DMR MS");
|
sprintf(state->ftype, "DMR MS");
|
||||||
state->lastsynctype = 32;
|
state->lastsynctype = 32;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (32);
|
return (32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -810,10 +878,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
//state->firstframe = 1;
|
//state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 34;
|
state->lastsynctype = 34;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (34);
|
return (34);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -839,20 +903,12 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
//state->firstframe = 1;
|
//state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 32;
|
state->lastsynctype = 32;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (32);
|
return (32);
|
||||||
}
|
}
|
||||||
else //inverted MS data frame
|
else //inverted MS data frame
|
||||||
{
|
{
|
||||||
sprintf(state->ftype, "DMR MS");
|
sprintf(state->ftype, "DMR MS");
|
||||||
state->lastsynctype = 33;
|
state->lastsynctype = 33;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (33);
|
return (33);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -875,10 +931,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
|
printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 10;
|
state->lastsynctype = 10;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (10);
|
return (10);
|
||||||
}
|
}
|
||||||
@@ -895,10 +947,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
state->firstframe = 1;
|
state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 11;
|
state->lastsynctype = 11;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (11); //11
|
return (11); //11
|
||||||
}
|
}
|
||||||
@@ -920,10 +968,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
//printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
|
//printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 33;
|
state->lastsynctype = 33;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (33);
|
return (33);
|
||||||
}
|
}
|
||||||
@@ -940,10 +984,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
state->firstframe = 1;
|
state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 32;
|
state->lastsynctype = 32;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (32);
|
return (32);
|
||||||
}
|
}
|
||||||
@@ -965,10 +1005,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
//printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
|
//printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 33;
|
state->lastsynctype = 33;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (33);
|
return (33);
|
||||||
}
|
}
|
||||||
@@ -985,10 +1021,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
state->firstframe = 1;
|
state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 32;
|
state->lastsynctype = 32;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (32);
|
return (32);
|
||||||
}
|
}
|
||||||
@@ -1014,10 +1046,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
state->firstframe = 1;
|
state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 12;
|
state->lastsynctype = 12;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (12);
|
return (12);
|
||||||
}
|
}
|
||||||
@@ -1031,10 +1059,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
|
printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 13;
|
state->lastsynctype = 13;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (13);
|
return (13);
|
||||||
}
|
}
|
||||||
@@ -1060,10 +1084,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
state->firstframe = 1;
|
state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 32;
|
state->lastsynctype = 32;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (32); //treat Direct Mode same as MS mode for now
|
return (32); //treat Direct Mode same as MS mode for now
|
||||||
}
|
}
|
||||||
@@ -1100,10 +1120,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
state->firstframe = 1;
|
state->firstframe = 1;
|
||||||
}
|
}
|
||||||
state->lastsynctype = 32;
|
state->lastsynctype = 32;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (32);
|
return (32);
|
||||||
}
|
}
|
||||||
@@ -1116,10 +1132,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
//printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
|
//printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 33;
|
state->lastsynctype = 33;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
return (33);
|
return (33);
|
||||||
}
|
}
|
||||||
@@ -1197,11 +1209,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
printFrameSync (opts, state, "+D-STAR ", synctest_pos + 1, modulation);
|
printFrameSync (opts, state, "+D-STAR ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 6;
|
state->lastsynctype = 6;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (6);
|
return (6);
|
||||||
}
|
}
|
||||||
if (strcmp (synctest, INV_DSTAR_SYNC) == 0)
|
if (strcmp (synctest, INV_DSTAR_SYNC) == 0)
|
||||||
@@ -1216,11 +1223,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
printFrameSync (opts, state, "-D-STAR ", synctest_pos + 1, modulation);
|
printFrameSync (opts, state, "-D-STAR ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 7;
|
state->lastsynctype = 7;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (7);
|
return (7);
|
||||||
}
|
}
|
||||||
if (strcmp (synctest, DSTAR_HD) == 0)
|
if (strcmp (synctest, DSTAR_HD) == 0)
|
||||||
@@ -1235,11 +1237,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
printFrameSync (opts, state, "+D-STAR_HD ", synctest_pos + 1, modulation);
|
printFrameSync (opts, state, "+D-STAR_HD ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 18;
|
state->lastsynctype = 18;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (18);
|
return (18);
|
||||||
}
|
}
|
||||||
if (strcmp (synctest, INV_DSTAR_HD) == 0)
|
if (strcmp (synctest, INV_DSTAR_HD) == 0)
|
||||||
@@ -1254,81 +1251,148 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
printFrameSync (opts, state, "-D-STAR_HD ", synctest_pos + 1, modulation);
|
printFrameSync (opts, state, "-D-STAR_HD ", synctest_pos + 1, modulation);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 19;
|
state->lastsynctype = 19;
|
||||||
if ( opts->monitor_input_audio == 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
|
||||||
}
|
|
||||||
return (19);
|
return (19);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//NXDN FSW sync and handling - moved to very bottom of sync stack for falsing sanity
|
//NXDN
|
||||||
else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
|
else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
|
||||||
{
|
{
|
||||||
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
|
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
|
||||||
strncpy (synctest19, (synctest_p - 18), 19); //Preamble + FSW
|
if (
|
||||||
|
(strcmp (synctest10, "3131331131") == 0 ) //this seems to be the most common 'correct' pattern on Type-C
|
||||||
//Preamble plus FSW, proceed right away
|
|| (strcmp (synctest10, "3331331131") == 0 ) //this one hits on new sync but gives a bad lich code
|
||||||
if ( (strncmperr (synctest19, NXDN_PANDFSW, 19, 1) == 0) )
|
|| (strcmp (synctest10, "3131331111") == 0 )
|
||||||
|
|| (strcmp (synctest10, "3331331111") == 0 )
|
||||||
|
|| (strcmp (synctest10, "3131311131") == 0 ) //First few FSW on NXDN48 Type-C seems to hit this for some reason
|
||||||
|
|
||||||
|
)
|
||||||
{
|
{
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
state->offset = synctest_pos;
|
||||||
state->max = ((state->max) + lmax) / 2;
|
state->max = ((state->max) + lmax) / 2;
|
||||||
state->min = ((state->min) + lmin) / 2;
|
state->min = ((state->min) + lmin) / 2;
|
||||||
state->lastsynctype = 28;
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
|
||||||
//if (opts->payload == 1) fprintf (stderr, "PANDF ");
|
|
||||||
return (28);
|
|
||||||
}
|
|
||||||
else if ( (strncmperr (synctest19, INV_NXDN_PANDFSW, 19, 1) == 0) )
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
state->lastsynctype = 29;
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
|
||||||
//if (opts->payload == 1) fprintf (stderr, "PANDF ");
|
|
||||||
return (29);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( (strncmperr (synctest10, NXDN_FSW, 10, 1) == 0) )
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
|
|
||||||
if (state->lastsynctype == 28)
|
if (state->lastsynctype == 28)
|
||||||
{
|
{
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
//if (opts->payload == 1) fprintf (stderr, "FSW ");
|
|
||||||
return (28);
|
return (28);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 28; //need two consecutive patterns to continue
|
state->lastsynctype = 28;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ( (strncmperr (synctest10, INV_NXDN_FSW, 10, 1) == 0) )
|
else if (
|
||||||
|
|
||||||
|
(strcmp (synctest10, "1313113313") == 0 )
|
||||||
|
|| (strcmp (synctest10, "1113113313") == 0 )
|
||||||
|
|| (strcmp (synctest10, "1313113333") == 0 )
|
||||||
|
|| (strcmp (synctest10, "1113113333") == 0 )
|
||||||
|
|| (strcmp (synctest10, "1313133313") == 0 )
|
||||||
|
|
||||||
|
)
|
||||||
{
|
{
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
state->offset = synctest_pos;
|
||||||
state->max = ((state->max) + lmax) / 2;
|
state->max = ((state->max) + lmax) / 2;
|
||||||
state->min = ((state->min) + lmin) / 2;
|
state->min = ((state->min) + lmin) / 2;
|
||||||
|
|
||||||
if (state->lastsynctype == 29)
|
if (state->lastsynctype == 29)
|
||||||
{
|
{
|
||||||
state->last_cc_sync_time = time(NULL);
|
state->last_cc_sync_time = time(NULL);
|
||||||
//if (opts->payload == 1) fprintf (stderr, "FSW ");
|
|
||||||
return (29);
|
return (29);
|
||||||
}
|
}
|
||||||
state->lastsynctype = 29; //need two consecutive patterns to continue
|
state->lastsynctype = 29;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Provoice Conventional -- Some False Positives due to shortened frame sync pattern, so use squelch if possible
|
||||||
|
#ifdef PVCONVENTIONAL
|
||||||
|
if (opts->frame_provoice == 1)
|
||||||
|
{
|
||||||
|
memset (synctest32, 0, sizeof(synctest32));
|
||||||
|
strncpy (synctest32, (synctest_p - 31), 16); //short sync grab here on 32
|
||||||
|
char pvc_txs[9]; //string (symbol) value of TX Address
|
||||||
|
char pvc_rxs[9]; //string (symbol) value of RX Address
|
||||||
|
uint8_t pvc_txa = 0; //actual value of TX Address
|
||||||
|
uint8_t pvc_rxa = 0; //actual value of RX Address
|
||||||
|
strncpy (pvc_txs, (synctest_p - 15), 8); //copy string value of TX Address
|
||||||
|
strncpy (pvc_rxs, (synctest_p - 7), 8); //copy string value of RX Address
|
||||||
|
if ((strcmp (synctest32, INV_PROVOICE_CONV_SHORT) == 0))
|
||||||
|
{
|
||||||
|
if (state->lastsynctype == 15) //use this condition, like NXDN, to migitage false positives due to short sync pattern
|
||||||
|
{
|
||||||
|
state->carrier = 1;
|
||||||
|
state->offset = synctest_pos;
|
||||||
|
state->max = ((state->max) + lmax) / 2;
|
||||||
|
state->min = ((state->min) + lmin) / 2;
|
||||||
|
sprintf (state->ftype, "ProVoice ");
|
||||||
|
// fprintf (stderr, "Sync Pattern = %s ", synctest32);
|
||||||
|
// fprintf (stderr, "TX = %s ", pvc_txs);
|
||||||
|
// fprintf (stderr, "RX = %s ", pvc_rxs);
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
pvc_txa = pvc_txa << 1;
|
||||||
|
pvc_rxa = pvc_rxa << 1;
|
||||||
|
//symbol 1 is binary 1 on inverted
|
||||||
|
//I hate working with strings, has to be a better way to evaluate this
|
||||||
|
memset (pvc_txs, 0, sizeof (pvc_txs));
|
||||||
|
memset (pvc_rxs, 0, sizeof (pvc_rxs));
|
||||||
|
strncpy (pvc_txs, (synctest_p - 15+i), 1);
|
||||||
|
strncpy (pvc_rxs, (synctest_p - 7+i), 1);
|
||||||
|
if ((strcmp (pvc_txs, "1") == 0))
|
||||||
|
pvc_txa = pvc_txa + 1;
|
||||||
|
if ((strcmp (pvc_rxs, "1") == 0))
|
||||||
|
pvc_rxa = pvc_rxa + 1;
|
||||||
|
}
|
||||||
|
printFrameSync (opts, state, "-PV_C ", synctest_pos + 1, modulation);
|
||||||
|
fprintf (stderr, "TX: %d ", pvc_txa);
|
||||||
|
fprintf (stderr, "RX: %d ", pvc_rxa);
|
||||||
|
if (pvc_txa == 172) fprintf (stderr, "ALL CALL ");
|
||||||
|
state->lastsynctype = 15;
|
||||||
|
return (15);
|
||||||
|
}
|
||||||
|
state->lastsynctype = 15;
|
||||||
|
}
|
||||||
|
else if ((strcmp (synctest32, PROVOICE_CONV_SHORT) == 0))
|
||||||
|
{
|
||||||
|
if (state->lastsynctype == 14) //use this condition, like NXDN, to migitage false positives due to short sync pattern
|
||||||
|
{
|
||||||
|
state->carrier = 1;
|
||||||
|
state->offset = synctest_pos;
|
||||||
|
state->max = ((state->max) + lmax) / 2;
|
||||||
|
state->min = ((state->min) + lmin) / 2;
|
||||||
|
sprintf (state->ftype, "ProVoice ");
|
||||||
|
// fprintf (stderr, "Sync Pattern = %s ", synctest32);
|
||||||
|
// fprintf (stderr, "TX = %s ", pvc_txs);
|
||||||
|
// fprintf (stderr, "RX = %s ", pvc_rxs);
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
pvc_txa = pvc_txa << 1;
|
||||||
|
pvc_rxa = pvc_rxa << 1;
|
||||||
|
//symbol 3 is binary 1 on positive
|
||||||
|
//I hate working with strings, has to be a better way to evaluate this
|
||||||
|
memset (pvc_txs, 0, sizeof (pvc_txs));
|
||||||
|
memset (pvc_rxs, 0, sizeof (pvc_rxs));
|
||||||
|
strncpy (pvc_txs, (synctest_p - 15+i), 1);
|
||||||
|
strncpy (pvc_rxs, (synctest_p - 7+i), 1);
|
||||||
|
if ((strcmp (pvc_txs, "3") == 0))
|
||||||
|
pvc_txa = pvc_txa + 1;
|
||||||
|
if ((strcmp (pvc_rxs, "3") == 0))
|
||||||
|
pvc_rxa = pvc_rxa + 1;
|
||||||
|
}
|
||||||
|
printFrameSync (opts, state, "+PV_C ", synctest_pos + 1, modulation);
|
||||||
|
fprintf (stderr, "TX: %d ", pvc_txa);
|
||||||
|
fprintf (stderr, "RX: %d ", pvc_rxa);
|
||||||
|
if (pvc_txa == 172) fprintf (stderr, "ALL CALL ");
|
||||||
|
state->lastsynctype = 14;
|
||||||
|
return (14);
|
||||||
|
}
|
||||||
|
state->lastsynctype = 14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //End Provoice Conventional
|
||||||
|
|
||||||
|
SYNC_TEST_END: ; //do nothing
|
||||||
|
|
||||||
} // t >= 10
|
} // t >= 10
|
||||||
|
|
||||||
if (exitflag == 1)
|
if (exitflag == 1)
|
||||||
@@ -1359,7 +1423,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||||||
if ((opts->errorbars == 1) && (opts->verbose > 1) && (state->carrier == 1))
|
if ((opts->errorbars == 1) && (opts->verbose > 1) && (state->carrier == 1))
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Sync: no sync\n");
|
fprintf (stderr,"Sync: no sync\n");
|
||||||
fprintf (stderr,"Press CTRL + C to close.\n");
|
// fprintf (stderr,"Press CTRL + C to close.\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
noCarrier (opts, state);
|
noCarrier (opts, state);
|
||||||
|
|||||||
111
src/dsd_import.c
111
src/dsd_import.c
@@ -145,3 +145,114 @@ int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Decimal Variant of Key Import
|
||||||
|
int csvKeyImportDec(dsd_opts * opts, dsd_state * state) //multi-key support
|
||||||
|
{
|
||||||
|
char filename[1024] = "filename.csv";
|
||||||
|
sprintf (filename, "%s", opts->key_in_file);
|
||||||
|
|
||||||
|
char buffer[BSIZE];
|
||||||
|
FILE * fp;
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("Unable to open file '%s'\n", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int row_count = 0;
|
||||||
|
int field_count = 0;
|
||||||
|
|
||||||
|
unsigned long long int keynumber = 0;
|
||||||
|
unsigned long long int keyvalue = 0;
|
||||||
|
|
||||||
|
uint16_t hash = 0;
|
||||||
|
uint8_t hash_bits[24];
|
||||||
|
memset (hash_bits, 0, sizeof(hash_bits));
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
if (field_count == 0)
|
||||||
|
{
|
||||||
|
sscanf (field, "%lld", &keynumber);
|
||||||
|
if (keynumber > 0xFFFF) //if larger than 16-bits, get its hash instead
|
||||||
|
{
|
||||||
|
keynumber = keynumber & 0xFFFFFF; //truncate to 24-bits (max allowed)
|
||||||
|
for (int i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
hash_bits[i] = ((keynumber << i) & 0x800000) >> 23; //load into array for CRC16
|
||||||
|
}
|
||||||
|
hash = ComputeCrcCCITT16d (hash_bits, 24);
|
||||||
|
keynumber = hash & 0xFFFF; //make sure its no larger than 16-bits
|
||||||
|
fprintf (stderr, "Hashed ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_count == 1)
|
||||||
|
{
|
||||||
|
sscanf (field, "%lld", &keyvalue);
|
||||||
|
state->rkey_array[keynumber] = keyvalue & 0xFFFFFFFFFF; //doesn't exceed 40-bit value
|
||||||
|
}
|
||||||
|
|
||||||
|
field = strtok(NULL, ",");
|
||||||
|
field_count++;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "Key [%03lld] [%05lld]", keynumber, state->rkey_array[keynumber]);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
hash = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Hex Variant of Key Import
|
||||||
|
int csvKeyImportHex(dsd_opts * opts, dsd_state * state) //key import for rc4 keys
|
||||||
|
{
|
||||||
|
char filename[1024] = "filename.csv";
|
||||||
|
sprintf (filename, "%s", opts->key_in_file);
|
||||||
|
char buffer[BSIZE];
|
||||||
|
FILE * fp;
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("Unable to open file '%s'\n", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int row_count = 0;
|
||||||
|
int field_count = 0;
|
||||||
|
unsigned long long int keynumber;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
if (field_count == 0)
|
||||||
|
{
|
||||||
|
sscanf (field, "%llX", &keynumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_count == 1)
|
||||||
|
{
|
||||||
|
sscanf (field, "%llX", &state->rkey_array[keynumber]);
|
||||||
|
}
|
||||||
|
|
||||||
|
field = strtok(NULL, ",");
|
||||||
|
field_count++;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "Key [%04llX] [%016llX]", keynumber, state->rkey_array[keynumber]);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
1273
src/dsd_main.c
1273
src/dsd_main.c
File diff suppressed because it is too large
Load Diff
1382
src/dsd_mbe.c
1382
src/dsd_mbe.c
File diff suppressed because it is too large
Load Diff
1953
src/dsd_ncurses.c
1953
src/dsd_ncurses.c
File diff suppressed because it is too large
Load Diff
164
src/dsd_reset.c
164
src/dsd_reset.c
@@ -2,26 +2,59 @@
|
|||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
#include "p25p1_heuristics.h"
|
#include "p25p1_heuristics.h"
|
||||||
|
|
||||||
void
|
//fixed the memory leak, but now random segfaults occur -- double free or corruption (out) or (!prev)
|
||||||
resetState (dsd_state * state)
|
void resetState (dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
//Dibit Buffer -- Free Allocated Memory
|
||||||
|
// free (state->dibit_buf);
|
||||||
|
|
||||||
|
//Dibit Buffer -- Memset/Init/Allocate Memory
|
||||||
// state->dibit_buf = malloc (sizeof (int) * 1000000);
|
// state->dibit_buf = malloc (sizeof (int) * 1000000);
|
||||||
// state->dibit_buf_p = state->dibit_buf + 200;
|
|
||||||
// memset (state->dibit_buf, 0, sizeof (int) * 200);
|
state->dibit_buf_p = state->dibit_buf + 200;
|
||||||
state->repeat = 0; //not sure yet
|
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||||
//state->audio_out_buf = malloc (sizeof (short) * 1000000);
|
state->repeat = 0; //repeat frame?
|
||||||
//memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
|
||||||
//state->audio_out_buf_p = state->audio_out_buf + 100;
|
//Audio Buffer -- Free Allocated Memory
|
||||||
//state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
|
free (state->audio_out_buf);
|
||||||
//memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
free (state->audio_out_float_buf);
|
||||||
//state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
free (state->audio_out_bufR);
|
||||||
//state->audio_out_idx = 0;
|
free (state->audio_out_float_bufR);
|
||||||
//state->audio_out_idx2 = 0;
|
|
||||||
//state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
//Audio Buffer -- Memset/Init/Allocate Memory per slot
|
||||||
//state->wav_out_bytes = 0;
|
|
||||||
|
//slot 1
|
||||||
|
state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
|
||||||
|
state->audio_out_buf = malloc (sizeof (short) * 1000000);
|
||||||
|
|
||||||
|
memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
||||||
|
state->audio_out_buf_p = state->audio_out_buf + 100;
|
||||||
|
|
||||||
|
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
||||||
|
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
||||||
|
|
||||||
|
state->audio_out_idx = 0;
|
||||||
|
state->audio_out_idx2 = 0;
|
||||||
|
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||||
|
|
||||||
|
//slot 2
|
||||||
|
state->audio_out_bufR = malloc (sizeof (short) * 1000000);
|
||||||
|
state->audio_out_float_bufR = malloc (sizeof (float) * 1000000);
|
||||||
|
|
||||||
|
memset (state->audio_out_bufR, 0, 100 * sizeof (short));
|
||||||
|
state->audio_out_buf_pR = state->audio_out_bufR + 100;
|
||||||
|
|
||||||
|
memset (state->audio_out_float_bufR, 0, 100 * sizeof (float));
|
||||||
|
state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100;
|
||||||
|
|
||||||
|
state->audio_out_idxR = 0;
|
||||||
|
state->audio_out_idx2R = 0;
|
||||||
|
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||||
|
|
||||||
|
//Sync
|
||||||
state->center = 0;
|
state->center = 0;
|
||||||
state->jitter = -1;
|
state->jitter = -1;
|
||||||
state->synctype = -1;
|
state->synctype = -1;
|
||||||
@@ -31,76 +64,73 @@ resetState (dsd_state * state)
|
|||||||
state->umid = 0;
|
state->umid = 0;
|
||||||
state->minref = -12000;
|
state->minref = -12000;
|
||||||
state->maxref = 12000;
|
state->maxref = 12000;
|
||||||
|
|
||||||
state->lastsample = 0;
|
state->lastsample = 0;
|
||||||
for (i = 0; i < 128; i++)
|
for (i = 0; i < 128; i++)
|
||||||
{
|
{
|
||||||
state->sbuf[i] = 0;
|
state->sbuf[i] = 0;
|
||||||
}
|
}
|
||||||
state->sidx = 0;
|
state->sidx = 0;
|
||||||
for (i = 0; i < 1024; i++)
|
for (i = 0; i < 1024; i++)
|
||||||
{
|
{
|
||||||
state->maxbuf[i] = 15000;
|
state->maxbuf[i] = 15000;
|
||||||
}
|
}
|
||||||
for (i = 0; i < 1024; i++)
|
for (i = 0; i < 1024; i++)
|
||||||
{
|
{
|
||||||
state->minbuf[i] = -15000;
|
state->minbuf[i] = -15000;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->midx = 0;
|
state->midx = 0;
|
||||||
state->err_str[0] = 0;
|
|
||||||
sprintf (state->fsubtype, " ");
|
|
||||||
sprintf (state->ftype, " ");
|
|
||||||
state->symbolcnt = 0;
|
state->symbolcnt = 0;
|
||||||
state->rf_mod = 0;
|
|
||||||
state->numflips = 0;
|
state->numflips = 0;
|
||||||
state->lastsynctype = -1;
|
state->lastsynctype = -1;
|
||||||
state->lastp25type = 0;
|
state->lastp25type = 0;
|
||||||
state->offset = 0;
|
state->offset = 0;
|
||||||
state->carrier = 0;
|
state->carrier = 0;
|
||||||
for (i = 0; i < 25; i++)
|
|
||||||
{
|
//Reset Voice Errors in C0 and C1 (or remaining Codewords in IMBE)
|
||||||
for (j = 0; j < 16; j++)
|
|
||||||
{
|
|
||||||
state->tg[i][j] = 48;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->tgcount = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->nac = 0;
|
|
||||||
state->errs = 0;
|
state->errs = 0;
|
||||||
state->errs2 = 0;
|
state->errs2 = 0;
|
||||||
state->mbe_file_type = -1;
|
state->errsR = 0;
|
||||||
|
state->errs2R = 0;
|
||||||
|
|
||||||
|
//Misc -- may not be needed
|
||||||
state->optind = 0;
|
state->optind = 0;
|
||||||
state->numtdulc = 0;
|
state->numtdulc = 0;
|
||||||
state->firstframe = 0;
|
state->firstframe = 0;
|
||||||
sprintf (state->slot0light, " slot0 ");
|
|
||||||
sprintf (state->slot1light, " slot1 ");
|
|
||||||
state->aout_gain = 25;
|
|
||||||
memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
|
||||||
state->aout_max_buf_p = state->aout_max_buf;
|
|
||||||
state->aout_max_buf_idx = 0;
|
|
||||||
state->samplesPerSymbol = 10;
|
|
||||||
state->symbolCenter = 4;
|
|
||||||
sprintf (state->algid, "________");
|
|
||||||
sprintf (state->keyid, "________________");
|
|
||||||
state->currentslot = 0;
|
|
||||||
state->cur_mp = malloc (sizeof (mbe_parms));
|
|
||||||
state->prev_mp = malloc (sizeof (mbe_parms));
|
|
||||||
state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
|
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
|
||||||
state->p25kid = 0;
|
|
||||||
|
|
||||||
state->debug_audio_errors = 0;
|
//unsure if these are still used or ever were used,
|
||||||
state->debug_header_errors = 0;
|
// memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
||||||
state->debug_header_critical_errors = 0;
|
// state->aout_max_buf_p = state->aout_max_buf;
|
||||||
|
// state->aout_max_buf_idx = 0;
|
||||||
|
|
||||||
state->nxdn_last_ran = 0;
|
// //MBE Specific
|
||||||
// state->payload_algid = 0;
|
// //free the memory before allocating it again -- may not use this
|
||||||
// state->payload_algidR = 0;
|
// free (state->cur_mp);
|
||||||
state->dmr_encL = 0;
|
// free (state->prev_mp);
|
||||||
state->dmr_encR = 0;
|
// free (state->prev_mp_enhanced);
|
||||||
|
|
||||||
//each time you run this, it increses memory use by 4MB, massive memory leak
|
// //memory allocation and init on mbe -- may not use this
|
||||||
//need to revisit this sometime and look into only resetting only the necesary items to let P25 switch between signals (C4FM or Wide) without needing a restart
|
// state->cur_mp = malloc (sizeof (mbe_parms));
|
||||||
initialize_p25_heuristics(&state->p25_heuristics); //see if we want to re-init this or not, currently seems to cause memory leak when running over and over, mitigated with a reset flag
|
// state->prev_mp = malloc (sizeof (mbe_parms));
|
||||||
|
// state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
|
||||||
|
// mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||||
|
|
||||||
|
//rest the heurestics, we want to do this on each tune, each RF frequency can deviate quite a bit in strength
|
||||||
|
initialize_p25_heuristics(&state->p25_heuristics);
|
||||||
|
initialize_p25_heuristics(&state->inv_p25_heuristics);
|
||||||
|
}
|
||||||
|
|
||||||
|
//simple function to reset the dibit buffer
|
||||||
|
void reset_dibit_buffer(dsd_state * state)
|
||||||
|
{
|
||||||
|
//Dibit Buffer -- Free Allocated Memory
|
||||||
|
// free (state->dibit_buf);
|
||||||
|
|
||||||
|
//Dibit Buffer -- Memset/Init/Allocate Memory
|
||||||
|
// state->dibit_buf = malloc (sizeof (int) * 1000000);
|
||||||
|
|
||||||
|
state->dibit_buf_p = state->dibit_buf + 200;
|
||||||
|
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,6 +250,7 @@ int UDPBind (char *hostname, int portno)
|
|||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//going to leave this function available, even if completely switched over to rtl_dev_tune now, may be useful in the future
|
||||||
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
||||||
{
|
{
|
||||||
int handle;
|
int handle;
|
||||||
@@ -273,4 +274,6 @@ void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
|||||||
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later
|
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later
|
||||||
address.sin_port = htons(udp_port);
|
address.sin_port = htons(udp_port);
|
||||||
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
||||||
|
|
||||||
|
close (handle); //close socket after sending.
|
||||||
}
|
}
|
||||||
276
src/dsd_symbol.c
276
src/dsd_symbol.c
@@ -17,6 +17,29 @@
|
|||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
|
long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as len
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
long int rms;
|
||||||
|
long p, t, s;
|
||||||
|
double dc, err;
|
||||||
|
|
||||||
|
p = t = 0L;
|
||||||
|
for (i=0; i<len; i+=step) {
|
||||||
|
s = (long)samples[i];
|
||||||
|
t += s;
|
||||||
|
p += s * s;
|
||||||
|
}
|
||||||
|
/* correct for dc offset in squares */
|
||||||
|
dc = (double)(t*step) / (double)len;
|
||||||
|
err = t * 2 * dc - dc * dc * len;
|
||||||
|
|
||||||
|
rms = (long int)sqrt((p-err) / len);
|
||||||
|
if (rms < 0) rms = 150;
|
||||||
|
return rms;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
||||||
{
|
{
|
||||||
@@ -82,35 +105,24 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the new sample from the input
|
// Read the new sample from the input
|
||||||
if(opts->audio_in_type == 0) // && state->menuopen == 0 still not quite working
|
if(opts->audio_in_type == 0) //pulse audio
|
||||||
{
|
{
|
||||||
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||||
//look into how processAudio handles playback, not sure if latency issues, or garbage sample values crash pulse when written
|
|
||||||
// if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
|
|
||||||
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample != 0)
|
|
||||||
{
|
|
||||||
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
|
|
||||||
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
|
|
||||||
}
|
|
||||||
//playback is wrong, depends on samples_per_symbol, maybe make a buffer and store first?
|
|
||||||
//making buffer might also fix raw audio monitoring as well
|
|
||||||
if (opts->wav_out_file_raw[0] != 0) //if set for recording sample raw files
|
|
||||||
{
|
|
||||||
//writeRawSample (opts, state, sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (opts->audio_in_type == 5) //OSS
|
||||||
|
{
|
||||||
|
read (opts->audio_in_fd, &sample, 2);
|
||||||
|
}
|
||||||
|
|
||||||
//stdin only, wav files moving to new number
|
//stdin only, wav files moving to new number
|
||||||
else if (opts->audio_in_type == 1)
|
else if (opts->audio_in_type == 1) //won't work in windows, needs posix pipe (mintty)
|
||||||
{
|
{
|
||||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||||
if(result == 0)
|
if(result == 0)
|
||||||
{
|
{
|
||||||
//cleanupAndExit (opts, state);
|
|
||||||
sf_close(opts->audio_in_file);
|
sf_close(opts->audio_in_file);
|
||||||
opts->audio_in_type = 0; //set input type
|
cleanupAndExit (opts, state);
|
||||||
openPulseInput(opts); //open pulse inpput
|
|
||||||
sample = 0; //send zero sample
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//wav files, same but using seperate value so we can still manipulate ncurses menu
|
//wav files, same but using seperate value so we can still manipulate ncurses menu
|
||||||
@@ -122,59 +134,200 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
{
|
{
|
||||||
|
|
||||||
sf_close(opts->audio_in_file);
|
sf_close(opts->audio_in_file);
|
||||||
opts->audio_in_type = 0; //set input type
|
fprintf (stderr, "\nEnd of %s\n", opts->audio_in_dev);
|
||||||
openPulseInput(opts); //open pulse inpput
|
//open pulse input if we are pulse output AND using ncurses terminal
|
||||||
sample = 0; //send zero sample
|
if (opts->audio_out_type == 0 && opts->use_ncurses_terminal == 1)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
openPulseInput(opts); //open pulse input
|
||||||
|
}
|
||||||
|
//else cleanup and exit
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (opts->use_ncurses_terminal == 1) ncursesClose(opts);
|
||||||
|
cleanupAndExit(opts, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
#ifdef USE_RTLSDR
|
#ifdef USE_RTLSDR
|
||||||
// TODO: need to read demodulated stream here
|
// Read demodulated stream here
|
||||||
// get_rtlsdr_sample(&sample);
|
|
||||||
get_rtlsdr_sample(&sample, opts, state);
|
get_rtlsdr_sample(&sample, opts, state);
|
||||||
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
|
opts->rtl_rms = rtl_return_rms();
|
||||||
{
|
|
||||||
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
|
|
||||||
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
//tcp socket input from SDR++
|
|
||||||
|
//tcp socket input from SDR++ -- now with 1 retry if connection is broken
|
||||||
else if (opts->audio_in_type == 8)
|
else if (opts->audio_in_type == 8)
|
||||||
{
|
{
|
||||||
|
#ifdef AERO_BUILD
|
||||||
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
if(result == 0) {
|
if(result == 0) {
|
||||||
sf_close(opts->tcp_file_in);
|
fprintf (stderr, "\nConnection to TCP Server Interrupted. Trying again in 3 seconds.\n");
|
||||||
opts->audio_in_type = 0; //set input type
|
sample = 0;
|
||||||
openPulseInput(opts); //open pulse inpput
|
sf_close(opts->tcp_file_in); //close current connection on this end
|
||||||
sample = 0; //zero sample on bad result, keep the ball rolling
|
sleep (3); //halt all processing and wait 3 seconds
|
||||||
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
|
||||||
|
//attempt to reconnect to socket
|
||||||
|
opts->tcp_sockfd = 0;
|
||||||
|
opts->tcp_sockfd = Connect(opts->tcp_hostname, opts->tcp_portno);
|
||||||
|
if (opts->tcp_sockfd != 0)
|
||||||
|
{
|
||||||
|
//reset audio input stream
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||||
|
opts->audio_in_file_info->channels=1;
|
||||||
|
opts->audio_in_file_info->seekable=0;
|
||||||
|
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||||
|
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
|
||||||
|
if(opts->tcp_file_in == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't Reconnect to TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "TCP Socket Reconnected Successfully.\n");
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "TCP Socket Connection Error.\n");
|
||||||
|
|
||||||
|
//now retry reading sample
|
||||||
|
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
if (result == 0) {
|
||||||
|
sf_close(opts->tcp_file_in);
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
opts->tcp_sockfd = 0; //added this line so we will know if it connected when using ncurses terminal keyboard shortcut
|
||||||
|
//openPulseInput(opts); //open pulse inpput
|
||||||
|
sample = 0; //zero sample on bad result, keep the ball rolling
|
||||||
|
//open pulse input if we are pulse output AND using ncurses terminal
|
||||||
|
if (opts->audio_out_type == 0 && opts->use_ncurses_terminal == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||||
|
fprintf (stderr, "Opening Pulse Audio Input.\n");
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
openPulseInput(opts); //open pulse input
|
||||||
|
}
|
||||||
|
//else cleanup and exit
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||||
|
fprintf (stderr, "Closing DSD-FME.\n");
|
||||||
|
if (opts->use_ncurses_terminal == 1) ncursesClose(opts);
|
||||||
|
cleanupAndExit(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
if(result == 0) {
|
||||||
|
fprintf (stderr, "\nConnection to TCP Server Interrupted. Trying again in 3 seconds.\n");
|
||||||
|
sample = 0;
|
||||||
|
sf_close(opts->tcp_file_in); //close current connection on this end
|
||||||
|
sleep (3); //halt all processing and wait 3 seconds
|
||||||
|
|
||||||
|
//attempt to reconnect to socket
|
||||||
|
opts->tcp_sockfd = 0;
|
||||||
|
opts->tcp_sockfd = Connect(opts->tcp_hostname, opts->tcp_portno);
|
||||||
|
if (opts->tcp_sockfd != 0)
|
||||||
|
{
|
||||||
|
//reset audio input stream
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||||
|
opts->audio_in_file_info->channels=1;
|
||||||
|
opts->audio_in_file_info->seekable=0;
|
||||||
|
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||||
|
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
|
||||||
|
if(opts->tcp_file_in == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't Reconnect to TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "TCP Socket Reconnected Successfully.\n");
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "TCP Socket Connection Error.\n");
|
||||||
|
|
||||||
|
//now retry reading sample
|
||||||
|
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
if (result == 0) {
|
||||||
|
sf_close(opts->tcp_file_in);
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
opts->tcp_sockfd = 0; //added this line so we will know if it connected when using ncurses terminal keyboard shortcut
|
||||||
|
openPulseInput(opts); //open pulse inpput
|
||||||
|
sample = 0; //zero sample on bad result, keep the ball rolling
|
||||||
|
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
//UDP Socket input...not working correct. Reads samples, but no sync
|
|
||||||
else if (opts->audio_in_type == 6)
|
//Source Audio Monitoring
|
||||||
|
if (have_sync == 0 && opts->monitor_input_audio == 1)
|
||||||
{
|
{
|
||||||
//I think this doesn't get the entire dgram when we run sf_read_short on the udp dgram
|
//sanity check to prevent an overflow
|
||||||
result = sf_read_short(opts->udp_file_in, &sample, 1);
|
if (state->analog_sample_counter > 959)
|
||||||
// if (sample != 0)
|
state->analog_sample_counter = 959;
|
||||||
// fprintf (stderr, "Result = %d Sample = %d \n", result, sample);
|
|
||||||
|
state->analog_out[state->analog_sample_counter++] = sample;
|
||||||
|
|
||||||
|
if (state->analog_sample_counter == 960)
|
||||||
|
{
|
||||||
|
//get an rms value if not using the rtl built in version
|
||||||
|
if (opts->audio_in_type != 3)
|
||||||
|
opts->rtl_rms = raw_rms(state->analog_out, 960, 1);
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// opts->rtl_rms = 101;
|
||||||
|
|
||||||
|
//make it a little quieter
|
||||||
|
for (int x = 0; x < 960; x++)
|
||||||
|
state->analog_out[x] /= 4;
|
||||||
|
|
||||||
|
//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 )
|
||||||
|
{
|
||||||
|
if (opts->audio_out_type == 0)
|
||||||
|
pa_simple_write(opts->pulse_raw_dev_out, state->analog_out, 960*2, NULL);
|
||||||
|
|
||||||
|
//NOTE: Worked okay earlier in Cygwin, so should be fine
|
||||||
|
if (opts->audio_out_type == 5)
|
||||||
|
write (opts->audio_out_fd, state->analog_out, 960*2);
|
||||||
|
|
||||||
|
//test hold on scan/trunking
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (state->analog_out, 0, sizeof(state->analog_out));
|
||||||
|
state->analog_sample_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (have_sync == 1 && opts->monitor_input_audio == 1)
|
||||||
|
{
|
||||||
|
//zero out and reset counter
|
||||||
|
memset (state->analog_out, 0, sizeof(state->analog_out));
|
||||||
|
state->analog_sample_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->use_cosine_filter)
|
if (opts->use_cosine_filter)
|
||||||
{
|
{
|
||||||
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34)
|
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33
|
||||||
|
|| state->lastsynctype == 34 || state->lastsynctype == 30 || state->lastsynctype == 31)
|
||||||
{
|
{
|
||||||
sample = dmr_filter(sample);
|
sample = dmr_filter(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (state->lastsynctype == 8 || state->lastsynctype == 9 ||
|
else if (state->lastsynctype == 8 || state->lastsynctype == 9 ||state->lastsynctype == 16 || state->lastsynctype == 17)
|
||||||
state->lastsynctype == 16 || state->lastsynctype == 17 ||
|
{
|
||||||
|
sample = m17_filter(sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (
|
||||||
state->lastsynctype == 20 || state->lastsynctype == 21 ||
|
state->lastsynctype == 20 || state->lastsynctype == 21 ||
|
||||||
state->lastsynctype == 22 || state->lastsynctype == 23 ||
|
state->lastsynctype == 22 || state->lastsynctype == 23 ||
|
||||||
state->lastsynctype == 24 || state->lastsynctype == 25 ||
|
state->lastsynctype == 24 || state->lastsynctype == 25 ||
|
||||||
@@ -383,6 +536,12 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//test throttle on wav input files
|
||||||
|
if (opts->audio_in_type == 2)
|
||||||
|
{
|
||||||
|
if (state->use_throttle == 1) usleep(.003); //very environment specific, tuning to cygwin
|
||||||
|
}
|
||||||
|
|
||||||
//read op25/fme symbol bin files
|
//read op25/fme symbol bin files
|
||||||
if (opts->audio_in_type == 4)
|
if (opts->audio_in_type == 4)
|
||||||
{
|
{
|
||||||
@@ -399,14 +558,27 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
useconds_t stime = state->symbol_throttle;
|
useconds_t stime = state->symbol_throttle;
|
||||||
if (state->use_throttle == 1)
|
if (state->use_throttle == 1)
|
||||||
{
|
{
|
||||||
usleep(stime);
|
// usleep(stime);
|
||||||
|
usleep(.003); //very environment specific, tuning to cygwin
|
||||||
}
|
}
|
||||||
//fprintf(stderr, "%d", state->symbolc);
|
//fprintf(stderr, "%d", state->symbolc);
|
||||||
if( feof(opts->symbolfile) )
|
if( feof(opts->symbolfile) )
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 0; //switch to pulse after playback, ncurses terminal can initiate replay if wanted
|
// opts->audio_in_type = 0; //switch to pulse after playback, ncurses terminal can initiate replay if wanted
|
||||||
fclose(opts->symbolfile);
|
fclose(opts->symbolfile);
|
||||||
openPulseInput(opts);
|
fprintf (stderr, "\nEnd of %s\n", opts->audio_in_dev);
|
||||||
|
//open pulse input if we are pulse output AND using ncurses terminal
|
||||||
|
if (opts->audio_out_type == 0 && opts->use_ncurses_terminal == 1)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
openPulseInput(opts); //open pulse input
|
||||||
|
}
|
||||||
|
//else cleanup and exit
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (opts->use_ncurses_terminal == 1) ncursesClose(opts);
|
||||||
|
cleanupAndExit(opts, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//assign symbol/dibit values based on modulation type
|
//assign symbol/dibit values based on modulation type
|
||||||
|
|||||||
@@ -480,7 +480,8 @@ decode_bch()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long int messagepp = 0x0;
|
//bugfix on 32-bit cygwin, probably overflowed here
|
||||||
|
unsigned long long int messagepp = 0x0;
|
||||||
|
|
||||||
//very simplified version, just to encode, get frame and compare
|
//very simplified version, just to encode, get frame and compare
|
||||||
unsigned long long int edacs_bch (unsigned long long int message)
|
unsigned long long int edacs_bch (unsigned long long int message)
|
||||||
|
|||||||
328
src/edacs-fme.c
328
src/edacs-fme.c
@@ -14,12 +14,16 @@
|
|||||||
* 1994-7
|
* 1994-7
|
||||||
*
|
*
|
||||||
* LWVMOBILE
|
* LWVMOBILE
|
||||||
* 2022-10 Version EDACS-FM Florida Man Edition
|
* 2023-08 Version EDACS-FM Florida Man Edition
|
||||||
*-----------------------------------------------------------------------------*/
|
*-----------------------------------------------------------------------------*/
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
char * getDateE(void) {
|
char * getDateE(void) {
|
||||||
char datename[99]; //might honestly just need a much smaller size, and not a larger one
|
#ifdef AERO_BUILD
|
||||||
|
char datename[80];
|
||||||
|
#else
|
||||||
|
char datename[99];
|
||||||
|
#endif
|
||||||
char * curr2;
|
char * curr2;
|
||||||
struct tm * to;
|
struct tm * to;
|
||||||
time_t t;
|
time_t t;
|
||||||
@@ -45,14 +49,200 @@ char * getTimeE(void) //get pretty hh:mm:ss timestamp
|
|||||||
return curr;
|
return curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openWavOutFile48k (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
SF_INFO info;
|
||||||
|
info.samplerate = 48000; //48k for analog output (has to match input)
|
||||||
|
info.channels = 1;
|
||||||
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
|
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info);
|
||||||
|
|
||||||
|
if (opts->wav_out_f == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//generic rms type function
|
||||||
|
long int gen_rms(short *samples, int len, int step)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
long int rms;
|
||||||
|
long p, t, s;
|
||||||
|
double dc, err;
|
||||||
|
|
||||||
|
p = t = 0L;
|
||||||
|
for (i=0; i<len; i+=step) {
|
||||||
|
s = (long)samples[i];
|
||||||
|
t += s;
|
||||||
|
p += s * s;
|
||||||
|
}
|
||||||
|
/* correct for dc offset in squares */
|
||||||
|
dc = (double)(t*step) / (double)len;
|
||||||
|
err = t * 2 * dc - dc * dc * len;
|
||||||
|
|
||||||
|
rms = (long int)sqrt((p-err) / len);
|
||||||
|
//make sure it doesnt' randomly feed us a large negative value (overflow?)
|
||||||
|
if (rms < 0) rms = 150; //could also consider returning 0 and making it return the last known good value instead
|
||||||
|
return rms;
|
||||||
|
}
|
||||||
|
|
||||||
|
//listening to and playing back analog audio
|
||||||
|
void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
short analog1[960];
|
||||||
|
short analog2[960];
|
||||||
|
short analog3[960];
|
||||||
|
short sample = 0;
|
||||||
|
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
|
||||||
|
memset (analog1, 0, sizeof(analog1));
|
||||||
|
memset (analog2, 0, sizeof(analog2));
|
||||||
|
memset (analog3, 0, sizeof(analog3));
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
while (rms > sql) //!exitflag && -- no exitflag on this version
|
||||||
|
{
|
||||||
|
//this will only work on 48k/1 short output
|
||||||
|
if (opts->audio_in_type == 0)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||||
|
analog1[i] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||||
|
analog2[i] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||||
|
analog3[i] = sample;
|
||||||
|
}
|
||||||
|
//this rms will only work properly (for now) with squelch enabled in SDR++ or other
|
||||||
|
rms = gen_rms(analog3, 960, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TCP Input -- TODO: Error Handling if connection drops
|
||||||
|
if (opts->audio_in_type == 8)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
analog1[i] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
analog2[i] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
analog3[i] = sample;
|
||||||
|
}
|
||||||
|
//this rms will only work properly (for now) with squelch enabled in SDR++
|
||||||
|
rms = gen_rms(analog3, 960, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//RTL Input
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
get_rtlsdr_sample(&sample, opts, state);
|
||||||
|
analog1[i] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
get_rtlsdr_sample(&sample, opts, state);
|
||||||
|
analog2[i] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 960; i++)
|
||||||
|
{
|
||||||
|
get_rtlsdr_sample(&sample, opts, state);
|
||||||
|
analog3[i] = sample;
|
||||||
|
}
|
||||||
|
//the rtl rms value works properly without needing a 'hard' squelch value
|
||||||
|
rms = rtl_return_rms();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (opts->audio_out_type == 0)
|
||||||
|
{
|
||||||
|
pa_simple_write(opts->pulse_raw_dev_out, analog1, 960*2, NULL);
|
||||||
|
pa_simple_write(opts->pulse_raw_dev_out, analog2, 960*2, NULL);
|
||||||
|
pa_simple_write(opts->pulse_raw_dev_out, analog3, 960*2, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->audio_out_type == 5)
|
||||||
|
{
|
||||||
|
write (opts->audio_out_fd, analog1, 960*2);
|
||||||
|
write (opts->audio_out_fd, analog2, 960*2);
|
||||||
|
write (opts->audio_out_fd, analog3, 960*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
opts->rtl_rms = rms;
|
||||||
|
|
||||||
|
|
||||||
|
printFrameSync (opts, state, " EDACS", 0, "A");
|
||||||
|
|
||||||
|
if (rms < sql) fprintf (stderr, "%s", KRED);
|
||||||
|
else fprintf (stderr, "%s", KGRN);
|
||||||
|
|
||||||
|
fprintf (stderr, " Analog RMS: %04ld SQL: %ld", rms, sql);
|
||||||
|
if (afs != 0)
|
||||||
|
fprintf (stderr, " AFS [0x%03X] [%02d-%03d] LCN [%02d]", afs, afs >> 7, afs & 0x7F, lcn);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
if (rms > sql) fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
//Update Ncurses Terminal
|
||||||
|
if (opts->use_ncurses_terminal == 1)
|
||||||
|
ncursesPrinter(opts, state);
|
||||||
|
|
||||||
|
//WIP: Write Analog Samples to wav file, easy enough,
|
||||||
|
//but needs wav to be opened as 48k, or downsampled
|
||||||
|
//working on 48k version of openWavOutFile
|
||||||
|
if (opts->wav_out_f != NULL)
|
||||||
|
{
|
||||||
|
sf_write_short(opts->wav_out_f, analog1, 960);
|
||||||
|
sf_write_short(opts->wav_out_f, analog2, 960);
|
||||||
|
sf_write_short(opts->wav_out_f, analog3, 960);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void edacs(dsd_opts * opts, dsd_state * state)
|
void edacs(dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
unsigned long long fr_1 = 0xFFFFFFFFFF; //40-bit frames
|
//changed to ulli here for 32-bit cygwin (not sure if was an issue, but playing it safe)
|
||||||
unsigned long long fr_2 = 0; //each is a 40 bit frame that repeats 3 times
|
unsigned long long int fr_1 = 0xFFFFFFFFFF; //40-bit frames
|
||||||
unsigned long long fr_3 = 0; //two messages per frame
|
unsigned long long int fr_2 = 0; //each is a 40 bit frame that repeats 3 times
|
||||||
unsigned long long fr_4 = 0xFFFFFFFFFF;
|
unsigned long long int fr_3 = 0; //two messages per frame
|
||||||
unsigned long long fr_5 = 0;
|
unsigned long long int fr_4 = 0xFFFFFFFFFF;
|
||||||
unsigned long long fr_6 = 0;
|
unsigned long long int fr_5 = 0;
|
||||||
|
unsigned long long int fr_6 = 0;
|
||||||
|
|
||||||
//BCH stuff
|
//BCH stuff
|
||||||
unsigned long long int fr_1m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler
|
unsigned long long int fr_1m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler
|
||||||
@@ -63,7 +253,6 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
|||||||
unsigned char command = 0xFF;
|
unsigned char command = 0xFF;
|
||||||
unsigned char mt1 = 0x1F;
|
unsigned char mt1 = 0x1F;
|
||||||
unsigned char mt2 = 0xF;
|
unsigned char mt2 = 0xF;
|
||||||
unsigned char mta = 0;
|
|
||||||
unsigned char lcn = 0;
|
unsigned char lcn = 0;
|
||||||
|
|
||||||
//commands; may not use these anymore
|
//commands; may not use these anymore
|
||||||
@@ -74,7 +263,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
state->edacs_vc_lcn = -1; //init on negative for ncurses and tuning
|
state->edacs_vc_lcn = -1; //init on negative for ncurses and tuning
|
||||||
|
|
||||||
int i, j;
|
int i;
|
||||||
int edacs_bit[241] = {0}; //zero out bit array and collect bits into it.
|
int edacs_bit[241] = {0}; //zero out bit array and collect bits into it.
|
||||||
|
|
||||||
for (i = 0; i < 240; i++) //288 bits every transmission minus 48 bit (24 dibit) sync pattern
|
for (i = 0; i < 240; i++) //288 bits every transmission minus 48 bit (24 dibit) sync pattern
|
||||||
@@ -123,32 +312,32 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
|
|
||||||
//ESK on/off detection, I honestly don't remember the logic for this anymore, but it works fine
|
//ESK on/off detection, I honestly don't remember the logic for this anymore, but it works fine
|
||||||
if ( (((fr_1t & 0xF000000000) >> 36) != 0xB) && (((fr_1t & 0xF000000000) >> 36) != 0x1) && (((fr_1t & 0xFF00000000) >> 32) != 0xF3) )
|
if ( (((fr_1t & 0xF000000000) >> 36) != 0xB) && (((fr_1t & 0xF000000000) >> 36) != 0x1) && (((fr_1t & 0xFF00000000) >> 32) != 0xF3) )
|
||||||
{
|
{
|
||||||
//experimenting with values here, not too high, and not too low
|
//experimenting with values here, not too high, and not too low
|
||||||
if ( (((fr_1t & 0xF000000000) >> 36) <= 0x8 ))
|
if ( (((fr_1t & 0xF000000000) >> 36) <= 0x8 ))
|
||||||
{
|
{
|
||||||
state->esk_mask = 0xA0;
|
state->esk_mask = 0xA0;
|
||||||
}
|
}
|
||||||
//ideal value would be 5, but some other values exist that don't allow it
|
//ideal value would be 5, but some other values exist that don't allow it
|
||||||
if ( (((fr_1t & 0xF000000000) >> 36) > 0x8 ) )
|
if ( (((fr_1t & 0xF000000000) >> 36) > 0x8 ) )
|
||||||
{
|
{
|
||||||
state->esk_mask = 0x0;
|
state->esk_mask = 0x0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Standard/Networked Auto Detection
|
//Standard/Networked Auto Detection
|
||||||
//if (command == netcmd) //netcmd is F3 Standard/Networked (I think)
|
//if (command == netcmd) //netcmd is F3 Standard/Networked (I think)
|
||||||
if ( (fr_1t >> 32 == netcmd) || (fr_1t >> 32 == (netcmd ^ 0xA0)) )
|
if ( (fr_1t >> 32 == netcmd) || (fr_1t >> 32 == (netcmd ^ 0xA0)) )
|
||||||
{
|
{
|
||||||
state->ea_mode = 0; //disable extended addressing mode
|
state->ea_mode = 0; //disable extended addressing mode
|
||||||
}
|
}
|
||||||
|
|
||||||
//EA Auto detection //peercmd is 0xF88 peer site relay 0xFF80000000 >> 28
|
//EA Auto detection //peercmd is 0xF88 peer site relay 0xFF80000000 >> 28
|
||||||
if (fr_1t >> 28 == peercmd || fr_1t >> 28 == (peercmd ^ 0xA00) )
|
if (fr_1t >> 28 == peercmd || fr_1t >> 28 == (peercmd ^ 0xA00) )
|
||||||
{
|
{
|
||||||
state->ea_mode = 1; //enable extended addressing mode
|
state->ea_mode = 1; //enable extended addressing mode
|
||||||
}
|
}
|
||||||
|
|
||||||
//Start Extended Addressing Mode
|
//Start Extended Addressing Mode
|
||||||
if (state->ea_mode == 1)
|
if (state->ea_mode == 1)
|
||||||
@@ -283,6 +472,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
sprintf (opts->wav_out_file, "./WAV/%s %s pV Site %lld TG %d SRC %d.wav", getDateE(), getTimeE(), state->edacs_site_id, group, source);
|
sprintf (opts->wav_out_file, "./WAV/%s %s pV Site %lld TG %d SRC %d.wav", getDateE(), getTimeE(), state->edacs_site_id, group, source);
|
||||||
openWavOutFile (opts, state);
|
openWavOutFile (opts, state);
|
||||||
|
// openWavOutFile48k (opts, state); //debug for testing analog wav only
|
||||||
}
|
}
|
||||||
|
|
||||||
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
|
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
|
||||||
@@ -291,15 +481,20 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
|||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because the lcn index starts at zero
|
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because the lcn index starts at zero
|
||||||
state->edacs_tuned_lcn = lcn;
|
state->edacs_tuned_lcn = lcn;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
//debug testing (since I don't have EDACS standard w/ Analog nearby)
|
||||||
|
// edacs_analog(opts, state, group, lcn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->audio_in_type == 3) //rtl dongle
|
if (opts->audio_in_type == 3) //rtl dongle
|
||||||
{
|
{
|
||||||
rtl_udp_tune(opts, state, state->trunk_lcn_freq[lcn-1]);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]);
|
||||||
state->edacs_tuned_lcn = lcn;
|
state->edacs_tuned_lcn = lcn;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
//debug testing (since I don't have EDACS standard w/ Analog nearby)
|
||||||
|
// edacs_analog(opts, state, group, lcn);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -397,44 +592,57 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NOTE: Restructured below so that analog and digital are handled the same, just that when
|
||||||
|
//its analog, it will now start edacs_analog which will while loop analog samples until
|
||||||
|
//signal level drops (RMS, or a dotting sequence is detected)
|
||||||
|
|
||||||
|
//analog working now with edacs_analog
|
||||||
if (command == 0xEE)
|
if (command == 0xEE)
|
||||||
{
|
|
||||||
//no handling for raw audio yet...that works properly atleast
|
|
||||||
fprintf (stderr, " Analog");
|
fprintf (stderr, " Analog");
|
||||||
}
|
|
||||||
else //Digital Call (ProVoice, hopefully not Aegis in 2022)
|
//Digital Call (ProVoice, hopefully, and not Aegis in 2022)
|
||||||
{
|
else
|
||||||
fprintf (stderr, " Digital");
|
fprintf (stderr, " Digital");
|
||||||
//this is working now with the new import setup
|
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
//skip analog calls on RTL Input on ARM devices due to High CPU usage from RTL RMS function
|
||||||
|
#ifdef __arm__
|
||||||
|
if (command == 0xEE && opts->audio_in_type == 3)
|
||||||
|
goto ENDPV;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//this is working now with the new import setup
|
||||||
|
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
||||||
|
{
|
||||||
|
if (lcn < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise)
|
||||||
{
|
{
|
||||||
if (lcn < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise)
|
//openwav file and do per call right here
|
||||||
|
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
|
||||||
{
|
{
|
||||||
//openwav file and do per call right here
|
sprintf (opts->wav_out_file, "./WAV/%s %s pV Site %lld AFS %02d-%03d - %X.wav", getDateE(), getTimeE(), state->edacs_site_id, a, fs, afs);
|
||||||
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
|
if (command == 0xEF) openWavOutFile (opts, state); //digital
|
||||||
{
|
if (command == 0xEE) openWavOutFile48k (opts, state); //analog at 48k
|
||||||
sprintf (opts->wav_out_file, "./WAV/%s %s pV Site %lld AFS %X.wav", getDateE(), getTimeE(), state->edacs_site_id, afs);
|
}
|
||||||
openWavOutFile (opts, state);
|
|
||||||
}
|
if (opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
{
|
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
state->edacs_tuned_lcn = lcn;
|
||||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero
|
opts->p25_is_tuned = 1;
|
||||||
state->edacs_tuned_lcn = lcn;
|
if (command == 0xEE) edacs_analog(opts, state, afs, lcn);
|
||||||
opts->p25_is_tuned = 1;
|
}
|
||||||
}
|
|
||||||
|
if (opts->audio_in_type == 3) //rtl dongle
|
||||||
if (opts->audio_in_type == 3) //rtl dongle
|
{
|
||||||
{
|
#ifdef USE_RTLSDR
|
||||||
rtl_udp_tune(opts, state, state->trunk_lcn_freq[lcn-1]);
|
rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]);
|
||||||
state->edacs_tuned_lcn = lcn;
|
state->edacs_tuned_lcn = lcn;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
}
|
if (command == 0xEE) edacs_analog(opts, state, afs, lcn);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -455,7 +663,9 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
} //end Standard or Networked
|
} //end Standard or Networked
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ENDPV:
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
|||||||
296
src/ez.cpp
296
src/ez.cpp
@@ -178,146 +178,166 @@ int ez_rs28_sacch (int payload[180], int parity[132])
|
|||||||
return (ec);
|
return (ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
//I-ISCH Lookup section, borrowed from OP25
|
std::map <std::uint64_t, int> isch_map;
|
||||||
std::map <std::string, int> isch_map;
|
|
||||||
void map_isch()
|
void map_isch()
|
||||||
{
|
{
|
||||||
isch_map["184229d461"] = 0;
|
isch_map[0x184229d461ULL] = 0;
|
||||||
isch_map["18761451f6"] = 1;
|
isch_map[0x18761451f6ULL] = 1;
|
||||||
isch_map["181ae27e2f"] = 2;
|
isch_map[0x181ae27e2fULL] = 2;
|
||||||
isch_map["182edffbb8"] = 3;
|
isch_map[0x182edffbb8ULL] = 3;
|
||||||
isch_map["18df8a7510"] = 4;
|
isch_map[0x18df8a7510ULL] = 4;
|
||||||
isch_map["18ebb7f087"] = 5;
|
isch_map[0x18ebb7f087ULL] = 5;
|
||||||
isch_map["188741df5e"] = 6;
|
isch_map[0x188741df5eULL] = 6;
|
||||||
isch_map["18b37c5ac9"] = 7;
|
isch_map[0x18b37c5ac9ULL] = 7;
|
||||||
isch_map["1146a44f13"] = 8;
|
isch_map[0x1146a44f13ULL] = 8;
|
||||||
isch_map["117299ca84"] = 9;
|
isch_map[0x117299ca84ULL] = 9;
|
||||||
isch_map["111e6fe55d"] = 10;
|
isch_map[0x111e6fe55dULL] = 10;
|
||||||
isch_map["112a5260ca"] = 11;
|
isch_map[0x112a5260caULL] = 11;
|
||||||
isch_map["11db07ee62"] = 12;
|
isch_map[0x11db07ee62ULL] = 12;
|
||||||
isch_map["11ef3a6bf5"] = 13;
|
isch_map[0x11ef3a6bf5ULL] = 13;
|
||||||
isch_map["1183cc442c"] = 14;
|
isch_map[0x1183cc442cULL] = 14;
|
||||||
isch_map["11b7f1c1bb"] = 15;
|
isch_map[0x11b7f1c1bbULL] = 15;
|
||||||
isch_map["1a4a2e239e"] = 16;
|
isch_map[0x1a4a2e239eULL] = 16;
|
||||||
isch_map["1a7e13a609"] = 17;
|
isch_map[0x1a7e13a609ULL] = 17;
|
||||||
isch_map["1a12e589d0"] = 18;
|
isch_map[0x1a12e589d0ULL] = 18;
|
||||||
isch_map["1a26d80c47"] = 19;
|
isch_map[0x1a26d80c47ULL] = 19;
|
||||||
isch_map["1ad78d82ef"] = 20;
|
isch_map[0x1ad78d82efULL] = 20;
|
||||||
isch_map["1ae3b00778"] = 21;
|
isch_map[0x1ae3b00778ULL] = 21;
|
||||||
isch_map["1a8f4628a1"] = 22;
|
isch_map[0x1a8f4628a1ULL] = 22;
|
||||||
isch_map["1abb7bad36"] = 23;
|
isch_map[0x1abb7bad36ULL] = 23;
|
||||||
isch_map["134ea3b8ec"] = 24;
|
isch_map[0x134ea3b8ecULL] = 24;
|
||||||
isch_map["137a9e3d7b"] = 25;
|
isch_map[0x137a9e3d7bULL] = 25;
|
||||||
isch_map["13166812a2"] = 26;
|
isch_map[0x13166812a2ULL] = 26;
|
||||||
isch_map["1322559735"] = 27;
|
isch_map[0x1322559735ULL] = 27;
|
||||||
isch_map["13d300199d"] = 28;
|
isch_map[0x13d300199dULL] = 28;
|
||||||
isch_map["13e73d9c0a"] = 29;
|
isch_map[0x13e73d9c0aULL] = 29;
|
||||||
isch_map["138bcbb3d3"] = 30;
|
isch_map[0x138bcbb3d3ULL] = 30;
|
||||||
isch_map["13bff63644"] = 31;
|
isch_map[0x13bff63644ULL] = 31;
|
||||||
isch_map["1442f705ef"] = 32;
|
isch_map[0x1442f705efULL] = 32;
|
||||||
isch_map["1476ca8078"] = 33;
|
isch_map[0x1476ca8078ULL] = 33;
|
||||||
isch_map["141a3cafa1"] = 34;
|
isch_map[0x141a3cafa1ULL] = 34;
|
||||||
isch_map["142e012a36"] = 35;
|
isch_map[0x142e012a36ULL] = 35;
|
||||||
isch_map["14df54a49e"] = 36;
|
isch_map[0x14df54a49eULL] = 36;
|
||||||
isch_map["14eb692109"] = 37;
|
isch_map[0x14eb692109ULL] = 37;
|
||||||
isch_map["14879f0ed0"] = 38;
|
isch_map[0x14879f0ed0ULL] = 38;
|
||||||
isch_map["14b3a28b47"] = 39;
|
isch_map[0x14b3a28b47ULL] = 39;
|
||||||
isch_map["1d467a9e9d"] = 40;
|
isch_map[0x1d467a9e9dULL] = 40;
|
||||||
isch_map["1d72471b0a"] = 41;
|
isch_map[0x1d72471b0aULL] = 41;
|
||||||
isch_map["1d1eb134d3"] = 42;
|
isch_map[0x1d1eb134d3ULL] = 42;
|
||||||
isch_map["1d2a8cb144"] = 43;
|
isch_map[0x1d2a8cb144ULL] = 43;
|
||||||
isch_map["1ddbd93fec"] = 44;
|
isch_map[0x1ddbd93fecULL] = 44;
|
||||||
isch_map["1defe4ba7b"] = 45;
|
isch_map[0x1defe4ba7bULL] = 45;
|
||||||
isch_map["1d831295a2"] = 46;
|
isch_map[0x1d831295a2ULL] = 46;
|
||||||
isch_map["1db72f1035"] = 47;
|
isch_map[0x1db72f1035ULL] = 47;
|
||||||
isch_map["164af0f210"] = 48;
|
isch_map[0x164af0f210ULL] = 48;
|
||||||
isch_map["167ecd7787"] = 49;
|
isch_map[0x167ecd7787ULL] = 49;
|
||||||
isch_map["16123b585e"] = 50;
|
isch_map[0x16123b585eULL] = 50;
|
||||||
isch_map["162606ddc9"] = 51;
|
isch_map[0x162606ddc9ULL] = 51;
|
||||||
isch_map["16d7535361"] = 52;
|
isch_map[0x16d7535361ULL] = 52;
|
||||||
isch_map["16e36ed6f6"] = 53;
|
isch_map[0x16e36ed6f6ULL] = 53;
|
||||||
isch_map["168f98f92f"] = 54;
|
isch_map[0x168f98f92fULL] = 54;
|
||||||
isch_map["16bba57cb8"] = 55;
|
isch_map[0x16bba57cb8ULL] = 55;
|
||||||
isch_map["1f4e7d6962"] = 56;
|
isch_map[0x1f4e7d6962ULL] = 56;
|
||||||
isch_map["1f7a40ecf5"] = 57;
|
isch_map[0x1f7a40ecf5ULL] = 57;
|
||||||
isch_map["1f16b6c32c"] = 58;
|
isch_map[0x1f16b6c32cULL] = 58;
|
||||||
isch_map["1f228b46bb"] = 59;
|
isch_map[0x1f228b46bbULL] = 59;
|
||||||
isch_map["1fd3dec813"] = 60;
|
isch_map[0x1fd3dec813ULL] = 60;
|
||||||
isch_map["1fe7e34d84"] = 61;
|
isch_map[0x1fe7e34d84ULL] = 61;
|
||||||
isch_map["1f8b15625d"] = 62;
|
isch_map[0x1f8b15625dULL] = 62;
|
||||||
isch_map["1fbf28e7ca"] = 63;
|
isch_map[0x1fbf28e7caULL] = 63;
|
||||||
isch_map["84d62c339"] = 64;
|
isch_map[0x084d62c339ULL] = 64;
|
||||||
isch_map["8795f46ae"] = 65;
|
isch_map[0x08795f46aeULL] = 65;
|
||||||
isch_map["815a96977"] = 66;
|
isch_map[0x0815a96977ULL] = 66;
|
||||||
isch_map["82194ece0"] = 67;
|
isch_map[0x082194ece0ULL] = 67;
|
||||||
isch_map["8d0c16248"] = 68;
|
isch_map[0x08d0c16248ULL] = 68;
|
||||||
isch_map["8e4fce7df"] = 69;
|
isch_map[0x08e4fce7dfULL] = 69;
|
||||||
isch_map["8880ac806"] = 70;
|
isch_map[0x08880ac806ULL] = 70;
|
||||||
isch_map["8bc374d91"] = 71;
|
isch_map[0x08bc374d91ULL] = 71;
|
||||||
isch_map["149ef584b"] = 72;
|
isch_map[0x0149ef584bULL] = 72;
|
||||||
isch_map["17dd2dddc"] = 73;
|
isch_map[0x017dd2dddcULL] = 73;
|
||||||
isch_map["11124f205"] = 74;
|
isch_map[0x011124f205ULL] = 74;
|
||||||
isch_map["125197792"] = 75;
|
isch_map[0x0125197792ULL] = 75;
|
||||||
isch_map["1d44cf93a"] = 76;
|
isch_map[0x01d44cf93aULL] = 76;
|
||||||
isch_map["1e0717cad"] = 77;
|
isch_map[0x01e0717cadULL] = 77;
|
||||||
isch_map["18c875374"] = 78;
|
isch_map[0x018c875374ULL] = 78;
|
||||||
isch_map["1b8bad6e3"] = 79;
|
isch_map[0x01b8bad6e3ULL] = 79;
|
||||||
isch_map["a456534c6"] = 80;
|
isch_map[0x0a456534c6ULL] = 80;
|
||||||
isch_map["a7158b151"] = 81;
|
isch_map[0x0a7158b151ULL] = 81;
|
||||||
isch_map["a1dae9e88"] = 82;
|
isch_map[0x0a1dae9e88ULL] = 82;
|
||||||
isch_map["a29931b1f"] = 83;
|
isch_map[0x0a29931b1fULL] = 83;
|
||||||
isch_map["ad8c695b7"] = 84;
|
isch_map[0x0ad8c695b7ULL] = 84;
|
||||||
isch_map["aecfb1020"] = 85;
|
isch_map[0x0aecfb1020ULL] = 85;
|
||||||
isch_map["a800d3ff9"] = 86;
|
isch_map[0x0a800d3ff9ULL] = 86;
|
||||||
isch_map["ab430ba6e"] = 87;
|
isch_map[0x0ab430ba6eULL] = 87;
|
||||||
isch_map["341e8afb4"] = 88;
|
isch_map[0x0341e8afb4ULL] = 88;
|
||||||
isch_map["375d52a23"] = 89;
|
isch_map[0x0375d52a23ULL] = 89;
|
||||||
isch_map["3192305fa"] = 90;
|
isch_map[0x03192305faULL] = 90;
|
||||||
isch_map["32d1e806d"] = 91;
|
isch_map[0x032d1e806dULL] = 91;
|
||||||
isch_map["3dc4b0ec5"] = 92;
|
isch_map[0x03dc4b0ec5ULL] = 92;
|
||||||
isch_map["3e8768b52"] = 93;
|
isch_map[0x03e8768b52ULL] = 93;
|
||||||
isch_map["38480a48b"] = 94;
|
isch_map[0x038480a48bULL] = 94;
|
||||||
isch_map["3b0bd211c"] = 95;
|
isch_map[0x03b0bd211cULL] = 95;
|
||||||
isch_map["44dbc12b7"] = 96;
|
isch_map[0x044dbc12b7ULL] = 96;
|
||||||
isch_map["479819720"] = 97;
|
isch_map[0x0479819720ULL] = 97;
|
||||||
isch_map["41577b8f9"] = 98;
|
isch_map[0x041577b8f9ULL] = 98;
|
||||||
isch_map["4214a3d6e"] = 99;
|
isch_map[0x04214a3d6eULL] = 99;
|
||||||
isch_map["4d01fb3c6"] = 100;
|
isch_map[0x04d01fb3c6ULL] = 100;
|
||||||
isch_map["4e4223651"] = 101;
|
isch_map[0x04e4223651ULL] = 101;
|
||||||
isch_map["488d41988"] = 102;
|
isch_map[0x0488d41988ULL] = 102;
|
||||||
isch_map["4bce99c1f"] = 103;
|
isch_map[0x04bce99c1fULL] = 103;
|
||||||
isch_map["d493189c5"] = 104;
|
isch_map[0x0d493189c5ULL] = 104;
|
||||||
isch_map["d7d0c0c52"] = 105;
|
isch_map[0x0d7d0c0c52ULL] = 105;
|
||||||
isch_map["d11fa238b"] = 106;
|
isch_map[0x0d11fa238bULL] = 106;
|
||||||
isch_map["d25c7a61c"] = 107;
|
isch_map[0x0d25c7a61cULL] = 107;
|
||||||
isch_map["dd49228b4"] = 108;
|
isch_map[0x0dd49228b4ULL] = 108;
|
||||||
isch_map["de0afad23"] = 109;
|
isch_map[0x0de0afad23ULL] = 109;
|
||||||
isch_map["d8c5982fa"] = 110;
|
isch_map[0x0d8c5982faULL] = 110;
|
||||||
isch_map["db864076d"] = 111;
|
isch_map[0x0db864076dULL] = 111;
|
||||||
isch_map["645bbe548"] = 112;
|
isch_map[0x0645bbe548ULL] = 112;
|
||||||
isch_map["6718660df"] = 113;
|
isch_map[0x06718660dfULL] = 113;
|
||||||
isch_map["61d704f06"] = 114;
|
isch_map[0x061d704f06ULL] = 114;
|
||||||
isch_map["6294dca91"] = 115;
|
isch_map[0x06294dca91ULL] = 115;
|
||||||
isch_map["6d8184439"] = 116;
|
isch_map[0x06d8184439ULL] = 116;
|
||||||
isch_map["6ec25c1ae"] = 117;
|
isch_map[0x06ec25c1aeULL] = 117;
|
||||||
isch_map["680d3ee77"] = 118;
|
isch_map[0x0680d3ee77ULL] = 118;
|
||||||
isch_map["6b4ee6be0"] = 119;
|
isch_map[0x06b4ee6be0ULL] = 119;
|
||||||
isch_map["f41367e3a"] = 120;
|
isch_map[0x0f41367e3aULL] = 120;
|
||||||
isch_map["f750bfbad"] = 121;
|
isch_map[0x0f750bfbadULL] = 121;
|
||||||
isch_map["f19fdd474"] = 122;
|
isch_map[0x0f19fdd474ULL] = 122;
|
||||||
isch_map["f2dc051e3"] = 123;
|
isch_map[0x0f2dc051e3ULL] = 123;
|
||||||
isch_map["fdc95df4b"] = 124;
|
isch_map[0x0fdc95df4bULL] = 124;
|
||||||
isch_map["fe8a85adc"] = 125;
|
isch_map[0x0fe8a85adcULL] = 125;
|
||||||
isch_map["f845e7505"] = 126;
|
isch_map[0x0f845e7505ULL] = 126;
|
||||||
isch_map["fb063f092"] = 127;
|
isch_map[0x0fb063f092ULL] = 127;
|
||||||
|
isch_map[0x575d57f7ffULL] = -2; // S-ISCH
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long int isch_lookup (unsigned long long int isch)
|
//I-ISCH Lookup, borrowed from OP25 (now w/ error correction)
|
||||||
|
int isch_lookup (uint64_t isch)
|
||||||
{
|
{
|
||||||
map_isch(); //initialize the lookup map
|
map_isch(); //initialize the lookup map
|
||||||
char s[64];
|
int decoded = -2;
|
||||||
unsigned long long int decoded = -2; //initialize lookup to an invalid number
|
|
||||||
sprintf(s, "%llx", isch);
|
// Must contain bit errors if no exact match, so
|
||||||
decoded = isch_map[s];
|
// look for codeword with smallest hamming distance
|
||||||
return(decoded);
|
if (isch_map.find(isch) == isch_map.end())
|
||||||
}
|
{
|
||||||
|
int popct, popmin = 40;
|
||||||
|
for (auto it = isch_map.begin(); it != isch_map.end(); ++it)
|
||||||
|
{
|
||||||
|
popct = __builtin_popcountll(isch ^ it->first);
|
||||||
|
// Encoding is (40, 9, 16) which can correct at most 7 bit-errors
|
||||||
|
if ((popct <= 7) && (popct < popmin))
|
||||||
|
{
|
||||||
|
decoded = it->second;
|
||||||
|
popmin = popct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
else // No errors, so simply return the matching value
|
||||||
|
decoded = isch_map[isch];
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
|
||||||
|
}
|
||||||
809
src/m17.c
Normal file
809
src/m17.c
Normal file
@@ -0,0 +1,809 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* m17.c
|
||||||
|
* M17 Decoder (WIP)
|
||||||
|
*
|
||||||
|
* m17_scramble Bit Array from SDR++
|
||||||
|
* CRC16 from M17-Implementations (thanks again, sp5wwp)
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-07 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//try to find a fancy lfsr or calculation for this and not an array if possible
|
||||||
|
uint8_t m17_scramble[369] = {
|
||||||
|
1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1,
|
||||||
|
1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||||
|
1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0,
|
||||||
|
1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0,
|
||||||
|
1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
|
||||||
|
1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,
|
||||||
|
1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
|
||||||
|
0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,
|
||||||
|
0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
|
||||||
|
1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1,
|
||||||
|
1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
|
||||||
|
0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
|
||||||
|
0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0,
|
||||||
|
0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0,
|
||||||
|
1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0,
|
||||||
|
0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1,
|
||||||
|
1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1,
|
||||||
|
1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1,
|
||||||
|
0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
char b40[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.";
|
||||||
|
|
||||||
|
uint8_t p1[62] = {
|
||||||
|
1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1,
|
||||||
|
1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1,
|
||||||
|
1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1,
|
||||||
|
1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
//produce 6 short samples (48k) for every 1 short sample (8k)
|
||||||
|
void upsampleM17 (short invalue, short prev, short outbuf[6])
|
||||||
|
{
|
||||||
|
|
||||||
|
float c, d;
|
||||||
|
|
||||||
|
c = prev;
|
||||||
|
d = invalue;
|
||||||
|
|
||||||
|
// basic triangle interpolation
|
||||||
|
outbuf[0] = c = ((invalue * (float) 0.166) + (c * (float) 0.834));
|
||||||
|
outbuf[1] = c = ((invalue * (float) 0.332) + (c * (float) 0.668));
|
||||||
|
outbuf[2] = c = ((invalue * (float) 0.500) + (c * (float) 0.5));
|
||||||
|
outbuf[3] = c = ((invalue * (float) 0.668) + (c * (float) 0.332));
|
||||||
|
outbuf[4] = c = ((invalue * (float) 0.834) + (c * (float) 0.166));
|
||||||
|
outbuf[5] = c = d;
|
||||||
|
prev = d; //shift prev to the last d value for the next repitition
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//from M17_Implementations -- sp5wwp -- should have just looked here to begin with
|
||||||
|
//this setup looks very similar to the OP25 variant of crc16, but with a few differences (uses packed bytes)
|
||||||
|
uint16_t crc16m17(const uint8_t *in, const uint16_t len)
|
||||||
|
{
|
||||||
|
uint32_t crc = 0xFFFF; //init val
|
||||||
|
uint16_t poly = 0x5935;
|
||||||
|
for(uint16_t i=0; i<len; i++)
|
||||||
|
{
|
||||||
|
crc^=in[i]<<8;
|
||||||
|
for(uint8_t j=0; j<8; j++)
|
||||||
|
{
|
||||||
|
crc<<=1;
|
||||||
|
if(crc&0x10000)
|
||||||
|
crc=(crc^poly)&0xFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc&(0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void M17decodeCSD(dsd_state * state, unsigned long long int dst, unsigned long long int src)
|
||||||
|
{
|
||||||
|
//evaluate dst and src, and determine if they need to be converted to calsign
|
||||||
|
int i, j;
|
||||||
|
char c;
|
||||||
|
unsigned long long int value;
|
||||||
|
|
||||||
|
if (dst == 0xFFFFFFFFFFFF)
|
||||||
|
fprintf (stderr, " DST: BROADCAST");
|
||||||
|
else if (dst == 0)
|
||||||
|
fprintf (stderr, " DST: RESERVED %012llx", dst);
|
||||||
|
else if (dst >= 0xEE6B28000000)
|
||||||
|
fprintf (stderr, " DST: RESERVED %012llx", dst);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, " DST: ");
|
||||||
|
for (i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
c = b40[dst % 40];
|
||||||
|
state->m17_dst_csd[i] = c;
|
||||||
|
fprintf (stderr, "%c", c);
|
||||||
|
dst = dst / 40;
|
||||||
|
}
|
||||||
|
//assign completed CSD to a more useful string instead
|
||||||
|
sprintf (state->m17_dst_str, "%c%c%c%c%c%c%c%c%c",
|
||||||
|
state->m17_dst_csd[0], state->m17_dst_csd[1], state->m17_dst_csd[2], state->m17_dst_csd[3],
|
||||||
|
state->m17_dst_csd[4], state->m17_dst_csd[5], state->m17_dst_csd[6], state->m17_dst_csd[7], state->m17_dst_csd[8]);
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, "DT: %s", state->m17_dst_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src == 0xFFFFFFFFFFFF)
|
||||||
|
fprintf (stderr, " SRC: UNKNOWN FFFFFFFFFFFF");
|
||||||
|
else if (src == 0)
|
||||||
|
fprintf (stderr, " SRC: RESERVED %012llx", src);
|
||||||
|
else if (src >= 0xEE6B28000000)
|
||||||
|
fprintf (stderr, " SRC: RESERVED %012llx", src);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, " SRC: ");
|
||||||
|
for (i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
c = b40[src % 40];
|
||||||
|
state->m17_src_csd[i] = c;
|
||||||
|
fprintf (stderr, "%c", c);
|
||||||
|
src = src / 40;
|
||||||
|
}
|
||||||
|
//assign completed CSD to a more useful string instead
|
||||||
|
sprintf (state->m17_src_str, "%c%c%c%c%c%c%c%c%c",
|
||||||
|
state->m17_src_csd[0], state->m17_src_csd[1], state->m17_src_csd[2], state->m17_src_csd[3],
|
||||||
|
state->m17_src_csd[4], state->m17_src_csd[5], state->m17_src_csd[6], state->m17_src_csd[7], state->m17_src_csd[8]);
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, "ST: %s", state->m17_src_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, " DST: %012llX SRC: %012llX", state->m17_dst, state->m17_src);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void M17decodeLSF(dsd_state * state)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
unsigned long long int lsf_dst = (unsigned long long int)ConvertBitIntoBytes(&state->m17_lsf[0], 48);
|
||||||
|
unsigned long long int lsf_src = (unsigned long long int)ConvertBitIntoBytes(&state->m17_lsf[48], 48);
|
||||||
|
uint16_t lsf_type = (uint16_t)ConvertBitIntoBytes(&state->m17_lsf[96], 16);
|
||||||
|
|
||||||
|
//this is the way the manual/code expects you to read these bits
|
||||||
|
// uint8_t lsf_ps = (lsf_type >> 0) & 0x1;
|
||||||
|
uint8_t lsf_dt = (lsf_type >> 1) & 0x3;
|
||||||
|
uint8_t lsf_et = (lsf_type >> 3) & 0x3;
|
||||||
|
uint8_t lsf_es = (lsf_type >> 5) & 0x3;
|
||||||
|
uint8_t lsf_cn = (lsf_type >> 7) & 0xF;
|
||||||
|
uint8_t lsf_rs = (lsf_type >> 11) & 0x1F;
|
||||||
|
|
||||||
|
//store this so we can reference it for playing voice and/or decoding data, dst/src etc
|
||||||
|
state->m17_str_dt = lsf_dt;
|
||||||
|
state->m17_dst = lsf_dst;
|
||||||
|
state->m17_src = lsf_src;
|
||||||
|
state->m17_can = lsf_cn;
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
//packet or stream
|
||||||
|
// if (lsf_ps == 0) fprintf (stderr, " P-");
|
||||||
|
// if (lsf_ps == 1) fprintf (stderr, " S-");
|
||||||
|
|
||||||
|
fprintf (stderr, " CAN: %d", lsf_cn);
|
||||||
|
M17decodeCSD(state, lsf_dst, lsf_src);
|
||||||
|
|
||||||
|
if (lsf_dt == 0) fprintf (stderr, " Reserved");
|
||||||
|
if (lsf_dt == 1) fprintf (stderr, " Data");
|
||||||
|
if (lsf_dt == 2) fprintf (stderr, " Voice (3200bps)");
|
||||||
|
if (lsf_dt == 3) fprintf (stderr, " Voice + Data");
|
||||||
|
|
||||||
|
if (lsf_rs != 0) fprintf (stderr, " RS: %02X", lsf_rs);
|
||||||
|
|
||||||
|
if (lsf_et != 0) fprintf (stderr, " ENC:");
|
||||||
|
if (lsf_et == 2) fprintf (stderr, " AES-CTR");
|
||||||
|
if (lsf_et == 1) fprintf (stderr, " Scrambler - %d", lsf_es);
|
||||||
|
|
||||||
|
state->m17_enc = lsf_et;
|
||||||
|
state->m17_enc_st = lsf_es;
|
||||||
|
|
||||||
|
//pack meta bits into 14 bytes
|
||||||
|
for (i = 0; i < 14; i++)
|
||||||
|
state->m17_meta[i] = (uint8_t)ConvertBitIntoBytes(&state->m17_lsf[(i*8)+112], 8);
|
||||||
|
|
||||||
|
//Examine the Meta data
|
||||||
|
if (lsf_et == 0 && state->m17_meta[0] != 0) //not sure if this applies universally, or just to text data byte 0 for null data
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Meta:");
|
||||||
|
if (lsf_es == 0) fprintf (stderr, " Text Data");
|
||||||
|
if (lsf_es == 1) fprintf (stderr, " GNSS Pos");
|
||||||
|
if (lsf_es == 2) fprintf (stderr, " Extended CSD");
|
||||||
|
if (lsf_es == 3) fprintf (stderr, " Reserved");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lsf_et == 2)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " IV: ");
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
fprintf (stderr, "%02X", state->m17_meta[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int M17processLICH(dsd_state * state, dsd_opts * opts, uint8_t * lich_bits)
|
||||||
|
{
|
||||||
|
int i, j, err;
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
uint8_t lich[4][24];
|
||||||
|
uint8_t lich_decoded[48];
|
||||||
|
uint8_t temp[96];
|
||||||
|
bool g[4];
|
||||||
|
|
||||||
|
//for testing bit arrangement vs the crc code
|
||||||
|
uint8_t bs[240];
|
||||||
|
memset(bs, 1, sizeof(bs));
|
||||||
|
|
||||||
|
uint16_t crc_cmp = 0;
|
||||||
|
uint16_t crc_ext = 0;
|
||||||
|
uint8_t crc_err = 0;
|
||||||
|
|
||||||
|
memset(lich, 0, sizeof(lich));
|
||||||
|
memset(lich_decoded, 0, sizeof(lich_decoded));
|
||||||
|
memset(temp, 0, sizeof(temp));
|
||||||
|
|
||||||
|
//execute golay 24,12 or 4 24-bit chunks and reorder into 4 12-bit chunks
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
g[i] = TRUE;
|
||||||
|
|
||||||
|
for (j = 0; j < 24; j++)
|
||||||
|
lich[i][j] = lich_bits[(i*24)+j];
|
||||||
|
|
||||||
|
g[i] = Golay_24_12_decode(lich[i]);
|
||||||
|
if(g[i] == FALSE) err = -1;
|
||||||
|
|
||||||
|
for (j = 0; j < 12; j++)
|
||||||
|
lich_decoded[i*12+j] = lich[i][j];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t lich_counter = (uint8_t)ConvertBitIntoBytes(&lich_decoded[40], 3); //lich_cunt
|
||||||
|
uint8_t lich_reserve = (uint8_t)ConvertBitIntoBytes(&lich_decoded[43], 5); //lich_reserved
|
||||||
|
|
||||||
|
if (err == 0)
|
||||||
|
fprintf (stderr, "LC: %d/6 ", lich_counter+1);
|
||||||
|
else fprintf (stderr, "LICH G24 ERR");
|
||||||
|
|
||||||
|
if (err = 0 && lich_reserve != 0) fprintf(stderr, " LRS: %d", lich_reserve);
|
||||||
|
|
||||||
|
//transfer to storage
|
||||||
|
for (i = 0; i < 40; i++)
|
||||||
|
state->m17_lsf[lich_counter*40+i] = lich_decoded[i];
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " LICH: ");
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
|
fprintf (stderr, "[%02X]", (uint8_t)ConvertBitIntoBytes(&lich_decoded[i*8], 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t lsf_packed[30];
|
||||||
|
memset (lsf_packed, 0, sizeof(lsf_packed));
|
||||||
|
|
||||||
|
if (lich_counter == 5)
|
||||||
|
{
|
||||||
|
|
||||||
|
//need to pack bytes for the sw5wwp variant of the crc (might as well, may be useful in the future)
|
||||||
|
for (i = 0; i < 30; i++)
|
||||||
|
lsf_packed[i] = (uint8_t)ConvertBitIntoBytes(&state->m17_lsf[i*8], 8);
|
||||||
|
|
||||||
|
crc_cmp = crc16m17(lsf_packed, 28);
|
||||||
|
crc_ext = (uint16_t)ConvertBitIntoBytes(&state->m17_lsf[224], 16);
|
||||||
|
|
||||||
|
if (crc_cmp != crc_ext) crc_err = 1;
|
||||||
|
|
||||||
|
if (crc_err == 0)
|
||||||
|
M17decodeLSF(state);
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n LSF: ");
|
||||||
|
for (i = 0; i < 30; i++)
|
||||||
|
{
|
||||||
|
if (i == 15) fprintf (stderr, "\n ");
|
||||||
|
fprintf (stderr, "[%02X]", lsf_packed[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (state->m17_lsf, 1, sizeof(state->m17_lsf));
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, " E-%04X; C-%04X (CRC CHK)", crc_ext, crc_cmp);
|
||||||
|
|
||||||
|
if (crc_err == 1) fprintf (stderr, " EMB LSF CRC ERR");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void M17processCodec2_1600(dsd_opts * opts, dsd_state * state, uint8_t * payload)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char voice1[8];
|
||||||
|
unsigned char voice2[8];
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
voice1[i] = (unsigned char)ConvertBitIntoBytes(&payload[i*8+0], 8);
|
||||||
|
voice2[i] = (unsigned char)ConvertBitIntoBytes(&payload[i*8+64], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Add some decryption methods
|
||||||
|
if (state->m17_enc != 0)
|
||||||
|
{
|
||||||
|
//process scrambler or AES-CTR decryption
|
||||||
|
//(no AES-CTR right now, Scrambler should be easy enough)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n CODEC2: ");
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
fprintf (stderr, "%02X", voice1[i]);
|
||||||
|
fprintf (stderr, " (1600)");
|
||||||
|
|
||||||
|
fprintf (stderr, "\n LSDATA: "); //low speed data
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
fprintf (stderr, "%02X", voice2[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_CODEC2
|
||||||
|
size_t nsam;
|
||||||
|
nsam = 160;
|
||||||
|
|
||||||
|
//converted to using allocated memory pointers to prevent the overflow issues
|
||||||
|
short * samp1 = malloc (sizeof(short) * nsam);
|
||||||
|
short * upsamp = malloc (sizeof(short) * nsam * 6);
|
||||||
|
short * out = malloc (sizeof(short) * 6);
|
||||||
|
short prev;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
codec2_decode(state->codec2_1600, samp1, voice1);
|
||||||
|
|
||||||
|
if (opts->audio_out_type == 0 && state->m17_enc == 0) //Pulse Audio
|
||||||
|
{
|
||||||
|
pa_simple_write(opts->pulse_digi_dev_out, samp1, nsam*2, NULL);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
if (opts->audio_out_type == 5 && state->m17_enc == 0) //OSS w/ required upsample to 48k
|
||||||
|
{
|
||||||
|
//upsample to 48k and then play
|
||||||
|
prev = samp1[0];
|
||||||
|
for (i = 0; i < 160; i++)
|
||||||
|
{
|
||||||
|
upsampleM17 (samp1[i], prev, out);
|
||||||
|
for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j];
|
||||||
|
}
|
||||||
|
write (opts->audio_out_fd, upsamp, nsam*2*6);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (opts->audio_out_type == 5 && state->m17_enc == 0) //OSS via PADSP at 8k
|
||||||
|
{
|
||||||
|
write (opts->audio_out_fd, samp1, nsam*2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//WIP: Wav file saving -- still need a way to open/close/label wav files similar to call history
|
||||||
|
if(opts->wav_out_f != NULL && state->m17_enc == 0) //WAV
|
||||||
|
{
|
||||||
|
sf_write_short(opts->wav_out_f, samp1, nsam);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Codec2 Raw file saving
|
||||||
|
// if(mbe_out_dir)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
free (samp1);
|
||||||
|
free (upsamp);
|
||||||
|
free (out);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void M17processCodec2_3200(dsd_opts * opts, dsd_state * state, uint8_t * payload)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char voice1[8];
|
||||||
|
unsigned char voice2[8];
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
voice1[i] = (unsigned char)ConvertBitIntoBytes(&payload[i*8+0], 8);
|
||||||
|
voice2[i] = (unsigned char)ConvertBitIntoBytes(&payload[i*8+64], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Add some decryption methods
|
||||||
|
if (state->m17_enc != 0)
|
||||||
|
{
|
||||||
|
//process scrambler or AES-CTR decryption
|
||||||
|
//(no AES-CTR right now, Scrambler should be easy enough)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n CODEC2: ");
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
fprintf (stderr, "%02X", voice1[i]);
|
||||||
|
fprintf (stderr, " (3200)");
|
||||||
|
|
||||||
|
fprintf (stderr, "\n CODEC2: ");
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
fprintf (stderr, "%02X", voice2[i]);
|
||||||
|
fprintf (stderr, " (3200)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_CODEC2
|
||||||
|
size_t nsam;
|
||||||
|
nsam = 160;
|
||||||
|
|
||||||
|
//converted to using allocated memory pointers to prevent the overflow issues
|
||||||
|
short * samp1 = malloc (sizeof(short) * nsam);
|
||||||
|
short * samp2 = malloc (sizeof(short) * nsam);
|
||||||
|
short * upsamp = malloc (sizeof(short) * nsam * 6);
|
||||||
|
short * out = malloc (sizeof(short) * 6);
|
||||||
|
short prev;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
codec2_decode(state->codec2_3200, samp1, voice1);
|
||||||
|
codec2_decode(state->codec2_3200, samp2, voice2);
|
||||||
|
|
||||||
|
if (opts->audio_out_type == 0 && state->m17_enc == 0) //Pulse Audio
|
||||||
|
{
|
||||||
|
pa_simple_write(opts->pulse_digi_dev_out, samp1, nsam*2, NULL);
|
||||||
|
pa_simple_write(opts->pulse_digi_dev_out, samp2, nsam*2, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
if (opts->audio_out_type == 5 && state->m17_enc == 0) //OSS w/ required upsample to 48k
|
||||||
|
{
|
||||||
|
//upsample to 48k and then play
|
||||||
|
// prev = samp1[0];
|
||||||
|
prev = 0;
|
||||||
|
for (i = 0; i < 160; i++)
|
||||||
|
{
|
||||||
|
upsampleM17 (samp1[i], prev, out);
|
||||||
|
for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j];
|
||||||
|
}
|
||||||
|
write (opts->audio_out_fd, upsamp, nsam*2*6);
|
||||||
|
prev = samp2[0];
|
||||||
|
for (i = 0; i < 160; i++)
|
||||||
|
{
|
||||||
|
upsampleM17 (samp2[i], prev, out);
|
||||||
|
for (j = 0; j < 6; j++) upsamp[(i*6)+j] = out[j];
|
||||||
|
}
|
||||||
|
write (opts->audio_out_fd, upsamp, nsam*2*6);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (opts->audio_out_type == 5 && state->m17_enc == 0) //OSS via PADSP at 8k
|
||||||
|
{
|
||||||
|
write (opts->audio_out_fd, samp1, nsam*2);
|
||||||
|
write (opts->audio_out_fd, samp2, nsam*2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
if (opts->audio_out_type == 1 && state->m17_enc == 0) //STDOUT
|
||||||
|
{
|
||||||
|
write (opts->audio_out_fd, samp1, nsam*2);
|
||||||
|
write (opts->audio_out_fd, samp2, nsam*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//WIP: Wav file saving -- still need a way to open/close/label wav files similar to call history
|
||||||
|
if(opts->wav_out_f != NULL && state->m17_enc == 0) //WAV
|
||||||
|
{
|
||||||
|
sf_write_short(opts->wav_out_f, samp1, nsam);
|
||||||
|
sf_write_short(opts->wav_out_f, samp2, nsam);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Codec2 Raw file saving
|
||||||
|
// if(mbe_out_dir)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
free (samp1);
|
||||||
|
free (samp2);
|
||||||
|
free (upsamp);
|
||||||
|
free (out);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void M17prepareStream(dsd_opts * opts, dsd_state * state, uint8_t * m17_bits)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i, k, x;
|
||||||
|
uint8_t m17_punc[275]; //25 * 11 = 275
|
||||||
|
memset (m17_punc, 0, sizeof(m17_punc));
|
||||||
|
for (i = 0; i < 272; i++)
|
||||||
|
m17_punc[i] = m17_bits[i+96];
|
||||||
|
|
||||||
|
//depuncture the bits
|
||||||
|
uint8_t m17_depunc[300]; //25 * 12 = 300
|
||||||
|
memset (m17_depunc, 0, sizeof(m17_depunc));
|
||||||
|
k = 0; x = 0;
|
||||||
|
for (i = 0; i < 25; i++)
|
||||||
|
{
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = m17_punc[x++];
|
||||||
|
m17_depunc[k++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//setup the convolutional decoder
|
||||||
|
uint8_t temp[300];
|
||||||
|
uint8_t s0;
|
||||||
|
uint8_t s1;
|
||||||
|
uint8_t m_data[28];
|
||||||
|
uint8_t trellis_buf[144];
|
||||||
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
|
memset (temp, 0, sizeof (temp));
|
||||||
|
memset (m_data, 0, sizeof (m_data));
|
||||||
|
|
||||||
|
for (i = 0; i < 296; i++)
|
||||||
|
temp[i] = m17_depunc[i] << 1;
|
||||||
|
|
||||||
|
CNXDNConvolution_start();
|
||||||
|
for (i = 0; i < 148; i++)
|
||||||
|
{
|
||||||
|
s0 = temp[(2*i)];
|
||||||
|
s1 = temp[(2*i)+1];
|
||||||
|
|
||||||
|
CNXDNConvolution_decode(s0, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CNXDNConvolution_chainback(m_data, 144);
|
||||||
|
|
||||||
|
//144/8 = 18, last 4 (144-148) are trailing zeroes
|
||||||
|
for(i = 0; i < 18; 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//load m_data into bits for either data packets or voice packets
|
||||||
|
uint8_t payload[128];
|
||||||
|
uint8_t end = 9;
|
||||||
|
uint16_t fn = 0;
|
||||||
|
memset (payload, 0, sizeof(payload));
|
||||||
|
|
||||||
|
end = trellis_buf[0];
|
||||||
|
fn = (uint16_t)ConvertBitIntoBytes(&trellis_buf[1], 15);
|
||||||
|
|
||||||
|
//insert fn bits into meta 14 and meta 15 for Initialization Vector
|
||||||
|
state->m17_meta[14] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[1], 7);
|
||||||
|
state->m17_meta[15] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[8], 8);
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, " FSN: %05d", fn);
|
||||||
|
|
||||||
|
if (end == 1)
|
||||||
|
fprintf (stderr, " END;");
|
||||||
|
|
||||||
|
for (i = 0; i < 128; i++)
|
||||||
|
payload[i] = trellis_buf[i+16];
|
||||||
|
|
||||||
|
if (state->m17_str_dt == 2)
|
||||||
|
M17processCodec2_3200(opts, state, payload);
|
||||||
|
else if (state->m17_str_dt == 3)
|
||||||
|
M17processCodec2_1600(opts, state, payload);
|
||||||
|
else if (state->m17_str_dt == 1) fprintf (stderr, " DATA;");
|
||||||
|
else if (state->m17_str_dt == 0) fprintf (stderr, " RES;");
|
||||||
|
// else fprintf (stderr, " UNK;");
|
||||||
|
|
||||||
|
if (opts->payload == 1 && state->m17_str_dt < 2)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n STREAM: ");
|
||||||
|
for (i = 0; i < 18; i++)
|
||||||
|
fprintf (stderr, "[%02X]", (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void processM17STR(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i, x;
|
||||||
|
//overflow/memory issue returns in Cygwin for...reasons...
|
||||||
|
uint8_t dbuf[384]; //384-bit frame - 16-bit (8 symbol) sync pattern (184 dibits)
|
||||||
|
uint8_t m17_rnd_bits[368]; //368 bits that are still scrambled (randomized)
|
||||||
|
uint8_t m17_int_bits[368]; //368 bits that are still interleaved
|
||||||
|
uint8_t m17_bits[368]; //368 bits that have been de-interleaved and de-scramble
|
||||||
|
uint8_t lich_bits[96];
|
||||||
|
int lich_err = -1;
|
||||||
|
|
||||||
|
memset (dbuf, 0, sizeof(dbuf));
|
||||||
|
memset (m17_rnd_bits, 0, sizeof(m17_rnd_bits));
|
||||||
|
memset (m17_int_bits, 0, sizeof(m17_int_bits));
|
||||||
|
memset (m17_bits, 0, sizeof(m17_bits));
|
||||||
|
memset (lich_bits, 0, sizeof(lich_bits));
|
||||||
|
|
||||||
|
//load dibits into dibit buffer
|
||||||
|
for (i = 0; i < 184; i++)
|
||||||
|
dbuf[i] = (uint8_t) getDibit(opts, state);
|
||||||
|
|
||||||
|
//convert dbuf into a bit array
|
||||||
|
for (i = 0; i < 184; i++)
|
||||||
|
{
|
||||||
|
m17_rnd_bits[i*2+0] = (dbuf[i] >> 1) & 1;
|
||||||
|
m17_rnd_bits[i*2+1] = (dbuf[i] >> 0) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//descramble the frame
|
||||||
|
for (i = 0; i < 368; i++)
|
||||||
|
m17_int_bits[i] = (m17_rnd_bits[i] ^ m17_scramble[i]) & 1;
|
||||||
|
|
||||||
|
//deinterleave the bit array using Quadratic Permutation Polynomial
|
||||||
|
//function π(x) = (45x + 92x^2 ) mod 368
|
||||||
|
for (i = 0; i < 368; i++)
|
||||||
|
{
|
||||||
|
x = ((45*i)+(92*i*i)) % 368;
|
||||||
|
m17_bits[i] = m17_int_bits[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 96; i++)
|
||||||
|
lich_bits[i] = m17_bits[i];
|
||||||
|
|
||||||
|
//check lich first, and handle LSF chunk and completed LSF
|
||||||
|
lich_err = M17processLICH(state, opts, lich_bits);
|
||||||
|
|
||||||
|
if (lich_err == 0)
|
||||||
|
M17prepareStream(opts, state, m17_bits);
|
||||||
|
|
||||||
|
// if (lich_err != 0) state->lastsynctype = -1; //disable
|
||||||
|
|
||||||
|
//ending linebreak
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
} //end processM17STR
|
||||||
|
|
||||||
|
void processM17LSF(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
//NOTE: Have not been able to get this to work successfully, but considering
|
||||||
|
//there is only one LSF frame at the beginning of TX, its probably just as easy to
|
||||||
|
//use and rely on the STR frame and embedded LSF chunk, which does work properly
|
||||||
|
int i, j, k, x;
|
||||||
|
uint8_t dbuf[384]; //384-bit frame - 16-bit (8 symbol) sync pattern (184 dibits)
|
||||||
|
uint8_t m17_rnd_bits[500]; //368 bits that are still scrambled (randomized)
|
||||||
|
uint8_t m17_int_bits[500]; //368 bits that are still interleaved
|
||||||
|
uint8_t m17_bits[500]; //368 bits that have been de-interleaved and de-scramble
|
||||||
|
uint8_t m17_depunc[500]; //488 bits after depuncturing
|
||||||
|
|
||||||
|
memset (dbuf, 0, sizeof(dbuf));
|
||||||
|
memset (m17_rnd_bits, 0, sizeof(m17_rnd_bits));
|
||||||
|
memset (m17_int_bits, 0, sizeof(m17_int_bits));
|
||||||
|
memset (m17_bits, 0, sizeof(m17_bits));
|
||||||
|
memset (m17_depunc, 0, sizeof(m17_depunc));
|
||||||
|
|
||||||
|
//load dibits into dibit buffer
|
||||||
|
for (i = 0; i < 184; i++)
|
||||||
|
dbuf[i] = (uint8_t) getDibit(opts, state);
|
||||||
|
|
||||||
|
//convert dbuf into a bit array
|
||||||
|
for (i = 0; i < 184; i++)
|
||||||
|
{
|
||||||
|
m17_rnd_bits[i*2+0] = (dbuf[i] >> 1) & 1;
|
||||||
|
m17_rnd_bits[i*2+1] = (dbuf[i] >> 0) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//descramble the frame
|
||||||
|
for (i = 0; i < 368; i++)
|
||||||
|
m17_int_bits[i] = (m17_rnd_bits[i] ^ m17_scramble[i]) & 1;
|
||||||
|
|
||||||
|
//deinterleave the bit array using Quadratic Permutation Polynomial
|
||||||
|
//function π(x) = (45x + 92x^2 ) mod 368
|
||||||
|
for (i = 0; i < 368; i++)
|
||||||
|
{
|
||||||
|
x = ((45*i)+(92*i*i)) % 368;
|
||||||
|
m17_bits[i] = m17_int_bits[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
j = 0; k = 0; x = 0;
|
||||||
|
|
||||||
|
// P1 Depuncture
|
||||||
|
for (i = 0; i < 488; i++)
|
||||||
|
{
|
||||||
|
if (p1[k++] == 1) m17_depunc[x++] = m17_bits[j++];
|
||||||
|
else m17_depunc[x++] = 1;
|
||||||
|
|
||||||
|
if (k == 61) k = 0; //61 -- should reset 8 times againt the array
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug -- values seem okay at end of run
|
||||||
|
// fprintf (stderr, "K = %d; J = %d; X = %d", k, j, x);
|
||||||
|
|
||||||
|
//setup the convolutional decoder
|
||||||
|
uint8_t temp[500];
|
||||||
|
uint8_t s0;
|
||||||
|
uint8_t s1;
|
||||||
|
uint8_t m_data[30];
|
||||||
|
uint8_t trellis_buf[240]; //30*8 = 240
|
||||||
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
|
memset (temp, 0, sizeof (temp));
|
||||||
|
memset (m_data, 0, sizeof (m_data));
|
||||||
|
|
||||||
|
for (i = 0; i < 488; i++)
|
||||||
|
temp[i] = m17_depunc[i] << 1;
|
||||||
|
|
||||||
|
CNXDNConvolution_start();
|
||||||
|
for (i = 0; i < 244; i++)
|
||||||
|
{
|
||||||
|
s0 = temp[(2*i)];
|
||||||
|
s1 = temp[(2*i)+1];
|
||||||
|
|
||||||
|
CNXDNConvolution_decode(s0, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CNXDNConvolution_chainback(m_data, 240);
|
||||||
|
|
||||||
|
//244/8 = 30, last 4 (244-248) are trailing zeroes
|
||||||
|
for(i = 0; i < 30; 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (state->m17_lsf, 0, sizeof(state->m17_lsf));
|
||||||
|
memcpy (state->m17_lsf, trellis_buf, 240);
|
||||||
|
|
||||||
|
uint8_t lsf_packed[30];
|
||||||
|
memset (lsf_packed, 0, sizeof(lsf_packed));
|
||||||
|
|
||||||
|
//need to pack bytes for the sw5wwp variant of the crc (might as well, may be useful in the future)
|
||||||
|
for (i = 0; i < 30; i++)
|
||||||
|
lsf_packed[i] = (uint8_t)ConvertBitIntoBytes(&state->m17_lsf[i*8], 8);
|
||||||
|
|
||||||
|
uint16_t crc_cmp = crc16m17(lsf_packed, 28);
|
||||||
|
uint16_t crc_ext = (uint16_t)ConvertBitIntoBytes(&state->m17_lsf[224], 16);
|
||||||
|
int crc_err = 0;
|
||||||
|
|
||||||
|
if (crc_cmp != crc_ext) crc_err = 1;
|
||||||
|
|
||||||
|
if (crc_err == 0)
|
||||||
|
M17decodeLSF(state);
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n LSF: ");
|
||||||
|
for (i = 0; i < 30; i++)
|
||||||
|
{
|
||||||
|
if (i == 15) fprintf (stderr, "\n ");
|
||||||
|
fprintf (stderr, "[%02X]", lsf_packed[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, " E-%04X; C-%04X (CRC CHK)", crc_ext, crc_cmp);
|
||||||
|
|
||||||
|
if (crc_err == 1) fprintf (stderr, " CRC ERR");
|
||||||
|
|
||||||
|
//ending linebreak
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
} //end processM17LSF
|
||||||
@@ -36,27 +36,27 @@
|
|||||||
|
|
||||||
|
|
||||||
/* Global variables ---------------------------------------------------------*/
|
/* Global variables ---------------------------------------------------------*/
|
||||||
static const uint8_t CNXDNConvolution_BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
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 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])
|
#define READ_BIT1(p,i) (p[(i)>>3] & CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
|
||||||
|
|
||||||
static const uint8_t CNXDNConvolution_BRANCH_TABLE1[] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U};
|
static const uint8_t CNXDNConvolution_BRANCH_TABLE1[8] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U};
|
||||||
static const uint8_t CNXDNConvolution_BRANCH_TABLE2[] = {0U, 2U, 2U, 0U, 0U, 2U, 2U, 0U};
|
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 unsigned int CNXDNConvolution_NUM_OF_STATES_D2 = 8U;
|
||||||
static const unsigned int CNXDNConvolution_NUM_OF_STATES = 16U;
|
static const unsigned int CNXDNConvolution_NUM_OF_STATES = 16U;
|
||||||
static const uint32_t CNXDNConvolution_M = 4U;
|
static const uint32_t CNXDNConvolution_M = 4U;
|
||||||
static const unsigned int CNXDNConvolution_K = 5U;
|
static const unsigned int CNXDNConvolution_K = 5U;
|
||||||
|
|
||||||
static uint16_t m_metrics1[16U] = {0};
|
//NOTE:
|
||||||
static uint16_t m_metrics2[16U] = {0};
|
static uint16_t m_metrics1[16U];
|
||||||
static uint64_t m_decisions[300U] = {0};
|
static uint16_t m_metrics2[16U];
|
||||||
|
static uint64_t m_decisions[300U]; //300
|
||||||
static uint16_t * m_oldMetrics = NULL;
|
static uint16_t * m_oldMetrics = NULL;
|
||||||
static uint16_t * m_newMetrics = NULL;
|
static uint16_t * m_newMetrics = NULL;
|
||||||
static uint64_t * m_dp = NULL;
|
static uint64_t * m_dp = NULL;
|
||||||
|
|
||||||
|
|
||||||
/* Functions ----------------------------------------------------------------*/
|
/* Functions ----------------------------------------------------------------*/
|
||||||
|
|
||||||
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
|
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
|
||||||
@@ -158,13 +158,15 @@ void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsign
|
|||||||
|
|
||||||
void CNXDNConvolution_start(void)
|
void CNXDNConvolution_start(void)
|
||||||
{
|
{
|
||||||
memset(m_metrics1, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
|
|
||||||
memset(m_metrics2, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
|
|
||||||
|
|
||||||
m_oldMetrics = m_metrics1;
|
m_oldMetrics = m_metrics1;
|
||||||
m_newMetrics = m_metrics2;
|
m_newMetrics = m_metrics2;
|
||||||
m_dp = m_decisions;
|
m_dp = m_decisions;
|
||||||
|
|
||||||
|
memset(m_metrics1, 0x00U, sizeof(m_metrics1));
|
||||||
|
memset(m_metrics2, 0x00U, sizeof(m_metrics2));
|
||||||
|
memset(m_decisions, 0x00U, sizeof(m_decisions));
|
||||||
|
|
||||||
for (int x = 0; x < 4; x++) CNXDNConvolution_decode(0U, 0U);
|
for (int x = 0; x < 4; x++) CNXDNConvolution_decode(0U, 0U);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,9 @@
|
|||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
#include "nxdn_const.h"
|
#include "nxdn_const.h"
|
||||||
|
|
||||||
static const uint8_t scramble_t[] = { //values are the position values we need to invert in the descramble
|
//NOTE: Descrambling was having an issue without a value inside the brackets, but only when
|
||||||
|
//the PARITY table was disabled, was this due to a memory issue or overflow?
|
||||||
|
static const uint8_t scramble_t[182] = { //values are the position values we need to invert in the descramble
|
||||||
2, 5, 6, 7, 10, 12, 14, 16, 17, 22, 23, 25, 26, 27, 28, 30, 33, 34, 36, 37, 38, 41, 45, 47,
|
2, 5, 6, 7, 10, 12, 14, 16, 17, 22, 23, 25, 26, 27, 28, 30, 33, 34, 36, 37, 38, 41, 45, 47,
|
||||||
52, 54, 56, 57, 59, 62, 63, 64, 65, 66, 67, 69, 70, 73, 76, 79, 81, 82, 84, 85, 86, 87, 88,
|
52, 54, 56, 57, 59, 62, 63, 64, 65, 66, 67, 69, 70, 73, 76, 79, 81, 82, 84, 85, 86, 87, 88,
|
||||||
89, 92, 95, 96, 98, 100, 103, 104, 107, 108, 116, 117, 121, 122, 125, 127, 131, 132, 134,
|
89, 92, 95, 96, 98, 100, 103, 104, 107, 108, 116, 117, 121, 122, 125, 127, 131, 132, 134,
|
||||||
@@ -33,16 +35,12 @@ static const uint8_t scramble_t[] = { //values are the position values we need t
|
|||||||
168, 170, 171, 174, 175, 176, 177, 181
|
168, 170, 171, 174, 175, 176, 177, 181
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
//decoding functions here
|
//decoding functions here
|
||||||
void nxdn_descramble(uint8_t dibits[], int len)
|
void nxdn_descramble(uint8_t dibits[], int len)
|
||||||
{
|
{
|
||||||
for (int i=0; i<sizeof(scramble_t); i++) {
|
for (int i=0; i<len; i++)
|
||||||
|
{
|
||||||
if (scramble_t[i] >= len)
|
if (scramble_t[i] >= len)
|
||||||
break;
|
break;
|
||||||
dibits[scramble_t[i]] ^= 0x2; // invert sign of scrambled dibits
|
dibits[scramble_t[i]] ^= 0x2; // invert sign of scrambled dibits
|
||||||
@@ -51,13 +49,15 @@ void nxdn_descramble(uint8_t dibits[], int len)
|
|||||||
|
|
||||||
void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
||||||
{
|
{
|
||||||
uint8_t deperm[144];
|
uint8_t deperm[144]; //144
|
||||||
uint8_t depunc[192];
|
uint8_t depunc[192]; //192
|
||||||
uint8_t trellis_buf[96];
|
uint8_t trellis_buf[96]; //96
|
||||||
uint16_t crc = 0; //crc calculated by function
|
uint16_t crc = 0; //crc calculated by function
|
||||||
uint16_t check = 0; //crc from payload for comparison
|
uint16_t check = 0; //crc from payload for comparison
|
||||||
int out;
|
int out;
|
||||||
char buf[128];
|
|
||||||
|
memset (deperm, 0, sizeof(deperm));
|
||||||
|
memset (depunc, 0, sizeof(depunc));
|
||||||
|
|
||||||
for (int i=0; i<144; i++)
|
for (int i=0; i<144; i++)
|
||||||
deperm[PERM_16_9[i]] = bits[i];
|
deperm[PERM_16_9[i]] = bits[i];
|
||||||
@@ -70,26 +70,19 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
|||||||
}
|
}
|
||||||
|
|
||||||
//switch to the convolutional decoder
|
//switch to the convolutional decoder
|
||||||
uint8_t temp[210];
|
uint8_t temp[200];
|
||||||
uint8_t s0;
|
uint8_t s0;
|
||||||
uint8_t s1;
|
uint8_t s1;
|
||||||
uint8_t m_data[13];
|
uint8_t m_data[20]; //13
|
||||||
memset (temp, 0, sizeof(temp));
|
memset (temp, 0, sizeof(temp));
|
||||||
memset (m_data, 0, sizeof(m_data));
|
memset (m_data, 0, sizeof(m_data));
|
||||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
|
|
||||||
for (int i = 0; i < 192; i++)
|
for (int i = 0; i < 192; i++)
|
||||||
{
|
|
||||||
temp[i] = depunc[i] << 1;
|
temp[i] = depunc[i] << 1;
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
temp[i+192] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNConvolution_start();
|
CNXDNConvolution_start();
|
||||||
for (int i = 0U; i < 100U; i++)
|
for (int i = 0; i < 96; i++)
|
||||||
{
|
{
|
||||||
s0 = temp[(2*i)];
|
s0 = temp[(2*i)];
|
||||||
s1 = temp[(2*i)+1];
|
s1 = temp[(2*i)+1];
|
||||||
@@ -97,7 +90,7 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
|||||||
CNXDNConvolution_decode(s0, s1);
|
CNXDNConvolution_decode(s0, s1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CNXDNConvolution_chainback(m_data, 96U);
|
CNXDNConvolution_chainback(m_data, 92);
|
||||||
|
|
||||||
for(int i = 0; i < 12; i++)
|
for(int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
@@ -111,38 +104,46 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
|||||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//load tail 16 bits into check variable to compare vs computed crc value
|
crc = crc12f (trellis_buf, 84); //80
|
||||||
for (int i = 0; i < 12; i++)
|
for (int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
check = check << 1;
|
check = check << 1;
|
||||||
check = check + trellis_buf[80+i];
|
check = check | trellis_buf[84+i]; //80
|
||||||
}
|
}
|
||||||
|
|
||||||
crc = crc12f (trellis_buf, 80);
|
|
||||||
|
|
||||||
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
||||||
//else if (opts->payload == 1) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
|
// else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
|
||||||
|
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " FACCH Payload ");
|
fprintf (stderr, " FACCH1 Payload ");
|
||||||
for (int i = 0; i < 12; i++)
|
for (int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
fprintf (stderr, "[%02X]", m_data[i]);
|
||||||
}
|
}
|
||||||
fprintf (stderr, " - %03X %03X", crc, check);
|
if (crc != check && opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//new sacch
|
//sacch
|
||||||
void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||||
{
|
{
|
||||||
uint8_t deperm[60];
|
//see about initializing these variables
|
||||||
uint8_t depunc[72];
|
uint8_t deperm[60]; //60
|
||||||
uint8_t trellis_buf[32];
|
uint8_t depunc[72]; //72
|
||||||
uint8_t answer[26];
|
uint8_t trellis_buf[32]; //32
|
||||||
|
|
||||||
|
memset (deperm, 0, sizeof(deperm));
|
||||||
|
memset (depunc, 0, sizeof(depunc));
|
||||||
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
|
|
||||||
int o = 0;
|
int o = 0;
|
||||||
uint8_t crc = 0; //value computed by crc6 on payload
|
uint8_t crc = 0; //value computed by crc6 on payload
|
||||||
uint8_t check = 0; //value pulled from last 6 bits
|
uint8_t check = 0; //value pulled from last 6 bits
|
||||||
@@ -168,27 +169,20 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
|||||||
}
|
}
|
||||||
|
|
||||||
//switch to the convolutional decoder
|
//switch to the convolutional decoder
|
||||||
uint8_t temp[90];
|
uint8_t temp[80];
|
||||||
uint8_t s0;
|
uint8_t s0;
|
||||||
uint8_t s1;
|
uint8_t s1;
|
||||||
uint8_t m_data[5];
|
uint8_t m_data[5]; //5
|
||||||
|
|
||||||
memset (temp, 0, sizeof (temp));
|
memset (temp, 0, sizeof (temp));
|
||||||
memset (m_data, 0, sizeof (m_data));
|
memset (m_data, 0, sizeof (m_data));
|
||||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
|
|
||||||
for (int i = 0; i < 72; i++)
|
for (int i = 0; i < 72; i++)
|
||||||
{
|
|
||||||
temp[i] = depunc[i] << 1;
|
temp[i] = depunc[i] << 1;
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
temp[i+72] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNConvolution_start();
|
CNXDNConvolution_start();
|
||||||
for (int i = 0U; i < 40U; i++)
|
for (int i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
s0 = temp[(2*i)];
|
s0 = temp[(2*i)];
|
||||||
s1 = temp[(2*i)+1];
|
s1 = temp[(2*i)+1];
|
||||||
@@ -197,7 +191,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
|||||||
}
|
}
|
||||||
|
|
||||||
//stored as 5 bytes, will need to convert to trellis_buf after running
|
//stored as 5 bytes, will need to convert to trellis_buf after running
|
||||||
CNXDNConvolution_chainback(m_data, 36U);
|
CNXDNConvolution_chainback(m_data, 32);
|
||||||
|
|
||||||
for(int i = 0; i < 4; i++)
|
for(int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
@@ -211,11 +205,11 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
|||||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
crc = crc6(trellis_buf, 32);
|
crc = crc6(trellis_buf, 26); //32
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
check = check << 1;
|
check = check << 1;
|
||||||
check = check + trellis_buf[i+26];
|
check = check | trellis_buf[i+26];
|
||||||
}
|
}
|
||||||
|
|
||||||
//FIRST! If part of a non_superframe, and CRC is good, send directly to NXDN_Elements_Content_decode
|
//FIRST! If part of a non_superframe, and CRC is good, send directly to NXDN_Elements_Content_decode
|
||||||
@@ -231,17 +225,42 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
|||||||
nsf_sacch[i] = trellis_buf[i+8];
|
nsf_sacch[i] = trellis_buf[i+8];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch);
|
if (crc == check)
|
||||||
|
{
|
||||||
|
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
||||||
|
state->nxdn_last_ran = ran;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "PF 1/1");
|
||||||
|
|
||||||
|
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch);
|
||||||
|
// else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, nsf_sacch);
|
||||||
|
|
||||||
|
//I'm placing this here, my observation is that SACCH NSF
|
||||||
|
//is almost always, if not always, just IDLE
|
||||||
|
else fprintf (stderr, " IDLE");
|
||||||
|
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n SACCH NSF ");
|
fprintf (stderr, "\n SACCH NSF ");
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
fprintf (stderr, "[%02X]", m_data[i]);
|
||||||
}
|
}
|
||||||
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
|
||||||
|
if (crc != check)
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//reset the sacch field -- Github Issue #118
|
||||||
|
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));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +270,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
|||||||
//sf and ran together are denoted as SR in the manual (more confusing acronyms)
|
//sf and ran together are denoted as SR in the manual (more confusing acronyms)
|
||||||
//sf (structure field) and RAN will always exist in first 8 bits of each SACCH, then the next 18 bits are the fragment of the superframe
|
//sf (structure field) and RAN will always exist in first 8 bits of each SACCH, then the next 18 bits are the fragment of the superframe
|
||||||
sf = (trellis_buf[0] << 1) | trellis_buf[1];
|
sf = (trellis_buf[0] << 1) | trellis_buf[1];
|
||||||
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
|
||||||
if (sf == 3) part_of_frame = 0;
|
if (sf == 3) part_of_frame = 0;
|
||||||
else if (sf == 2) part_of_frame = 1;
|
else if (sf == 2) part_of_frame = 1;
|
||||||
else if (sf == 1) part_of_frame = 2;
|
else if (sf == 1) part_of_frame = 2;
|
||||||
@@ -262,12 +281,22 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
|||||||
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
||||||
else fprintf (stderr, " ");
|
else fprintf (stderr, " ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
fprintf (stderr, "PF %d/4", part_of_frame+1);
|
||||||
|
|
||||||
|
if (crc != check)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
//reset scrambler seed to key value on new superframe
|
//reset scrambler seed to key value on new superframe
|
||||||
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
|
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
|
||||||
|
|
||||||
if (crc == 0)
|
if (crc == check)
|
||||||
{
|
{
|
||||||
|
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
||||||
state->nxdn_ran = state->nxdn_last_ran = ran;
|
state->nxdn_ran = state->nxdn_last_ran = ran;
|
||||||
state->nxdn_sf = sf;
|
state->nxdn_sf = sf;
|
||||||
state->nxdn_part_of_frame = part_of_frame;
|
state->nxdn_part_of_frame = part_of_frame;
|
||||||
@@ -290,28 +319,32 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
|||||||
NXDN_SACCH_Full_decode (opts, state);
|
NXDN_SACCH_Full_decode (opts, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->payload == 1)
|
// if (opts->payload == 1)
|
||||||
{
|
// {
|
||||||
fprintf (stderr, "\n");
|
// fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " SACCH SF Segment #%d ", part_of_frame+1);
|
// fprintf (stderr, " SACCH SF Segment #%d ", part_of_frame+1);
|
||||||
for (int i = 0; i < 5; i++)
|
// for (int i = 0; i < 4; i++)
|
||||||
{
|
// {
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
// fprintf (stderr, "[%02X]", m_data[i]);
|
||||||
}
|
// }
|
||||||
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
// // if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348])
|
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type)
|
||||||
{
|
{
|
||||||
|
uint8_t deperm[348]; //348
|
||||||
uint8_t deperm[348];
|
uint8_t depunc[406]; //406
|
||||||
uint8_t depunc[406];
|
uint8_t trellis_buf[208]; //199
|
||||||
uint8_t trellis_buf[199];
|
|
||||||
int id = 0;
|
int id = 0;
|
||||||
uint16_t crc = 0;
|
uint16_t crc = 0;
|
||||||
|
uint16_t check = 0;
|
||||||
|
|
||||||
|
memset (deperm, 0, sizeof(deperm));
|
||||||
|
memset (depunc, 0, sizeof(depunc));
|
||||||
|
|
||||||
for (int i=0; i<348; i++) {
|
for (int i=0; i<348; i++) {
|
||||||
deperm[PERM_12_29[i]] = bits[i];
|
deperm[PERM_12_29[i]] = bits[i];
|
||||||
@@ -334,26 +367,19 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
|
|||||||
}
|
}
|
||||||
|
|
||||||
//switch to the convolutional decoder
|
//switch to the convolutional decoder
|
||||||
uint8_t temp[220];
|
uint8_t temp[406];
|
||||||
uint8_t s0;
|
uint8_t s0;
|
||||||
uint8_t s1;
|
uint8_t s1;
|
||||||
uint8_t m_data[26];
|
uint8_t m_data[26]; //26
|
||||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
memset (temp, 0, sizeof (temp));
|
memset (temp, 0, sizeof (temp));
|
||||||
memset (m_data, 0, sizeof (m_data));
|
memset (m_data, 0, sizeof (m_data));
|
||||||
|
|
||||||
for (int i = 0; i < 406; i++)
|
for (int i = 0; i < 406; i++)
|
||||||
{
|
|
||||||
temp[i] = depunc[i] << 1;
|
temp[i] = depunc[i] << 1;
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
temp[i+203] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNConvolution_start();
|
CNXDNConvolution_start();
|
||||||
for (int i = 0U; i < 207U; i++)
|
for (int i = 0; i < 203; i++)
|
||||||
{
|
{
|
||||||
s0 = temp[(2*i)];
|
s0 = temp[(2*i)];
|
||||||
s1 = temp[(2*i)+1];
|
s1 = temp[(2*i)+1];
|
||||||
@@ -362,7 +388,7 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
|
|||||||
}
|
}
|
||||||
|
|
||||||
//numerals seem okay now
|
//numerals seem okay now
|
||||||
CNXDNConvolution_chainback(m_data, 203U);
|
CNXDNConvolution_chainback(m_data, 199);
|
||||||
|
|
||||||
for(int i = 0; i < 26; i++)
|
for(int i = 0; i < 26; i++)
|
||||||
{
|
{
|
||||||
@@ -377,47 +403,87 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
|
|||||||
}
|
}
|
||||||
|
|
||||||
crc = crc15(trellis_buf, 199);
|
crc = crc15(trellis_buf, 199);
|
||||||
|
for (int i = 0; i < 15; i++)
|
||||||
|
{
|
||||||
|
check = check << 1;
|
||||||
|
check = check | trellis_buf[i+184];
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t f2u_message_buffer[199];
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
if (type == 0) fprintf (stderr, " UDCH");
|
||||||
|
if (type == 1) fprintf (stderr, " FACCH2");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
uint8_t f2u_message_buffer[400]; //199
|
||||||
memset (f2u_message_buffer, 0, sizeof(f2u_message_buffer));
|
memset (f2u_message_buffer, 0, sizeof(f2u_message_buffer));
|
||||||
|
|
||||||
//just going to leave this all here in case its needed, like in cac,
|
//just going to leave this all here in case its needed, like in cac,
|
||||||
//don't have any samples with f2 or udch data in it
|
//don't have any samples with f2 or udch data in it
|
||||||
for (int i = 0; i < 199-8; i++)
|
for (int i = 0; i < 199-8-15; i++)
|
||||||
{
|
{
|
||||||
f2u_message_buffer[i] = trellis_buf[i+8];
|
f2u_message_buffer[i] = trellis_buf[i+8];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crc == 0)
|
if (crc == check)
|
||||||
{
|
{
|
||||||
fprintf (stderr, " F2/U ");
|
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
|
||||||
//NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
if (type == 0) ; //need handling for user data (text messages and AVL)
|
||||||
NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
|
|
||||||
}
|
}
|
||||||
|
// else if (opts->aggressive_framesync == 0)
|
||||||
|
// {
|
||||||
|
// if (type == 1) NXDN_Elements_Content_decode(opts, state, 0, f2u_message_buffer);
|
||||||
|
// if (type == 0) {} //need handling for user data (text messages and AVL)
|
||||||
|
// }
|
||||||
|
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " F2/U Payload\n ");
|
if (type == 0) fprintf (stderr, " UDCH");
|
||||||
|
if (type == 1) fprintf (stderr, " FACCH2");
|
||||||
|
fprintf (stderr, " Payload\n ");
|
||||||
for (int i = 0; i < 26; i++)
|
for (int i = 0; i < 26; i++)
|
||||||
{
|
{
|
||||||
|
if (i == 13) fprintf (stderr, "\n ");
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
fprintf (stderr, "[%02X]", m_data[i]);
|
||||||
if (i == 12) fprintf (stderr, "\n ");
|
|
||||||
}
|
}
|
||||||
if (crc != 0) fprintf (stderr, " CRC ERR ");
|
|
||||||
|
if (crc != check)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (crc != check) fprintf (stderr, " CRC ERR ");
|
||||||
|
|
||||||
|
if (type == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n UDCH Data: ASCII - " );
|
||||||
|
for (int i = 0; i < 24; i++) //remove crc portion
|
||||||
|
{
|
||||||
|
if (m_data[i] <= 0x7E && m_data[i] >=0x20)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%c", m_data[i]);
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
||||||
{
|
{
|
||||||
|
uint8_t deperm[300]; //300
|
||||||
uint8_t deperm[300];
|
uint8_t depunc[350]; //350
|
||||||
uint8_t depunc[350];
|
uint8_t trellis_buf[176]; //176
|
||||||
uint8_t trellis_buf[171];
|
|
||||||
int id = 0;
|
int id = 0;
|
||||||
|
int ran = 0;
|
||||||
uint16_t crc = 0;
|
uint16_t crc = 0;
|
||||||
|
|
||||||
|
memset (deperm, 0, sizeof(deperm));
|
||||||
|
memset (depunc, 0, sizeof(depunc));
|
||||||
|
|
||||||
for (int i=0; i<300; i++) {
|
for (int i=0; i<300; i++) {
|
||||||
deperm[PERM_12_25[i]] = bits[i];
|
deperm[PERM_12_25[i]] = bits[i];
|
||||||
}
|
}
|
||||||
@@ -439,26 +505,19 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
|||||||
}
|
}
|
||||||
|
|
||||||
//switch to the convolutional decoder
|
//switch to the convolutional decoder
|
||||||
uint8_t temp[360];
|
uint8_t temp[179];
|
||||||
uint8_t s0;
|
uint8_t s0;
|
||||||
uint8_t s1;
|
uint8_t s1;
|
||||||
uint8_t m_data[26];
|
uint8_t m_data[22]; //26
|
||||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
memset (temp, 0, sizeof (temp));
|
memset (temp, 0, sizeof (temp));
|
||||||
memset (m_data, 0, sizeof (m_data));
|
memset (m_data, 0, sizeof (m_data));
|
||||||
|
|
||||||
for (int i = 0; i < 350; i++)
|
for (int i = 0; i < 350; i++)
|
||||||
{
|
|
||||||
temp[i] = depunc[i] << 1;
|
temp[i] = depunc[i] << 1;
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
temp[i+350] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNConvolution_start();
|
CNXDNConvolution_start();
|
||||||
for (int i = 0U; i < 179U; i++)
|
for (int i = 0; i < 175; i++) //179
|
||||||
{
|
{
|
||||||
s0 = temp[(2*i)];
|
s0 = temp[(2*i)];
|
||||||
s1 = temp[(2*i)+1];
|
s1 = temp[(2*i)+1];
|
||||||
@@ -466,7 +525,7 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
|||||||
CNXDNConvolution_decode(s0, s1);
|
CNXDNConvolution_decode(s0, s1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CNXDNConvolution_chainback(m_data, 175U);
|
CNXDNConvolution_chainback(m_data, 171); //175
|
||||||
|
|
||||||
for(int i = 0; i < 22; i++)
|
for(int i = 0; i < 22; i++)
|
||||||
{
|
{
|
||||||
@@ -482,30 +541,38 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
|||||||
|
|
||||||
crc = crc16cac(trellis_buf, 171);
|
crc = crc16cac(trellis_buf, 171);
|
||||||
|
|
||||||
//message type will probably be neccesary beforehand on single/dual meessage runs
|
uint8_t cac_message_buffer[147]; //171
|
||||||
//run message, check len, load into a seperate buffer, send to element, do same with other piece?
|
|
||||||
uint8_t MessageType = 0;
|
|
||||||
for (int i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
MessageType = MessageType << 1;
|
|
||||||
MessageType = MessageType + trellis_buf[i+2]; //double check this on CAC
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t cac_message_buffer[171];
|
|
||||||
memset (cac_message_buffer, 0, sizeof(cac_message_buffer));
|
memset (cac_message_buffer, 0, sizeof(cac_message_buffer));
|
||||||
|
|
||||||
//shift the cac_message into the appropriate byte arrangement for element_decoder
|
//shift the cac_message into the appropriate byte arrangement for element_decoder -- skip SR field and ending crc
|
||||||
for (int i = 0; i < 160; i++) //in future, we can use this to send multiple messages to decoder, if present
|
for (int i = 0; i < 147; i++)
|
||||||
{
|
{
|
||||||
cac_message_buffer[i] = trellis_buf[i+8];
|
cac_message_buffer[i] = trellis_buf[i+8];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
||||||
|
else fprintf (stderr, " ");
|
||||||
|
|
||||||
if (crc == 0)
|
if (crc == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, " CAC ");
|
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
||||||
NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
|
state->nxdn_last_ran = ran;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " CAC");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
if (crc != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
|
||||||
|
// else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, cac_message_buffer);
|
||||||
|
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
@@ -515,32 +582,312 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
|||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
fprintf (stderr, "[%02X]", m_data[i]);
|
||||||
if (i == 10) fprintf (stderr, "\n ");
|
if (i == 10) fprintf (stderr, "\n ");
|
||||||
}
|
}
|
||||||
if (crc != 0) fprintf (stderr, " CRC ERR ");
|
// if (crc != 0) fprintf (stderr, " CRC ERR ");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Type-D "IDAS"
|
||||||
|
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " SCCH");
|
||||||
|
|
||||||
|
//see about initializing these variables
|
||||||
|
uint8_t deperm[60]; //60
|
||||||
|
uint8_t depunc[72]; //72
|
||||||
|
uint8_t trellis_buf[32]; //32
|
||||||
|
|
||||||
|
memset (deperm, 0, sizeof(deperm));
|
||||||
|
memset (depunc, 0, sizeof(depunc));
|
||||||
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
|
|
||||||
|
int o = 0;
|
||||||
|
uint8_t crc = 0; //value computed by crc7 on payload
|
||||||
|
uint8_t check = 0; //value pulled from last 7 bits
|
||||||
|
int sf = 0;
|
||||||
|
int part_of_frame = 0;
|
||||||
|
|
||||||
|
for (int i=0; i<60; i++)
|
||||||
|
deperm[PERM_12_5[i]] = bits[i];
|
||||||
|
for (int p=0; p<60; p+= 10) {
|
||||||
|
depunc[o++] = deperm[p+0];
|
||||||
|
depunc[o++] = deperm[p+1];
|
||||||
|
depunc[o++] = deperm[p+2];
|
||||||
|
depunc[o++] = deperm[p+3];
|
||||||
|
depunc[o++] = deperm[p+4];
|
||||||
|
depunc[o++] = 0;
|
||||||
|
depunc[o++] = deperm[p+5];
|
||||||
|
depunc[o++] = deperm[p+6];
|
||||||
|
depunc[o++] = deperm[p+7];
|
||||||
|
depunc[o++] = deperm[p+8];
|
||||||
|
depunc[o++] = deperm[p+9];
|
||||||
|
depunc[o++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//switch to the convolutional decoder
|
||||||
|
uint8_t temp[72];
|
||||||
|
uint8_t s0;
|
||||||
|
uint8_t s1;
|
||||||
|
uint8_t m_data[5]; //5
|
||||||
|
|
||||||
|
memset (temp, 0, sizeof (temp));
|
||||||
|
memset (m_data, 0, sizeof (m_data));
|
||||||
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
|
|
||||||
|
for (int i = 0; i < 72; i++)
|
||||||
|
temp[i] = depunc[i] << 1;
|
||||||
|
|
||||||
|
CNXDNConvolution_start();
|
||||||
|
for (int i = 0; i < 36; i++)
|
||||||
|
{
|
||||||
|
s0 = temp[(2*i)];
|
||||||
|
s1 = temp[(2*i)+1];
|
||||||
|
|
||||||
|
CNXDNConvolution_decode(s0, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CNXDNConvolution_chainback(m_data, 32);
|
||||||
|
|
||||||
|
for(int i = 0; i < 4; 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
crc = crc7_scch(trellis_buf, 25);
|
||||||
|
for (int i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
check = check << 1;
|
||||||
|
check = check | trellis_buf[i+25];
|
||||||
|
}
|
||||||
|
|
||||||
|
//check the sf early for scrambler reset, if required
|
||||||
|
sf = (trellis_buf[0] << 1) | trellis_buf[1];
|
||||||
|
if (sf == 3) part_of_frame = 0;
|
||||||
|
else if (sf == 2) part_of_frame = 1;
|
||||||
|
else if (sf == 1) part_of_frame = 2;
|
||||||
|
else if (sf == 0) part_of_frame = 3;
|
||||||
|
else part_of_frame = 0;
|
||||||
|
|
||||||
|
//reset scrambler seed to key value on new superframe
|
||||||
|
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
4.3.2. Mapping to Functional Channel
|
||||||
|
When Subscriber Unit performs voice communication in RTCH2, SCCH is allocated as
|
||||||
|
described below.
|
||||||
|
|
||||||
|
SCCH has super Frame Structure with 4 frame unit during communication, and has non
|
||||||
|
super Frame Structure with single frame unit at the start time and end time of sending.
|
||||||
|
Allocation of the other channel is not specified particularly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//English Translation: Superframe when voice, Non Superframe when not voice
|
||||||
|
|
||||||
|
//What I've found is that by not using the superframe structure, and decoding each 'unit'
|
||||||
|
//individually instead, we can get more expedient decoding on elements without having to
|
||||||
|
//sacrifice an entire superframe for one bad CRC, also each element cleanly divides into a
|
||||||
|
//single 'unit' except for enc parms IV, which can be stored seperately if needed
|
||||||
|
|
||||||
|
//NOTE: scch has its own message format, and thus, doesn't go to content element decoding
|
||||||
|
//like everything else does
|
||||||
|
|
||||||
|
if (crc == check) NXDN_decode_scch (opts, state, trellis_buf, direction);
|
||||||
|
// else if (opts->aggressive_framesync == 0) NXDN_decode_scch (opts, state, trellis_buf, direction);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n SCCH Payload ");
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", m_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc != check)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type)
|
||||||
|
{
|
||||||
|
uint8_t deperm[144]; //144
|
||||||
|
uint8_t depunc[192]; //192
|
||||||
|
uint8_t trellis_buf[96]; //96
|
||||||
|
uint8_t f3_udch2[288]; //completed bitstream without crc and tailing bits attached
|
||||||
|
uint8_t f3_udch2_bytes[36]; //completed bytes - with crc and tail
|
||||||
|
uint16_t crc[2]; //crc calculated by function
|
||||||
|
uint16_t check[2]; //crc from payload for comparison
|
||||||
|
int out;
|
||||||
|
|
||||||
|
//switch to the convolutional decoder
|
||||||
|
uint8_t temp[192];
|
||||||
|
uint8_t s0;
|
||||||
|
uint8_t s1;
|
||||||
|
uint8_t m_data[12]; //13
|
||||||
|
memset (temp, 0, sizeof(temp));
|
||||||
|
memset (m_data, 0, sizeof(m_data));
|
||||||
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||||
|
memset (deperm, 0, sizeof(deperm));
|
||||||
|
memset (depunc, 0, sizeof(depunc));
|
||||||
|
memset (crc, 0, sizeof(crc));
|
||||||
|
memset (check, 0, sizeof(check));
|
||||||
|
memset (f3_udch2, 0, sizeof(f3_udch2));
|
||||||
|
memset (f3_udch2_bytes, 0, sizeof(f3_udch2_bytes));
|
||||||
|
|
||||||
|
for (int j=0; j<2; j++)
|
||||||
|
{
|
||||||
|
for (int i=0; i<144; i++)
|
||||||
|
deperm[PERM_16_9[i]] = bits[i+(j*144)];
|
||||||
|
out = 0;
|
||||||
|
for (int i=0; i<144; i+=3) {
|
||||||
|
depunc[out++] = deperm[i+0];
|
||||||
|
depunc[out++] = 0;
|
||||||
|
depunc[out++] = deperm[i+1];
|
||||||
|
depunc[out++] = deperm[i+2];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 192; i++)
|
||||||
|
temp[i] = depunc[i] << 1;
|
||||||
|
|
||||||
|
CNXDNConvolution_start();
|
||||||
|
for (int i = 0; i < 96; i++)
|
||||||
|
{
|
||||||
|
s0 = temp[(2*i)];
|
||||||
|
s1 = temp[(2*i)+1];
|
||||||
|
|
||||||
|
CNXDNConvolution_decode(s0, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CNXDNConvolution_chainback(m_data, 92);
|
||||||
|
|
||||||
|
for(int 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
crc[j] = crc12f (trellis_buf, 84); //84
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
check[j] = check[j] << 1;
|
||||||
|
check[j] = check[j] | trellis_buf[84+i]; //84
|
||||||
|
|
||||||
|
}
|
||||||
|
//transfer to storage sans crc and tail bits
|
||||||
|
for (int i = 0; i < 80; i++) f3_udch2[i+(j*80)] = trellis_buf[i];
|
||||||
|
for (int i = 0; i < 12; i++) f3_udch2_bytes[i+(j*12)] = m_data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
if (type == 0) fprintf (stderr, " UDCH2");
|
||||||
|
if (type == 1) fprintf (stderr, " FACCH3");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
|
||||||
|
if (crc[0] == check[0] && crc[1] == check[1])
|
||||||
|
{
|
||||||
|
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
||||||
|
if (type == 0) ; //need handling for user data (text messages and AVL)
|
||||||
|
}
|
||||||
|
// else if (opts->aggressive_framesync == 0)
|
||||||
|
// {
|
||||||
|
// if (type == 1) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
|
||||||
|
// if (type == 0) {} //need handling for user data (text messages and AVL)
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
if (type == 0) fprintf (stderr, " UDCH2");
|
||||||
|
if (type == 1) fprintf (stderr, " FACCH3");
|
||||||
|
fprintf (stderr, " Payload \n ");
|
||||||
|
for (int i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
if (i == 12) fprintf (stderr, "\n ");
|
||||||
|
fprintf (stderr, "[%02X]", f3_udch2_bytes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (crc[0] != check[0] || crc[1] != check[1]) && opts->payload == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
// fprintf (stderr, " - %03X %03X", check[0], crc[0]);
|
||||||
|
// fprintf (stderr, " - %03X %03X", check[1], crc[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n UDCH2 Data: ASCII - " );
|
||||||
|
for (int i = 0; i < 22; i++) //all but last crc portion
|
||||||
|
{
|
||||||
|
if (i == 12) i = 14; //skip first crc portion
|
||||||
|
if (f3_udch2_bytes[i] <= 0x7E && f3_udch2_bytes[i] >=0x20)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%c", f3_udch2_bytes[i]);
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//NOTE: Most Req/Resp (request and respone) share same message type but differ depending on channel type
|
||||||
|
//RTCH Outbound will take precedent when differences may occur (except CALL_ASSGN)
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
if (MessageType == 0x10) fprintf(stderr, " IDLE");
|
if (MessageType == 0x10) fprintf(stderr, " IDLE");
|
||||||
else if (MessageType == 0x11) fprintf(stderr, " DISC"); //disconnect
|
else if (MessageType == 0x00) fprintf(stderr, " CALL_RESP");
|
||||||
else if (MessageType == 0x01) fprintf(stderr, " VCALL");
|
else if (MessageType == 0x01) fprintf(stderr, " VCALL");
|
||||||
else if (MessageType == 0x03) fprintf(stderr, " VCALL IV");
|
else if (MessageType == 0x02) fprintf(stderr, " VCALL_REC_REQ");
|
||||||
|
else if (MessageType == 0x03) fprintf(stderr, " VCALL_IV");
|
||||||
else if (MessageType == 0x07) fprintf(stderr, " TX_REL_EX");
|
|
||||||
else if (MessageType == 0x08) fprintf(stderr, " TX_REL");
|
|
||||||
|
|
||||||
else if (MessageType == 0x04) fprintf(stderr, " VCALL_ASSGN");
|
else if (MessageType == 0x04) fprintf(stderr, " VCALL_ASSGN");
|
||||||
else if (MessageType == 0x05) fprintf(stderr, " VCALL_ASSGN_DUP");
|
else if (MessageType == 0x05) fprintf(stderr, " VCALL_ASSGN_DUP");
|
||||||
|
else if (MessageType == 0x06) fprintf(stderr, " CALL_CONN_RESP");
|
||||||
|
else if (MessageType == 0x07) fprintf(stderr, " TX_REL_EX");
|
||||||
|
else if (MessageType == 0x08) fprintf(stderr, " TX_REL");
|
||||||
|
else if (MessageType == 0x09) fprintf(stderr, " DCALL_HEADER");
|
||||||
|
else if (MessageType == 0x0A) fprintf(stderr, " DCALL_REC_REQ");
|
||||||
|
else if (MessageType == 0x0B) fprintf(stderr, " DCALL_UDATA");
|
||||||
|
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
|
||||||
|
else if (MessageType == 0x0D) fprintf(stderr, " DCALL_ASSGN_DUP");
|
||||||
else if (MessageType == 0x0E) fprintf(stderr, " DCALL_ASSGN");
|
else if (MessageType == 0x0E) fprintf(stderr, " DCALL_ASSGN");
|
||||||
|
else if (MessageType == 0x11) fprintf(stderr, " DISC");
|
||||||
|
else if (MessageType == 0x17) fprintf(stderr, " DST_ID_INFO");
|
||||||
else if (MessageType == 0x18) fprintf(stderr, " SITE_INFO");
|
else if (MessageType == 0x18) fprintf(stderr, " SITE_INFO");
|
||||||
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
|
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
|
||||||
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO");
|
|
||||||
else if (MessageType == 0x1A) fprintf(stderr, " CCH_INFO");
|
else if (MessageType == 0x1A) fprintf(stderr, " CCH_INFO");
|
||||||
else if (MessageType == 0x1B) fprintf(stderr, " ADJ_SITE_INFO");
|
else if (MessageType == 0x1B) fprintf(stderr, " ADJ_SITE_INFO");
|
||||||
|
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO");
|
||||||
else if (MessageType == 0x20) fprintf(stderr, " REG_RESP");
|
else if (MessageType == 0x20) fprintf(stderr, " REG_RESP");
|
||||||
else if (MessageType == 0x22) fprintf(stderr, " REG_C_RESP");
|
else if (MessageType == 0x22) fprintf(stderr, " REG_C_RESP");
|
||||||
else if (MessageType == 0x24) fprintf(stderr, " GRP_REG_RESP");
|
else if (MessageType == 0x24) fprintf(stderr, " GRP_REG_RESP");
|
||||||
@@ -549,24 +896,23 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
|||||||
else if (MessageType == 0x38) fprintf(stderr, " SDCALL_REQ_HEADER");
|
else if (MessageType == 0x38) fprintf(stderr, " SDCALL_REQ_HEADER");
|
||||||
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
|
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
|
||||||
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
|
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
|
||||||
|
|
||||||
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
|
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
|
||||||
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
|
|
||||||
else fprintf(stderr, " Unknown M-%02X", MessageType);
|
else fprintf(stderr, " Unknown M-%02X", MessageType);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
//zero out stale values so they won't persist after a transmit release
|
//Zero out stale values on DISC or TX_REL only (IDLE messaages occur often on NXDN96 VCH, and randomly on Type-C FACCH1 steals for some reason)
|
||||||
//disable if random messages wipe out the alias
|
if (MessageType == 0x08 || MessageType == 0x11)
|
||||||
if (MessageType == 0x08 || MessageType == 0x10) //idle, tx_rel
|
|
||||||
{
|
{
|
||||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||||
state->nxdn_last_rid = 0;
|
state->nxdn_last_rid = 0;
|
||||||
state->nxdn_last_tg = 0;
|
state->nxdn_last_tg = 0;
|
||||||
state->nxdn_cipher_type = 0;
|
state->nxdn_cipher_type = 0; //force will reactivate it if needed during voice tx
|
||||||
|
if (state->keyloader == 1) state->R = 0;
|
||||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||||
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||||
sprintf (state->nxdn_call_type, "%s", "");
|
sprintf (state->nxdn_call_type, "%s", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//voice descrambler
|
//voice descrambler
|
||||||
@@ -669,13 +1015,32 @@ static uint16_t crc15(const uint8_t buf[], int len)
|
|||||||
|
|
||||||
static uint16_t crc16cac(const uint8_t buf[], int len)
|
static uint16_t crc16cac(const uint8_t buf[], int len)
|
||||||
{
|
{
|
||||||
int crc = 0xc3ee;
|
uint32_t crc = 0xc3ee; //not sure why this though
|
||||||
int poly = (1<<12) + (1<<5) + 1;
|
uint32_t poly = (1<<12) + (1<<5) + 1; //poly is fine
|
||||||
for (int i=0;i<len;i++) {
|
for (int i=0;i<len;i++)
|
||||||
crc = ((crc << 1) | buf[i]) & 0x1ffff;
|
{
|
||||||
if(crc & 0x10000)
|
crc = ((crc << 1) | buf[i]) & 0x1ffff;
|
||||||
crc = (crc & 0xffff) ^ poly;
|
if(crc & 0x10000) crc = (crc & 0xffff) ^ poly;
|
||||||
}
|
}
|
||||||
crc = crc ^ 0xffff;
|
crc = crc ^ 0xffff;
|
||||||
return crc & 0xffff;
|
return crc & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t crc7_scch(uint8_t bits[], int len)
|
||||||
|
{
|
||||||
|
uint8_t s[7];
|
||||||
|
uint8_t a;
|
||||||
|
for (int i=0;i<7;i++)
|
||||||
|
s[i] = 1;
|
||||||
|
for (int i=0;i<len;i++) {
|
||||||
|
a = bits[i] ^ s[0];
|
||||||
|
s[0] = s[1];
|
||||||
|
s[1] = s[2];
|
||||||
|
s[2] = s[3];
|
||||||
|
s[3] = a ^ s[4];
|
||||||
|
s[4] = s[5];
|
||||||
|
s[5] = s[6];
|
||||||
|
s[6] = a;
|
||||||
|
}
|
||||||
|
return load_i(s, 7);
|
||||||
}
|
}
|
||||||
1304
src/nxdn_element.c
1304
src/nxdn_element.c
File diff suppressed because it is too large
Load Diff
382
src/nxdn_frame.c
382
src/nxdn_frame.c
@@ -32,53 +32,74 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
// length is implicitly 192, with frame sync in first 10 dibits
|
// length is implicitly 192, with frame sync in first 10 dibits
|
||||||
uint8_t dbuf[182];
|
uint8_t dbuf[182];
|
||||||
uint8_t lich;
|
uint8_t lich;
|
||||||
int answer_len=0;
|
int answer_len = 0;
|
||||||
uint8_t answer[32];
|
uint8_t answer[32];
|
||||||
uint8_t sacch_answer[32];
|
uint8_t sacch_answer[32];
|
||||||
int lich_parity_received;
|
int lich_parity_received;
|
||||||
int lich_parity_computed;
|
int lich_parity_computed;
|
||||||
int voice=0;
|
int voice = 0;
|
||||||
int facch=0;
|
int facch = 0;
|
||||||
int facch2=0;
|
int facch2 = 0;
|
||||||
int sacch=0;
|
int udch = 0;
|
||||||
int cac=0;
|
int sacch = 0;
|
||||||
|
int cac = 0;
|
||||||
int sr_structure;
|
int sr_structure;
|
||||||
int sr_ran;
|
int sr_ran;
|
||||||
|
|
||||||
uint8_t lich_dibits[8] = {0};
|
//new, and even more confusing NXDN Type-D / "IDAS" acronyms
|
||||||
uint8_t sacch_bits[60] = {0};
|
int idas = 0;
|
||||||
uint8_t facch_bits_a[144] = {0};
|
int scch = 0;
|
||||||
uint8_t facch_bits_b[144] = {0};
|
int facch3 = 0;
|
||||||
uint8_t cac_bits[300] = {0};
|
int udch2 = 0;
|
||||||
uint8_t facch2_bits[348] = {0}; //facch2 or udch, same amount of bits
|
|
||||||
|
|
||||||
//nxdn bit buffer, for easy assignment handling
|
//new breakdown of lich codes
|
||||||
int nxdn_bit_buffer[364] = {0};
|
uint8_t lich_rf = 0; //RF Channel Type
|
||||||
int nxdn_dibit_buffer[182] = {0};
|
uint8_t lich_fc = 0; //Functional Channel Type
|
||||||
|
uint8_t lich_op = 0; //Options
|
||||||
|
uint8_t direction; //inbound or outbound direction
|
||||||
|
|
||||||
|
uint8_t lich_dibits[8];
|
||||||
|
uint8_t sacch_bits[60];
|
||||||
|
uint8_t facch_bits_a[144];
|
||||||
|
uint8_t facch_bits_b[144];
|
||||||
|
uint8_t cac_bits[300];
|
||||||
|
uint8_t facch2_bits[348]; //facch2 or udch, same amount of bits
|
||||||
|
uint8_t facch3_bits[288]; //facch3 or udch2, same amount of bits
|
||||||
|
|
||||||
|
//nxdn bit buffer, for easy assignment handling
|
||||||
|
int nxdn_bit_buffer[364];
|
||||||
|
int nxdn_dibit_buffer[182];
|
||||||
|
|
||||||
|
//init all arrays
|
||||||
memset (dbuf, 0, sizeof(dbuf));
|
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));
|
||||||
|
memset (facch_bits_a, 0, sizeof(facch_bits_a));
|
||||||
|
memset (cac_bits, 0, sizeof(cac_bits));
|
||||||
|
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
|
//collect lich bits first, if they are good, then we can collect the rest of them
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
||||||
{
|
|
||||||
lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
nxdn_descramble (lich_dibits, 8);
|
nxdn_descramble (lich_dibits, 8);
|
||||||
|
|
||||||
lich = 0;
|
lich = 0;
|
||||||
for (int i=0; i<8; i++)
|
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
|
||||||
{
|
|
||||||
lich |= (lich_dibits[i] >> 1) << (7-i);
|
|
||||||
}
|
|
||||||
|
|
||||||
lich_parity_received = lich & 1;
|
lich_parity_received = lich & 1;
|
||||||
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1;
|
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1;
|
||||||
lich = lich >> 1;
|
lich = lich >> 1;
|
||||||
if (lich_parity_received != lich_parity_computed)
|
if (lich_parity_received != lich_parity_computed)
|
||||||
{
|
{
|
||||||
//fprintf(stderr, "NXDN lich parity error, ignoring frame \n");
|
if (opts->payload == 1) fprintf(stderr, " Lich Parity Error %02X\n", lich);
|
||||||
return;
|
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||||
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
voice = 0;
|
voice = 0;
|
||||||
@@ -86,6 +107,16 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
facch2 = 0;
|
facch2 = 0;
|
||||||
sacch = 0;
|
sacch = 0;
|
||||||
cac = 0;
|
cac = 0;
|
||||||
|
|
||||||
|
//test for inbound direction lich when trunking (false positive) and skip
|
||||||
|
//all inbound lich are even value (lsb is set to 0 for inbound direction)
|
||||||
|
if (lich % 2 == 0 && opts->p25_trunk == 1)
|
||||||
|
{
|
||||||
|
if (opts->payload == 1) fprintf(stderr, " Simplex/Inbound NXDN lich on trunking system - type 0x%02X\n", lich);
|
||||||
|
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
|
switch(lich) { //cases without breaks continue to flow downwards until they hit the break
|
||||||
case 0x01: // CAC types
|
case 0x01: // CAC types
|
||||||
case 0x05:
|
case 0x05:
|
||||||
@@ -93,21 +124,20 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
break;
|
break;
|
||||||
case 0x28: //facch2 types
|
case 0x28: //facch2 types
|
||||||
case 0x29:
|
case 0x29:
|
||||||
case 0x2e:
|
|
||||||
case 0x2f:
|
|
||||||
case 0x48:
|
case 0x48:
|
||||||
case 0x49:
|
case 0x49:
|
||||||
|
facch2 = 1;
|
||||||
|
break;
|
||||||
|
case 0x2e: //udch types
|
||||||
|
case 0x2f:
|
||||||
case 0x4e:
|
case 0x4e:
|
||||||
case 0x4f:
|
case 0x4f:
|
||||||
case 0x69:
|
udch = 1;
|
||||||
case 0x6f:
|
|
||||||
facch2 = 1;
|
|
||||||
break;
|
break;
|
||||||
case 0x32: //facch in 1, vch in 2
|
case 0x32: //facch in 1, vch in 2
|
||||||
case 0x33:
|
case 0x33:
|
||||||
case 0x52:
|
case 0x52:
|
||||||
case 0x53:
|
case 0x53:
|
||||||
case 0x73:
|
|
||||||
voice = 2;
|
voice = 2;
|
||||||
facch = 1;
|
facch = 1;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
@@ -115,17 +145,15 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
case 0x34: //vch in 1, facch in 2
|
case 0x34: //vch in 1, facch in 2
|
||||||
case 0x35:
|
case 0x35:
|
||||||
case 0x54:
|
case 0x54:
|
||||||
case 0x55:
|
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
|
||||||
case 0x75:
|
|
||||||
voice = 1;
|
voice = 1;
|
||||||
facch = 2;
|
facch = 2;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
break;
|
break;
|
||||||
case 0x36: //vch in both
|
case 0x36: //vch in both
|
||||||
case 0x37:
|
case 0x37:
|
||||||
case 0x56:
|
case 0x56:
|
||||||
case 0x57:
|
case 0x57:
|
||||||
case 0x77:
|
|
||||||
voice = 3;
|
voice = 3;
|
||||||
facch = 0;
|
facch = 0;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
@@ -138,8 +166,6 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
case 0x41:
|
case 0x41:
|
||||||
case 0x50:
|
case 0x50:
|
||||||
case 0x51:
|
case 0x51:
|
||||||
case 0x61:
|
|
||||||
case 0x71:
|
|
||||||
voice = 0;
|
voice = 0;
|
||||||
facch = 3;
|
facch = 3;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
@@ -148,10 +174,63 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
case 0x39:
|
case 0x39:
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
break;
|
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: //vch in 1, facch1 in 2 (facch 2 steal)
|
||||||
|
case 0x75:
|
||||||
|
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;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (opts->payload == 1) fprintf(stderr, " false sync or unsupported NXDN lich type 0x%02X\n", lich);
|
if (opts->payload == 1) fprintf(stderr, " false sync or unsupported NXDN lich type 0x%02X\n", lich);
|
||||||
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
|
//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, 0, sizeof(state->nxdn_sacch_frame_segment));
|
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));
|
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
|
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||||
voice = 0;
|
voice = 0;
|
||||||
@@ -159,17 +238,29 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
break;
|
break;
|
||||||
} // end of switch(lich)
|
} // end of switch(lich)
|
||||||
|
|
||||||
|
//enable these after good lich parity and known lich value
|
||||||
|
state->carrier = 1;
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
|
||||||
//printframesync after determining we have a good lich and it has something in it
|
//printframesync after determining we have a good lich and it has something in it
|
||||||
if (voice || facch || sacch || facch2 || cac)
|
if (idas)
|
||||||
|
{
|
||||||
|
if (opts->frame_nxdn48 == 1)
|
||||||
|
{
|
||||||
|
printFrameSync (opts, state, "IDAS D ", 0, "-");
|
||||||
|
}
|
||||||
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "L%02X - ", lich);
|
||||||
|
}
|
||||||
|
else if (voice || facch || sacch || facch2 || udch || cac)
|
||||||
{
|
{
|
||||||
// if (state->synctype == 28) fprintf (stderr, " +");
|
|
||||||
// else fprintf (stderr, " -");
|
|
||||||
if (opts->frame_nxdn48 == 1)
|
if (opts->frame_nxdn48 == 1)
|
||||||
{
|
{
|
||||||
printFrameSync (opts, state, "NXDN48 ", 0, "-");
|
printFrameSync (opts, state, "NXDN48 ", 0, "-");
|
||||||
}
|
}
|
||||||
else printFrameSync (opts, state, "NXDN96 ", 0, "-");
|
else printFrameSync (opts, state, "NXDN96 ", 0, "-");
|
||||||
if (opts->payload == 1) fprintf (stderr, "L%02X -", lich);
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "L%02X - ", lich);
|
||||||
}
|
}
|
||||||
|
|
||||||
//now that we have a good LICH, we can collect all of our dibits
|
//now that we have a good LICH, we can collect all of our dibits
|
||||||
@@ -188,7 +279,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
|
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//sacch
|
//sacch or scch bits
|
||||||
for (int i = 0; i < 60; i++)
|
for (int i = 0; i < 60; i++)
|
||||||
{
|
{
|
||||||
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
||||||
@@ -213,23 +304,122 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
facch2_bits[i] = nxdn_bit_buffer[i+16];
|
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
|
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
|
||||||
//sacch needs extra handling depending on superframe or non-superframe variety
|
//sacch needs extra handling depending on superframe or non-superframe variety
|
||||||
|
|
||||||
if (voice)
|
//Add advanced decoding of LICH (RF, FC, OPT, and Direction
|
||||||
|
lich_rf = (lich >> 5) & 0x3;
|
||||||
|
lich_fc = (lich >> 3) & 0x3;
|
||||||
|
lich_op = (lich >> 1) & 0x3;
|
||||||
|
if (lich % 2 == 0) direction = 0;
|
||||||
|
else direction = 1;
|
||||||
|
|
||||||
|
// RF Channel Type
|
||||||
|
if (lich_rf == 0) fprintf (stderr, "RCCH ");
|
||||||
|
else if (lich_rf == 1) fprintf (stderr, "RTCH ");
|
||||||
|
else if (lich_rf == 2) fprintf (stderr, "RDCH ");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (lich < 0x60) fprintf (stderr, "RTCH_C ");
|
||||||
|
else fprintf (stderr, "RTCH2 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functional Channel Type -- things start to get really convoluted here
|
||||||
|
// These will echo when handled, either with the decoded message type, or relevant crc err
|
||||||
|
// if (lich_rf == 0) //CAC Type
|
||||||
|
// {
|
||||||
|
// //Technically, we should be checking direction as well, but the fc never has split meaning on CAC
|
||||||
|
// if (lich_fc == 0) fprintf (stderr, "CAC ");
|
||||||
|
// else if (lich_fc == 1) fprintf (stderr, "Long CAC ");
|
||||||
|
// else if (lich_fc == 3) fprintf (stderr, "Short CAC ");
|
||||||
|
// else fprintf (stderr, "Reserved ");
|
||||||
|
// }
|
||||||
|
// else //USC Type
|
||||||
|
// {
|
||||||
|
// if (lich_fc == 0) fprintf (stderr, "NSF SACCH ");
|
||||||
|
// else if (lich_fc == 1) fprintf (stderr, "UDCH ");
|
||||||
|
// else if (lich_fc == 2) fprintf (stderr, "SF SACCH ");
|
||||||
|
// else if (lich_fc == 3) fprintf (stderr, "SF SACCH/IDLE ");
|
||||||
|
// }
|
||||||
|
|
||||||
|
#ifdef LIMAZULUTWEAKS
|
||||||
|
|
||||||
|
//LimaZulu specific tweak, load keys from frequency value, if avalable -- test before VCALL
|
||||||
|
//needs to be loaded here, if superframe data pair, then we need to run the LFSR on it as well
|
||||||
|
|
||||||
|
if (voice) //can this run TOO frequently?
|
||||||
|
{
|
||||||
|
long int freq = 0;
|
||||||
|
uint8_t hash_bits[24];
|
||||||
|
memset (hash_bits, 0, sizeof(hash_bits));
|
||||||
|
uint16_t limazulu = 0;
|
||||||
|
|
||||||
|
//if not available, then poll rigctl if its available
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
freq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||||
|
|
||||||
|
//if using rtl input, we can ask for the current frequency tuned
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
freq = (long int)opts->rtlsdr_center_freq;
|
||||||
|
|
||||||
|
// freq = 167831250; //hardset for testing
|
||||||
|
|
||||||
|
//since a frequency value will be larger than the 16-bit max, we need to hash it first
|
||||||
|
//the hash has to be run the same way as the import, so at a 24-bit depth, which hopefully
|
||||||
|
//will not lead to any duplicate key loads due to multiple CRC16 collisions on a larger value?
|
||||||
|
for (int i = 0; i < 24; i++)
|
||||||
|
hash_bits[i] = ((freq << i) & 0x800000) >> 23; //load into array for CRC16
|
||||||
|
|
||||||
|
if (freq) limazulu = ComputeCrcCCITT16d (hash_bits, 24);
|
||||||
|
limazulu = limazulu & 0xFFFF; //make sure no larger than 16-bits
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
if (freq) fprintf (stderr, "\n Freq: %ld - Freq Hash: %d", freq, limazulu);
|
||||||
|
if (state->rkey_array[limazulu] != 0) fprintf (stderr, " - Key Loaded: %lld", state->rkey_array[limazulu]);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
if (state->rkey_array[limazulu] != 0)
|
||||||
|
state->R = state->rkey_array[limazulu];
|
||||||
|
|
||||||
|
if (state->R != 0 && state->M == 1) state->nxdn_cipher_type = 0x1;
|
||||||
|
|
||||||
|
//add additional time to last_sync_time for LimaZulu to hold on current frequency
|
||||||
|
//a little longer without affecting normal scan time on trunk_hangtime variable
|
||||||
|
state->last_cc_sync_time = time(NULL) + 2; //ask him for an ideal wait timer
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //end LIMAZULUTWEAKS
|
||||||
|
|
||||||
|
if (opts->scanner_mode == 1)
|
||||||
|
state->last_cc_sync_time = time(NULL) + 2; //add a little extra hangtime between resuming scan
|
||||||
|
|
||||||
|
//Option/Steal Flags echoed in Voice, V+F, or Data
|
||||||
|
if (voice && !facch) //voice only, no facch steal
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf (stderr, "%s", KGRN);
|
||||||
fprintf (stderr, " Voice ");
|
fprintf (stderr, " Voice ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
else
|
else if (voice && facch) //voice with facch1 steal
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, " V%d+F%d ", 3 - facch, facch); //print which position on each
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
else //Covers FACCH1 in both, FACCH2, UDCH, UDCH2, CAC
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KCYN);
|
fprintf (stderr, "%s", KCYN);
|
||||||
fprintf (stderr, " Data ");
|
fprintf (stderr, " Data ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
//roll the voice scrambler LFSR here if key available to advance seed (usually just needed on NXDN96)
|
//roll the voice scrambler LFSR here if key available to advance seed (usually just needed on NXDN96)
|
||||||
if (state->nxdn_cipher_type == 0x1 && state->R != 0) //!voice, or voice == 0
|
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||||
{
|
{
|
||||||
char ambe_temp[49] = {0};
|
char ambe_temp[49] = {0};
|
||||||
char ambe_d[49] = {0};
|
char ambe_d[49] = {0};
|
||||||
@@ -239,16 +429,98 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->nxdn_sacch_non_superframe = (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) ? true : false;
|
if (voice && facch == 1) //facch steal 1 -- before voice
|
||||||
if (sacch)nxdn_deperm_sacch(opts, state, sacch_bits);
|
{
|
||||||
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
//force scrambler here, but with unspecified key (just use what's loaded)
|
||||||
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits);
|
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||||
|
//roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal
|
||||||
|
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||||
|
{
|
||||||
|
char ambe_temp[49] = {0};
|
||||||
|
char ambe_d[49] = {0};
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
LFSRN(ambe_temp, ambe_d, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) state->nxdn_sacch_non_superframe = TRUE;
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
|
||||||
|
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
||||||
|
|
||||||
|
//Seperated UDCH user data from facch2 data
|
||||||
|
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
|
||||||
|
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
|
||||||
|
|
||||||
|
//SHOULD be okay to run facch1's again on steal frames, will need testing
|
||||||
|
// if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
||||||
|
// if (facch & 2) nxdn_deperm_facch(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 & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
||||||
if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
|
if (facch & 2)
|
||||||
if (voice) nxdn_voice (opts, state, voice, dbuf);
|
{
|
||||||
|
if (memcmp (facch_bits_a, facch_bits_b, 144) != 0)
|
||||||
|
nxdn_deperm_facch(opts, state, facch_bits_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voice)
|
||||||
|
{
|
||||||
|
//restore MBE file open here
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, 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;
|
||||||
|
//process voice frame
|
||||||
|
nxdn_voice (opts, state, voice, dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//close MBE file if no voice and its open
|
||||||
|
if (!voice)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL)
|
||||||
|
{
|
||||||
|
if (opts->frame_nxdn96 == 1) //nxdn96 has pure voice and data frames mixed together, so we will need to do a time check first
|
||||||
|
{
|
||||||
|
if ( (time(NULL) - state->last_vc_sync_time) > 1) //test for optimal time, 1 sec should be okay
|
||||||
|
{
|
||||||
|
closeMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//may need to reconsider this, due to double FACCH1 steals on some Type-C (ASSGN_DUP, etc) and Conventional Systems (random IDLE FACCH1 steal for no reason)
|
||||||
|
if (opts->frame_nxdn48 == 1) closeMbeOutFile (opts, state); //okay to close right away if nxdn48, no data/voice frames mixing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voice && facch == 2) //facch steal 2 -- after voice 1
|
||||||
|
{
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
char ambe_temp[49] = {0};
|
||||||
|
char ambe_d[49] = {0};
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
LFSRN(ambe_temp, ambe_d, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1 && !voice) fprintf (stderr, "\n");
|
||||||
|
else if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
END: ; //do nothing
|
END: ; //do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* p25_frequency.c
|
* p25_frequency.c
|
||||||
* P25 Channel to Frequency Calculator
|
* P25 Channel to Frequency Calculator
|
||||||
*
|
*
|
||||||
* NXDN Channel to Frequency, Courtesy of IcomIcR20 and EricCottrell (base values)
|
* NXDN Channel to Frequency Calculator
|
||||||
*
|
*
|
||||||
* LWVMOBILE
|
* LWVMOBILE
|
||||||
* 2022-11 DSD-FME Florida Man Edition
|
* 2022-11 DSD-FME Florida Man Edition
|
||||||
@@ -59,16 +59,16 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums
|
|
||||||
long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t channel)
|
long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t channel)
|
||||||
{
|
{
|
||||||
long int freq;
|
long int freq;
|
||||||
long int base = 0;
|
long int base = 0;
|
||||||
|
long int step = 0;
|
||||||
|
|
||||||
//the base freq value is per EricCottrell in the Understanding NXDN Trunking Forum Thread
|
//reworked to include Direct Frequency Assignment if available
|
||||||
//will need to do some research or tests to confirm these values are accurate
|
|
||||||
|
|
||||||
//first, check channel map
|
//first, check channel map for imported value, DFA systems most likely won't need an import,
|
||||||
|
//unless it has 'system definable' attributes
|
||||||
if (state->trunk_chan_map[channel] != 0)
|
if (state->trunk_chan_map[channel] != 0)
|
||||||
{
|
{
|
||||||
freq = state->trunk_chan_map[channel];
|
freq = state->trunk_chan_map[channel];
|
||||||
@@ -76,33 +76,41 @@ long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t
|
|||||||
return (freq);
|
return (freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if not found, attempt to find it via calculation
|
//then, let's see if its DFA instead -- 6.5.36
|
||||||
else
|
else if (state->nxdn_rcn == 1)
|
||||||
{
|
{
|
||||||
if ((channel > 0) && (channel <= 400))
|
//determine the base frequency in Hz
|
||||||
{
|
if (state->nxdn_base_freq == 1) base = 100000000; //100 MHz
|
||||||
if (opts->frame_nxdn48 == 1) base = 450000000;
|
else if (state->nxdn_base_freq == 2) base = 330000000; //330 Mhz
|
||||||
else base = 451000000;
|
else if (state->nxdn_base_freq == 3) base = 400000000; //400 Mhz
|
||||||
|
else if (state->nxdn_base_freq == 4) base = 750000000; //750 Mhz
|
||||||
|
else base = 0; //just set to zero, will be system definable most likely and won't be able to calc
|
||||||
|
|
||||||
freq = base + (channel - 1) * 12500;
|
//determine the step value in Hz
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
if (state->nxdn_step = 2) step = 1250; //1.25 kHz
|
||||||
|
else if (state->nxdn_step = 3) step = 3125; //3.125 kHz
|
||||||
|
else step = 0; //just set to zero, will be system definable most likely and won't be able to calc
|
||||||
|
|
||||||
|
//if we have a valid base and step, then calc frequency
|
||||||
|
//6.5.45. Outbound/Inbound Frequency Number (OFN/IFN)
|
||||||
|
if (base && step)
|
||||||
|
{
|
||||||
|
freq = base + (channel * step);
|
||||||
|
fprintf (stderr, "\n DFA Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||||
return (freq);
|
return (freq);
|
||||||
}
|
}
|
||||||
else if ((channel >= 401) && (channel <= 800))
|
else
|
||||||
{
|
{
|
||||||
if (opts->frame_nxdn48 == 1) base = 460000000;
|
fprintf(stderr, "\n Custom DFA Settings -- Unknown Freq;");
|
||||||
else base = 461000000;
|
|
||||||
|
|
||||||
freq = base + (channel - 401) * 12500;
|
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
|
||||||
return (freq);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n Non-standard frequency or channel not found in import file");
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n Channel not found in import file");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
483
src/p25_lcw.c
483
src/p25_lcw.c
@@ -1,160 +1,363 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* p25_lcw.c
|
||||||
|
* P25p1 Link Control Word Decoding
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-05 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
void
|
//new p25_lcw function here -- TIA-102.AABF-D LCW Format Messages (if anybody wants to fill the rest out)
|
||||||
processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo)
|
void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t irrecoverable_errors)
|
||||||
{
|
{
|
||||||
|
uint8_t lc_format = (uint8_t)ConvertBitIntoBytes(&LCW_bits[0], 8); //format
|
||||||
|
uint8_t lc_opcode = (uint8_t)ConvertBitIntoBytes(&LCW_bits[2], 8); //opcode portion of format
|
||||||
|
uint8_t lc_mfid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 8); //mfid
|
||||||
|
uint8_t lc_svcopt = (uint8_t)ConvertBitIntoBytes(&LCW_bits[16], 8); //service options
|
||||||
|
uint8_t lc_pf = LCW_bits[0]; //protect flag
|
||||||
|
uint8_t lc_sf = LCW_bits[1]; //Implicit / Explicit MFID Format
|
||||||
|
|
||||||
char tgid[17], tmpstr[255];
|
if (lc_pf == 1) //check the protect flag -- if set, its an encrypted lcw
|
||||||
long talkgroup, source;
|
{
|
||||||
int i, j;
|
fprintf (stderr, "%s",KRED);
|
||||||
|
fprintf (stderr, " LCW Protected ");
|
||||||
|
fprintf (stderr, "%s",KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
tgid[16] = 0;
|
if (lc_pf == 0) //not protected/encrypted lcw
|
||||||
|
{
|
||||||
|
|
||||||
if (opts->p25lc == 1)
|
if (opts->payload == 1) fprintf (stderr, " LCW");
|
||||||
{
|
|
||||||
fprintf (stderr, "lcformat: %s mfid: %s lcinfo: %s ", lcformat, mfid, lcinfo);
|
|
||||||
if (opts->p25tg == 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (lcformat, "00000100") == 0)
|
//check to see if we need to run these as MFID 0 or 1 only (standard)
|
||||||
|
if (lc_mfid == 0 || lc_mfid == 1) //lc_mfid == 0
|
||||||
{
|
{
|
||||||
|
|
||||||
// first tg is the active channel
|
//check the service options on applicable formats
|
||||||
j = 0;
|
if (lc_format == 0x4A || lc_format == 0x46 || lc_format == 0x45
|
||||||
for (i = 40; i < 52; i++)
|
|| lc_format == 0x44 || lc_format == 0x03 || lc_format == 0x00)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (lc_svcopt & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (lc_svcopt & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
{
|
{
|
||||||
if (state->tgcount < 24)
|
if (lc_svcopt & 0x20) fprintf (stderr, " Duplex");
|
||||||
{
|
if (lc_svcopt & 0x10) fprintf (stderr, " Packet");
|
||||||
state->tg[state->tgcount][j] = lcinfo[i];
|
else fprintf (stderr, " Circuit");
|
||||||
}
|
if (lc_svcopt & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
tmpstr[j] = lcinfo[i];
|
fprintf (stderr, " Priority %d", lc_svcopt & 0x7); //call priority
|
||||||
j++;
|
|
||||||
}
|
}
|
||||||
tmpstr[12] = 48;
|
}
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
if (lc_format == 0x00)
|
||||||
tmpstr[15] = 48;
|
{
|
||||||
tmpstr[16] = 0;
|
fprintf (stderr, " Group Voice Channel User");
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
uint8_t res = (uint8_t)ConvertBitIntoBytes(&LCW_bits[24], 7);
|
||||||
state->lasttg = talkgroup;
|
uint8_t explicit = LCW_bits[24]; //explicit source id required == 1, full SUID on seperate LCW
|
||||||
if (state->tgcount < 24)
|
uint16_t group = (uint16_t)ConvertBitIntoBytes(&LCW_bits[32], 16);
|
||||||
|
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||||
|
fprintf (stderr, " - Group %d Source %d", group, source);
|
||||||
|
|
||||||
|
//don't set this when zero, annoying blink occurs in ncurses
|
||||||
|
if (group != 0) state->lasttg = group;
|
||||||
|
if (source != 0) state->lastsrc = source;
|
||||||
|
|
||||||
|
sprintf (state->call_string[0], " Group ");
|
||||||
|
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
|
||||||
|
else if (lc_svcopt & 0x40) strcat (state->call_string[0], " Encrypted ");
|
||||||
|
else strcat (state->call_string[0], " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x03)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit to Unit Voice Channel User");
|
||||||
|
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[24], 24);
|
||||||
|
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||||
|
fprintf (stderr, " - Target %d Source %d", target, source);
|
||||||
|
|
||||||
|
//don't set this when zero, annoying blink occurs in ncurses
|
||||||
|
if (target != 0) state->lasttg = target;
|
||||||
|
if (source != 0) state->lastsrc = source;
|
||||||
|
|
||||||
|
sprintf (state->call_string[0], " Private ");
|
||||||
|
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
|
||||||
|
else if (lc_svcopt & 0x40) strcat (state->call_string[0], " Encrypted ");
|
||||||
|
else strcat (state->call_string[0], " ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//uses explicit bit?
|
||||||
|
else if (lc_format == 0x42) //is this group only, or group and private?
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Group Voice Channel Update - ");
|
||||||
|
uint16_t channel1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[8], 16);
|
||||||
|
uint16_t group1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||||||
|
uint16_t channel2 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[40], 16);
|
||||||
|
uint16_t group2 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||||||
|
|
||||||
|
if (channel1 && group1)
|
||||||
{
|
{
|
||||||
state->tgcount = state->tgcount + 1;
|
fprintf (stderr, "Ch: %04X TG: %d; ", channel1, group1);
|
||||||
}
|
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d; ", channel1, group1);
|
||||||
if (opts->p25tg == 1)
|
state->last_active_time = time(NULL);
|
||||||
{
|
|
||||||
fprintf (stderr, "tg: %li ", talkgroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->p25tg == 1)
|
if (channel2 && group2 && group1 != group2)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "tg: %li ", talkgroup);
|
fprintf (stderr, "Ch: %04X TG: %d; ", channel2, group2);
|
||||||
|
sprintf (state->active_channel[1], "Active Ch: %04X TG: %d; ", channel2, group2);
|
||||||
// the remaining 3 appear to be other active tg's on the system
|
state->last_active_time = time(NULL);
|
||||||
j = 0;
|
|
||||||
for (i = 28; i < 40; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
fprintf (stderr, "%li ", talkgroup);
|
|
||||||
j = 0;
|
|
||||||
for (i = 16; i < 28; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
fprintf (stderr, "%li ", talkgroup);
|
|
||||||
j = 0;
|
|
||||||
for (i = 4; i < 16; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
fprintf (stderr, "%li\n", talkgroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x44)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Group Voice Channel Update – Explicit");
|
||||||
|
uint16_t group1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||||||
|
uint16_t channelt = (uint16_t)ConvertBitIntoBytes(&LCW_bits[40], 16);
|
||||||
|
uint16_t channelr = (uint16_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||||||
|
fprintf (stderr, "Ch: %04X TG: %d; ", channelt, group1);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x45)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit to Unit Answer Request");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x46)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Telephone Interconnect Voice Channel User");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x47)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Telephone Interconnect Answer Request");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x49)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Souce ID Extension");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x4A)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit to Unit Voice Channel User – Extended");
|
||||||
|
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 24);
|
||||||
|
uint32_t suid = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 24);
|
||||||
|
fprintf (stderr, "TGT: %04X SUID: %d; ", target, suid);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x50)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Group Affiliation Query");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x51)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit Registration Command");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x52) //wonder if anybody uses this if its deleted/obsolete
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit Authentication Command - OBSOLETE");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x53)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Status Query");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x54)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Status Update");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x55)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Status Update");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x56)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Call Alert");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x57)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Extended Function Command");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x58)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Channel Identifier Update");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x59)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Channel Identifier Update VU");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x5A)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Status Update – Source ID Extension Required");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x5C)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Extended Function Command – Source ID Extension Required");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x60)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " System Service Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x61)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Secondary Control Channel Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x62)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Adjacent Site Status Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x63)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " RFSS Status Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x64)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Network Status Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x65)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Protection Parameter Broadcast - OBSOLETE");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
else if (lc_format == 0x66)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Secondary Control Channel Broadcast – Explicit (LCSCBX)");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x67) //explicit
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Adjacent Site Status (LCASBX)");
|
||||||
|
uint8_t lra = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 8);
|
||||||
|
uint16_t channelt = (uint16_t)ConvertBitIntoBytes(&LCW_bits[16], 16);
|
||||||
|
uint8_t rfssid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[32], 8);
|
||||||
|
uint8_t siteid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[40], 8);
|
||||||
|
uint16_t channelr = (uint16_t)ConvertBitIntoBytes(&LCW_bits[48], 16);
|
||||||
|
uint8_t cfva = (uint8_t)ConvertBitIntoBytes(&LCW_bits[64], 4);
|
||||||
|
fprintf (stderr, " - RFSS %d Site %d CH %04X", rfssid, siteid, channelt);
|
||||||
|
|
||||||
|
//debug print only
|
||||||
|
// fprintf (stderr, "\n ");
|
||||||
|
// fprintf (stderr, " LRA [%02X] RFSS [%03d] SITE [%03d] CHAN-T [%04X] CHAN-R [%02X] CFVA [%X]\n ", lra, rfssid, siteid, channelt, channelr, cfva);
|
||||||
|
// if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||||
|
// if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||||
|
// if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||||
|
// else fprintf (stderr, " Last Known");
|
||||||
|
if (cfva & 0x1) fprintf (stderr, "- Connection Active");
|
||||||
|
// process_channel_to_freq (opts, state, channelt);
|
||||||
|
//end debug, way too much for a simple link control line
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x68)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " RFSS Status Broadcast – Explicit (LCRSBX)");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x69)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Network Status Broadcast – Explicit (LCNSBX)");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x6A)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Conventional Fallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x6B)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Message Update – Source ID Extension Required");
|
||||||
|
}
|
||||||
|
|
||||||
|
//tune back to CC here - save about 1-2 seconds
|
||||||
|
else if (lc_format == 0x4F) //# Call Termination/Cancellation
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Call Termination");
|
||||||
|
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
//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?
|
||||||
|
|
||||||
|
//clear heuristics from current traffic channel
|
||||||
|
if (opts->frame_p25p1 == 1 && opts->use_heuristics == 1)
|
||||||
|
{
|
||||||
|
initialize_p25_heuristics(&state->p25_heuristics);
|
||||||
|
initialize_p25_heuristics(&state->inv_p25_heuristics);
|
||||||
|
}
|
||||||
|
|
||||||
|
//rigctl
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
|
state->lasttg = 0;
|
||||||
|
state->lastsrc = 0;
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
// state->payload_miP = 0;
|
||||||
|
//reset some strings
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->active_channel[0], "%s", "");
|
||||||
|
sprintf (state->active_channel[1], "%s", "");
|
||||||
|
opts->p25_is_tuned = 0;
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||||
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
|
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||||||
|
}
|
||||||
|
//rtl
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
state->lasttg = 0;
|
||||||
|
state->lastsrc = 0;
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
// state->payload_miP = 0;
|
||||||
|
//reset some strings
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->active_channel[0], "%s", "");
|
||||||
|
sprintf (state->active_channel[1], "%s", "");
|
||||||
|
opts->p25_is_tuned = 0;
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||||
|
rtl_dev_tune (opts, state->p25_cc_freq);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else fprintf (stderr, " Unknown Format %02X MFID %02X SVC %02X", lc_format, lc_mfid, lc_svcopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (strcmp (lcformat, "00000000") == 0)
|
//not a duplicate, this one will print if not MFID 0 or 1
|
||||||
{
|
else fprintf (stderr, " Unknown Format %02X MFID %02X ", lc_format, lc_mfid);
|
||||||
j = 0;
|
}
|
||||||
if (strcmp (mfid, "10010000") == 0)
|
|
||||||
{
|
//ending line break
|
||||||
for (i = 20; i < 32; i++)
|
fprintf (stderr, "\n");
|
||||||
{
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tg[state->tgcount][j] = lcinfo[i];
|
|
||||||
}
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 16; i < 32; i++)
|
|
||||||
{
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tg[state->tgcount][j] = lcinfo[i];
|
|
||||||
}
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lasttg = talkgroup;
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tgcount = state->tgcount + 1;
|
|
||||||
}
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "tg: %li ", talkgroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
j = 0;
|
|
||||||
for (i = 32; i < 56; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[24] = 0;
|
|
||||||
source = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lastsrc = source;
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "src: %li emr: %c\n", source, lcinfo[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((opts->p25tg == 1) && (opts->p25lc == 1))
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ static int find_min(uint8_t list[], int len) {
|
|||||||
index = i;
|
index = i;
|
||||||
unique = 1;
|
unique = 1;
|
||||||
} else if (list[i] == min) {
|
} else if (list[i] == min) {
|
||||||
unique = 0;
|
//unique = 0;
|
||||||
|
; //do nothing, still can be correct and pass CRC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* return -1 if a minimum can't be found */
|
/* return -1 if a minimum can't be found */
|
||||||
|
|||||||
118
src/p25p1_hdu.c
118
src/p25p1_hdu.c
@@ -453,93 +453,53 @@ processHDU(dsd_opts* opts, dsd_state* state)
|
|||||||
status = getDibit (opts, state);
|
status = getDibit (opts, state);
|
||||||
//TODO: Do something useful with the status bits...
|
//TODO: Do something useful with the status bits...
|
||||||
|
|
||||||
//if (state->carrier == 1 && state->errs == 0) //only update when carrier is present, and no errors??
|
algidhex = strtol (algid, NULL, 2);
|
||||||
if (state->carrier == 1)
|
kidhex = strtol (kid, NULL, 2);
|
||||||
{
|
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
||||||
algidhex = strtol (algid, NULL, 2);
|
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
||||||
kidhex = strtol (kid, NULL, 2);
|
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
||||||
|
|
||||||
state->payload_algid = algidhex;
|
//reset dropbytes - skip first 11 for LCW
|
||||||
state->payload_keyid = kidhex;
|
state->dropL = 267;
|
||||||
//state->payload_mfid = ConvertBitIntoBytes(&mfid[0], 7);
|
|
||||||
//state->payload_mfid = strtol (mfid, NULL, 2);
|
|
||||||
|
|
||||||
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
|
||||||
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
|
||||||
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
|
||||||
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
|
||||||
state->payload_miP = (mihex1 << 32) | (mihex2);
|
|
||||||
}
|
|
||||||
|
|
||||||
//set vc counter to 0
|
//set vc counter to 0
|
||||||
state->p25vc = 0;
|
state->p25vc = 0;
|
||||||
|
|
||||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
if (irrecoverable_errors == 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX MFID: 0x%02X", algidhex, kidhex, mihex1, mihex2, mihex3, state->payload_mfid);
|
fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%04X MI: 0x%08llX%08llX", algidhex, kidhex, mihex1, mihex2);
|
||||||
|
state->payload_algid = algidhex;
|
||||||
|
state->payload_keyid = kidhex;
|
||||||
|
if (state->R != 0 && state->payload_algid == 0xAA)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Key: %010llX", state->R);
|
||||||
|
opts->unmute_encrypted_p25 = 1;
|
||||||
|
}
|
||||||
|
else if (state->payload_algid != 0 && state->payload_algid != 0x80)
|
||||||
|
{
|
||||||
|
//may want to mute this again, or may not want to
|
||||||
|
opts->unmute_encrypted_p25 = 0;
|
||||||
|
}
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
||||||
|
state->payload_miP = (mihex1 << 32) | (mihex2);
|
||||||
|
|
||||||
|
if (state->payload_algid != 0x80 && state->payload_algid != 0x0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " ENC");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, " ENC \n");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->p25enc == 1 && opts->payload == 0)
|
|
||||||
{
|
|
||||||
algidhex = strtol (algid, NULL, 2);
|
|
||||||
kidhex = strtol (kid, NULL, 2);
|
|
||||||
}
|
|
||||||
if (opts->p25lc == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "mfid: %s tgid: %s ", mfid, tgid);
|
|
||||||
if (opts->p25tg == 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
j = 0;
|
|
||||||
if (strcmp (mfid, "10010000") == 0)
|
|
||||||
{
|
|
||||||
for (i = 4; i < 16; i++)
|
|
||||||
{
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tg[state->tgcount][j] = tgid[i];
|
|
||||||
}
|
|
||||||
tmpstr[j] = tgid[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = '0';
|
|
||||||
tmpstr[13] = '0';
|
|
||||||
tmpstr[14] = '0';
|
|
||||||
tmpstr[15] = '0';
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (i = 0; i < 16; i++)
|
fprintf (stderr, "%s", KRED);
|
||||||
{
|
fprintf (stderr, " HDU FEC ERR \n");
|
||||||
if (state->tgcount < 24)
|
fprintf (stderr, "%s", KNRM);
|
||||||
{
|
}
|
||||||
state->tg[state->tgcount][j] = tgid[i];
|
|
||||||
}
|
|
||||||
tmpstr[j] = tgid[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lasttg = talkgroup;
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tgcount = state->tgcount + 1;
|
|
||||||
}
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "tg: %li\n", talkgroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,8 @@ processLDU1 (dsd_opts* opts, dsd_state* state)
|
|||||||
|
|
||||||
// TODO: error correction of the LSD bytes...
|
// TODO: error correction of the LSD bytes...
|
||||||
// TODO: do something useful with the LSD bytes...
|
// TODO: do something useful with the LSD bytes...
|
||||||
|
|
||||||
|
state->dropL += 2; //need to skip 2 here for the LSD bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMBE 9
|
// IMBE 9
|
||||||
@@ -324,15 +326,53 @@ processLDU1 (dsd_opts* opts, dsd_state* state)
|
|||||||
lcinfo[54] = hex_data[ 0][4] + '0';
|
lcinfo[54] = hex_data[ 0][4] + '0';
|
||||||
lcinfo[55] = hex_data[ 0][5] + '0';
|
lcinfo[55] = hex_data[ 0][5] + '0';
|
||||||
|
|
||||||
unsigned long long int lcinfohex = 0;
|
int j;
|
||||||
if (state->carrier == 1 && state->errs == 0) //only update when carrier is present, otherwise, garbage values may be collected
|
uint8_t LCW_bytes[9];
|
||||||
{
|
uint8_t LCW_bits[72];
|
||||||
state->payload_mfid = strtol (mfid, NULL, 2);
|
memset (LCW_bytes, 0, sizeof(LCW_bytes));
|
||||||
|
memset (LCW_bits, 0, sizeof(LCW_bits));
|
||||||
|
|
||||||
//state->payload_mfid = ConvertBitIntoBytes(&mfid[0], 7);
|
//load array above into byte form first
|
||||||
lcinfohex = ConvertBitIntoBytes(&lcinfo[0], 55);
|
LCW_bytes[0] = (uint8_t) ConvertBitIntoBytes(&lcformat[0], 8);
|
||||||
//fprintf (stderr, " LDU1 LCINFO %16llX \n", lcinfohex);
|
LCW_bytes[1] = (uint8_t) ConvertBitIntoBytes(&mfid[0], 8);
|
||||||
|
for (i = 0; i < 7; i++) LCW_bytes[i+2] = (uint8_t) ConvertBitIntoBytes(&lcinfo[i*8], 8);
|
||||||
|
|
||||||
|
//load as bit array next (easier to process data in bit form)
|
||||||
|
for(i = 0, j = 0; i < 9; i++, j+=8)
|
||||||
|
{
|
||||||
|
LCW_bits[j + 0] = (LCW_bytes[i] >> 7) & 0x01;
|
||||||
|
LCW_bits[j + 1] = (LCW_bytes[i] >> 6) & 0x01;
|
||||||
|
LCW_bits[j + 2] = (LCW_bytes[i] >> 5) & 0x01;
|
||||||
|
LCW_bits[j + 3] = (LCW_bytes[i] >> 4) & 0x01;
|
||||||
|
LCW_bits[j + 4] = (LCW_bytes[i] >> 3) & 0x01;
|
||||||
|
LCW_bits[j + 5] = (LCW_bytes[i] >> 2) & 0x01;
|
||||||
|
LCW_bits[j + 6] = (LCW_bytes[i] >> 1) & 0x01;
|
||||||
|
LCW_bits[j + 7] = (LCW_bytes[i] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
//send to new P25 LCW function
|
||||||
|
if (irrecoverable_errors == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
p25_lcw (opts, state, LCW_bits, 0);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KRED);
|
||||||
|
fprintf (stderr, " LCW FEC ERR ");
|
||||||
|
fprintf (stderr, "%s\n", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KCYN);
|
||||||
|
fprintf (stderr, " P25 LCW Payload ");
|
||||||
|
for (i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", LCW_bytes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s\n", KNRM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,6 +172,8 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
// TODO: error correction of the LSD bytes...
|
// TODO: error correction of the LSD bytes...
|
||||||
// TODO: do something useful with the LSD bytes...
|
// TODO: do something useful with the LSD bytes...
|
||||||
|
|
||||||
|
state->dropL += 2; //need to skip 2 here for the LSD bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMBE 9
|
// IMBE 9
|
||||||
@@ -183,6 +185,9 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
|
|||||||
//set vc counter to 0
|
//set vc counter to 0
|
||||||
state->p25vc = 0;
|
state->p25vc = 0;
|
||||||
|
|
||||||
|
//reset dropbytes -- skip first 11 for next LCW
|
||||||
|
state->dropL = 267;
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
@@ -357,37 +362,87 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
|
|||||||
kid[14] = hex_data[ 0][4] + '0';
|
kid[14] = hex_data[ 0][4] + '0';
|
||||||
kid[15] = hex_data[ 0][5] + '0';
|
kid[15] = hex_data[ 0][5] + '0';
|
||||||
|
|
||||||
|
algidhex = strtol (algid, NULL, 2);
|
||||||
|
kidhex = strtol (kid, NULL, 2);
|
||||||
|
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
||||||
|
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
||||||
|
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
||||||
|
|
||||||
//if (state->carrier == 1 && state->errs == 0) //only update when carrier is present, and no errors??
|
if (irrecoverable_errors == 0)
|
||||||
if (state->carrier == 1)
|
|
||||||
{
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
algidhex = strtol (algid, NULL, 2);
|
fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%04X MI: 0x%08llX%08llX", algidhex, kidhex, mihex1, mihex2);
|
||||||
kidhex = strtol (kid, NULL, 2);
|
|
||||||
|
|
||||||
state->payload_algid = algidhex;
|
state->payload_algid = algidhex;
|
||||||
state->payload_keyid = kidhex;
|
state->payload_keyid = kidhex;
|
||||||
|
if (state->R != 0 && state->payload_algid == 0xAA)
|
||||||
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
{
|
||||||
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
fprintf (stderr, " Key: %010llX", state->R);
|
||||||
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
opts->unmute_encrypted_p25 = 1;
|
||||||
|
}
|
||||||
|
else if (state->payload_algid != 0 && state->payload_algid != 0x80)
|
||||||
|
{
|
||||||
|
//may want to mute this again, or may not want to
|
||||||
|
opts->unmute_encrypted_p25 = 0;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
||||||
state->payload_miP = (mihex1 << 32) | (mihex2);
|
state->payload_miP = (mihex1 << 32) | (mihex2);
|
||||||
|
|
||||||
|
if (state->payload_algid != 0x80 && state->payload_algid != 0x0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " ENC");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " LDU2 FEC ERR \n");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
//run LFSR on the MI if we have irrecoverable errors here
|
||||||
|
if (irrecoverable_errors && state->payload_algid != 0x80 && state->payload_keyid != 0 && state->payload_miP != 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KYEL);
|
LFSRP(state);
|
||||||
fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX", algidhex, kidhex, mihex1, mihex2, mihex3);
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, " ENC \n");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//LFSR code courtesy of https://github.com/mattames/LFSR/
|
||||||
|
void LFSRP(dsd_state * state)
|
||||||
|
{
|
||||||
|
//rework for P2 TDMA support
|
||||||
|
unsigned long long int lfsr = 0;
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
lfsr = state->payload_miP;
|
||||||
|
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
lfsr = state->payload_miN;
|
||||||
|
|
||||||
|
int cnt = 0;
|
||||||
|
for(cnt=0;cnt<64;cnt++)
|
||||||
|
{
|
||||||
|
// Polynomial is C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
|
||||||
|
unsigned long long int bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
|
||||||
|
lfsr = (lfsr << 1) | (bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
state->payload_miP = lfsr;
|
||||||
|
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
state->payload_miN = lfsr;
|
||||||
|
|
||||||
|
//print current ENC identifiers already known and new calculated MI
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
fprintf (stderr, " LDU2/ESS_B FEC ERR - ALG: 0x%02X KEY ID: 0x%04X LFSR MI: 0x%016llX", state->payload_algid, state->payload_keyid, state->payload_miP);
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
fprintf (stderr, " LDU2/ESS_B FEC ERR - ALG: 0x%02X KEY ID: 0x%04X LFSR MI: 0x%016llX", state->payload_algidR, state->payload_keyidR, state->payload_miN);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|||||||
531
src/p25p1_mdpu.c
Normal file
531
src/p25p1_mdpu.c
Normal file
@@ -0,0 +1,531 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* p25p1_mpdu.c
|
||||||
|
* P25p1 Multi Block PDU and Multi Block Trunking
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-03 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
static uint32_t crc32mbf(uint8_t buf[], int len)
|
||||||
|
{
|
||||||
|
uint32_t g = 0x04c11db7;
|
||||||
|
uint64_t crc = 0;
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
crc <<= 1;
|
||||||
|
int b = ( buf [i / 8] >> (7 - (i % 8)) ) & 1;
|
||||||
|
if (((crc >> 32) ^ b) & 1) crc ^= g;
|
||||||
|
}
|
||||||
|
crc = (crc & 0xffffffff) ^ 0xffffffff;
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processMPDU(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
//reset some strings when returning from a call in case they didn't get zipped already
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
|
||||||
|
//clear stale Active Channel messages here
|
||||||
|
if ( (time(NULL) - state->last_active_time) > 3 )
|
||||||
|
{
|
||||||
|
memset (state->active_channel, 0, sizeof(state->active_channel));
|
||||||
|
}
|
||||||
|
|
||||||
|
int tsbkbit[196]; //tsbk bit array, 196 trellis encoded bits
|
||||||
|
int tsbk_dibit[98];
|
||||||
|
|
||||||
|
memset (tsbkbit, 0, sizeof(tsbkbit));
|
||||||
|
memset (tsbk_dibit, 0, sizeof(tsbk_dibit));
|
||||||
|
|
||||||
|
int dibit = 0;
|
||||||
|
|
||||||
|
uint8_t tsbk_byte[12]; //12 byte return from bd_bridge (block_deinterleave)
|
||||||
|
memset (tsbk_byte, 0, sizeof(tsbk_byte));
|
||||||
|
|
||||||
|
unsigned long long int PDU[24];
|
||||||
|
memset (PDU, 0, sizeof(PDU));
|
||||||
|
|
||||||
|
int tsbk_decoded_bits[190]; //decoded bits from tsbk_bytes for sending to crc16_lb_bridge
|
||||||
|
memset (tsbk_decoded_bits, 0, sizeof(tsbk_decoded_bits));
|
||||||
|
|
||||||
|
uint8_t mpdu_decoded_bits[288];
|
||||||
|
memset (mpdu_decoded_bits, 0, sizeof(mpdu_decoded_bits));
|
||||||
|
|
||||||
|
uint8_t mpdu_crc_bits[196];
|
||||||
|
memset (mpdu_crc_bits, 0, sizeof(mpdu_crc_bits));
|
||||||
|
|
||||||
|
uint8_t mpdu_crc_bytes[24];
|
||||||
|
memset (mpdu_crc_bytes, 0, sizeof(mpdu_crc_bytes));
|
||||||
|
|
||||||
|
int flushing_bits[196]; //for flushing the trellis state machine
|
||||||
|
memset (flushing_bits, 0, sizeof(flushing_bits));
|
||||||
|
|
||||||
|
int i, j, k, b, x, y;
|
||||||
|
int ec[3]; //error value returned from (block_deinterleave)
|
||||||
|
int err[2]; //error value returned from ccrc16 or crc32
|
||||||
|
memset (ec, -2, sizeof(ec));
|
||||||
|
memset (err, -2, sizeof(err));
|
||||||
|
|
||||||
|
int skipdibit = 14; //initial status dibit will occur at 14, then add 36 each time it occurs
|
||||||
|
int protectbit = 0;
|
||||||
|
int MFID = 0xFF; //Manufacturer ID
|
||||||
|
|
||||||
|
uint8_t mpdu_byte[36];
|
||||||
|
memset (mpdu_byte, 0, sizeof(mpdu_byte));
|
||||||
|
|
||||||
|
uint8_t io = 0;
|
||||||
|
uint8_t fmt = 0;
|
||||||
|
uint8_t sap = 0;
|
||||||
|
uint8_t blks = 0;
|
||||||
|
uint8_t opcode = 0;
|
||||||
|
|
||||||
|
//CRC32
|
||||||
|
uint32_t CRCComputed = 0;
|
||||||
|
uint32_t CRCExtracted = 0;
|
||||||
|
|
||||||
|
//collect three reps of 101 dibits (98 valid dibits with status dibits interlaced)
|
||||||
|
for (j = 0; j < 3; j++)
|
||||||
|
{
|
||||||
|
k = 0;
|
||||||
|
for (i = 0; i < 101; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
|
||||||
|
if (i+(j*101) != skipdibit) //
|
||||||
|
{
|
||||||
|
tsbk_dibit[k] = dibit;
|
||||||
|
tsbkbit[k*2] = (dibit >> 1) & 1;
|
||||||
|
tsbkbit[k*2+1] = (dibit & 1);
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i+(j*101) == skipdibit)
|
||||||
|
{
|
||||||
|
skipdibit += 36; //was set to 35, this was the bug
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//flushing the trellis state machine
|
||||||
|
if (j == 0)
|
||||||
|
{
|
||||||
|
bd_bridge(flushing_bits, tsbk_byte);
|
||||||
|
//reset tsbk_byte afterwards
|
||||||
|
memset (tsbk_byte, 0, sizeof(tsbk_byte));
|
||||||
|
}
|
||||||
|
|
||||||
|
//send tsbkbit to block_deinterleave and return tsbk_byte
|
||||||
|
ec[j] = bd_bridge(tsbkbit, tsbk_byte);
|
||||||
|
|
||||||
|
//too many bit manipulations!
|
||||||
|
k = 0;
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
for (x = 0; x < 8; x++)
|
||||||
|
{
|
||||||
|
tsbk_decoded_bits[k] = ((tsbk_byte[i] << x) & 0x80) >> 7;
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//CRC16 on the header
|
||||||
|
if (j == 0) err[0] = crc16_lb_bridge(tsbk_decoded_bits, 80);
|
||||||
|
|
||||||
|
//load into bit array for storage (easier decoding for future PDUs)
|
||||||
|
for (i = 0; i < 96; i++) mpdu_decoded_bits[i+(j*96)] = (uint8_t)tsbk_decoded_bits[i];
|
||||||
|
|
||||||
|
//shuffle corrected bits back into tsbk_byte
|
||||||
|
k = 0;
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
int byte = 0;
|
||||||
|
for (x = 0; x < 8; x++)
|
||||||
|
{
|
||||||
|
byte = byte << 1;
|
||||||
|
byte = byte | tsbk_decoded_bits[k];
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
tsbk_byte[i] = byte;
|
||||||
|
mpdu_byte[i+(j*12)] = byte; //add to completed MBF format bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
io = (mpdu_byte[0] >> 5) & 0x1;
|
||||||
|
fmt = mpdu_byte[0] & 0x1F;
|
||||||
|
sap = mpdu_byte[1] & 0x3F;
|
||||||
|
blks = mpdu_byte[6] & 0x7F;
|
||||||
|
MFID = mpdu_byte[2];
|
||||||
|
|
||||||
|
//group list mode so we can look and see if we need to block tuning any groups, etc
|
||||||
|
char mode[8]; //allow, block, digital, enc, etc
|
||||||
|
|
||||||
|
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||||
|
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||||
|
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||||
|
|
||||||
|
fprintf (stderr, "%s",KGRN);
|
||||||
|
fprintf (stderr, " P25 MBF FMT: %02X SAP: %02X", fmt, sap);
|
||||||
|
fprintf (stderr, " Blocks: %02X MFID: %02X", blks, MFID);
|
||||||
|
|
||||||
|
//trunking blocks
|
||||||
|
if ((sap == 0x3D) && ((fmt == 0x17) || (fmt == 0x15)))
|
||||||
|
{
|
||||||
|
if (fmt == 0x15) fprintf (stderr, " UNC");
|
||||||
|
else fprintf (stderr, " ALT");
|
||||||
|
fprintf (stderr, " MBT"); //find way to convert to vPDU or TSBK format?
|
||||||
|
if (fmt == 0x17) opcode = mpdu_byte[7] & 0x3F; //alt MBT
|
||||||
|
else opcode = mpdu_byte[12] & 0x3F; //unconf MBT
|
||||||
|
fprintf (stderr, " - OP: %02X", opcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
//CRC32 is now working!
|
||||||
|
for (i = 0; i < 196; i++) mpdu_crc_bits[i] = mpdu_decoded_bits[i+96];
|
||||||
|
for (i = 0; i < 24; i++) mpdu_crc_bytes[i] = mpdu_byte[i+12];
|
||||||
|
if (blks == 1) //blocks to follow is 1
|
||||||
|
{
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&mpdu_crc_bits[96-32], 32);
|
||||||
|
CRCComputed = crc32mbf(mpdu_crc_bytes, 96-32);
|
||||||
|
if (CRCComputed == CRCExtracted) err[1] = 0;
|
||||||
|
}
|
||||||
|
if (blks == 2) //blocks to follow is 2
|
||||||
|
{
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&mpdu_crc_bits[192-32], 32);
|
||||||
|
CRCComputed = crc32mbf(mpdu_crc_bytes, 192-32);
|
||||||
|
if (CRCComputed == CRCExtracted) err[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//just going to do a couple for now -- the extended format will not be vPDU compatible :(
|
||||||
|
if (err[0] == 0 && err[1] == 0 && io == 1 && fmt == 0x17) //ALT Format
|
||||||
|
{
|
||||||
|
//NET_STS_BCST -- TIA-102.AABC-D 6.2.11.2
|
||||||
|
if (opcode == 0x3B)
|
||||||
|
{
|
||||||
|
int lra = mpdu_byte[3];
|
||||||
|
int sysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
|
||||||
|
int res_a = mpdu_byte[8];
|
||||||
|
int res_b = mpdu_byte[9];
|
||||||
|
long int wacn = (mpdu_byte[12] << 12) | (mpdu_byte[13] << 4) | (mpdu_byte[14] >> 4);
|
||||||
|
int channelt = (mpdu_byte[15] << 8) | mpdu_byte[16];
|
||||||
|
int channelr = (mpdu_byte[17] << 8) | mpdu_byte[18];
|
||||||
|
int ssc = mpdu_byte[19];
|
||||||
|
fprintf (stderr, "%s",KYEL);
|
||||||
|
fprintf (stderr, "\n Network Status Broadcast MBT - Extended \n");
|
||||||
|
fprintf (stderr, " LRA [%02X] WACN [%05lX] SYSID [%03X] NAC [%03llX]\n", lra, wacn, sysid, state->p2_cc);
|
||||||
|
fprintf (stderr, " CHAN-T [%04X] CHAN-R [%04X]", channelt, channelr);
|
||||||
|
long int ct_freq = process_channel_to_freq(opts, state, channelt);
|
||||||
|
long int cr_freq = process_channel_to_freq(opts, state, channelr);
|
||||||
|
|
||||||
|
state->p25_cc_freq = ct_freq;
|
||||||
|
state->p25_cc_is_tdma = 0; //flag off for CC tuning purposes when system is qpsk
|
||||||
|
|
||||||
|
//place the cc freq into the list at index 0 if 0 is empty, or not the same,
|
||||||
|
//so we can hunt for rotating CCs without user LCN list
|
||||||
|
if (state->trunk_lcn_freq[0] == 0 || state->trunk_lcn_freq[0] != state->p25_cc_freq)
|
||||||
|
{
|
||||||
|
state->trunk_lcn_freq[0] = state->p25_cc_freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
//only set IF these values aren't already hard set by the user
|
||||||
|
if (state->p2_hardset == 0)
|
||||||
|
{
|
||||||
|
state->p2_wacn = wacn;
|
||||||
|
state->p2_sysid = sysid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//RFSS Status Broadcast - Extended 6.2.15.2
|
||||||
|
else if (opcode == 0x3A)
|
||||||
|
{
|
||||||
|
int lra = mpdu_byte[3];
|
||||||
|
int lsysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
|
||||||
|
int rfssid = mpdu_byte[12];
|
||||||
|
int siteid = mpdu_byte[13];
|
||||||
|
int channelt = (mpdu_byte[14] << 8) | mpdu_byte[15];
|
||||||
|
int channelr = (mpdu_byte[16] << 8) | mpdu_byte[17];
|
||||||
|
int sysclass = mpdu_byte[18];
|
||||||
|
fprintf (stderr, "%s",KYEL);
|
||||||
|
fprintf (stderr, "\n RFSS Status Broadcast MBF - Extended \n");
|
||||||
|
fprintf (stderr, " LRA [%02X] SYSID [%03X] RFSS ID [%02X] SITE ID [%02X]\n CHAN-T [%04X] CHAN-R [%02X] SSC [%02X] ", lra, lsysid, rfssid, siteid, channelt, channelr, sysclass);
|
||||||
|
process_channel_to_freq (opts, state, channelt);
|
||||||
|
process_channel_to_freq (opts, state, channelr);
|
||||||
|
|
||||||
|
state->p2_siteid = siteid;
|
||||||
|
state->p2_rfssid = rfssid;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Adjacent Status Broadcast (ADJ_STS_BCST) Extended 6.2.2.2
|
||||||
|
else if (opcode == 0x3C)
|
||||||
|
{
|
||||||
|
int lra = mpdu_byte[3];
|
||||||
|
int cfva = mpdu_byte[4] >> 4;
|
||||||
|
int lsysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
|
||||||
|
int rfssid = mpdu_byte[8];
|
||||||
|
int siteid = mpdu_byte[9];
|
||||||
|
int channelt = (mpdu_byte[12] << 8) | mpdu_byte[13];
|
||||||
|
int channelr = (mpdu_byte[14] << 8) | mpdu_byte[15];
|
||||||
|
int sysclass = mpdu_byte[16];
|
||||||
|
long int wacn = (mpdu_byte[17] << 12) | (mpdu_byte[18] << 4) | (mpdu_byte[19] >> 4);
|
||||||
|
fprintf (stderr, "%s",KYEL);
|
||||||
|
fprintf (stderr, "\n Adjacent Status Broadcast - Extended\n");
|
||||||
|
fprintf (stderr, " LRA [%02X] CFVA [%X] RFSS[%03d] SYSID [%03X] SITE [%03X]\n CHAN-T [%04X] CHAN-R [%04X] SSC [%02X] WACN [%05lX]\n ", lra, cfva, rfssid, lsysid, siteid, channelt, channelr, sysclass, wacn);
|
||||||
|
if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||||
|
if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||||
|
if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||||
|
else fprintf (stderr, " Last Known");
|
||||||
|
if (cfva & 0x1) fprintf (stderr, " Valid RFSS Connection Active");
|
||||||
|
process_channel_to_freq (opts, state, channelt);
|
||||||
|
process_channel_to_freq (opts, state, channelr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Group Voice Channel Grant - Extended
|
||||||
|
else if (opcode == 0x0)
|
||||||
|
{
|
||||||
|
int svc = mpdu_byte[8];
|
||||||
|
int channelt = (mpdu_byte[14] << 8) | mpdu_byte[15];
|
||||||
|
int channelr = (mpdu_byte[16] << 8) | mpdu_byte[17];
|
||||||
|
long int source = (mpdu_byte[3] << 16) |(mpdu_byte[4] << 8) | mpdu_byte[5];
|
||||||
|
int group = (mpdu_byte[18] << 8) | mpdu_byte[19];
|
||||||
|
long int freq1 = 0;
|
||||||
|
long int freq2 = 0;
|
||||||
|
fprintf (stderr, "%s\n ",KYEL);
|
||||||
|
if (svc & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (svc & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (svc & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (svc & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
|
||||||
|
}
|
||||||
|
fprintf (stderr, " Group Voice Channel Grant Update - Extended");
|
||||||
|
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc, channelt, channelr, group, group);
|
||||||
|
freq1 = process_channel_to_freq (opts, state, channelt);
|
||||||
|
freq2 = process_channel_to_freq (opts, state, channelr);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d ", channelt, group);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
|
{
|
||||||
|
if (state->group_array[i].groupNumber == group)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||||
|
strcpy (mode, state->group_array[i].groupMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//Skip tuning encrypted calls if enc calls are disabled
|
||||||
|
if ( (svc & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//tune if tuning available
|
||||||
|
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
||||||
|
{
|
||||||
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
|
||||||
|
{
|
||||||
|
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||||
|
// if (opts->mod_qpsk == 1)
|
||||||
|
// {
|
||||||
|
// int spacing = state->p25_chan_spac[channelt >> 12];
|
||||||
|
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||||
|
// {
|
||||||
|
// state->samplesPerSymbol = 8;
|
||||||
|
// state->symbolCenter = 3;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
|
if (1 == 1)
|
||||||
|
{
|
||||||
|
if (state->p25_chan_tdma[channelt >> 12] == 1)
|
||||||
|
{
|
||||||
|
state->samplesPerSymbol = 8;
|
||||||
|
state->symbolCenter = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//rigctl
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
|
SetFreq(opts->rigctl_sockfd, freq1);
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||||
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
}
|
||||||
|
//rtl
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, freq1);
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||||
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unit to Unit Voice Channel Grant - Extended
|
||||||
|
else if (opcode == 0x6)
|
||||||
|
{
|
||||||
|
//I'm not doing EVERY element of this, just enough for tuning!
|
||||||
|
int svc = mpdu_byte[8];
|
||||||
|
int channelt = (mpdu_byte[22] << 8) | mpdu_byte[23];
|
||||||
|
int channelr = (mpdu_byte[24] << 8) | mpdu_byte[25]; //optional!
|
||||||
|
//using source and target address, not source and target id (is this correct?)
|
||||||
|
long int source = (mpdu_byte[3] << 16) |(mpdu_byte[4] << 8) | mpdu_byte[5];
|
||||||
|
long int target = (mpdu_byte[19] << 16) |(mpdu_byte[20] << 8) | mpdu_byte[21];
|
||||||
|
long int freq1 = 0;
|
||||||
|
long int freq2 = 0;
|
||||||
|
fprintf (stderr, "%s\n ",KYEL);
|
||||||
|
if (svc & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (svc & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (svc & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (svc & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
|
||||||
|
}
|
||||||
|
fprintf (stderr, " Unit to Unit Voice Channel Grant Update - Extended");
|
||||||
|
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Source [%ld][%04lX] Target [%ld][%04lX]", svc, channelt, channelr, source, source, target, target);
|
||||||
|
freq1 = process_channel_to_freq (opts, state, channelt);
|
||||||
|
freq2 = process_channel_to_freq (opts, state, channelr); //optional!
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "Active Ch: %04X TGT: %ld; ", channelt, target);
|
||||||
|
|
||||||
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
|
{
|
||||||
|
if (state->group_array[i].groupNumber == target)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||||
|
strcpy (mode, state->group_array[i].groupMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Skip tuning private calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_private_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//Skip tuning encrypted calls if enc calls are disabled
|
||||||
|
if ( (svc & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//tune if tuning available
|
||||||
|
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
||||||
|
{
|
||||||
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
|
||||||
|
{
|
||||||
|
|
||||||
|
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||||
|
// if (opts->mod_qpsk == 1)
|
||||||
|
// {
|
||||||
|
// int spacing = state->p25_chan_spac[channelt >> 12];
|
||||||
|
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||||
|
// {
|
||||||
|
// state->samplesPerSymbol = 8;
|
||||||
|
// state->symbolCenter = 3;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
|
if (1 == 1)
|
||||||
|
{
|
||||||
|
if (state->p25_chan_tdma[channelt >> 12] == 1)
|
||||||
|
{
|
||||||
|
state->samplesPerSymbol = 8;
|
||||||
|
state->symbolCenter = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//rigctl
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
|
SetFreq(opts->rigctl_sockfd, freq1);
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||||
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
}
|
||||||
|
//rtl
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, freq1);
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||||
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//convert mpdu_byte to vPDU format and get indication of what the message was
|
||||||
|
PDU[0] = 0x0C; //P25p1 MB Duid 0x0C
|
||||||
|
PDU[1] = opcode;
|
||||||
|
PDU[2] = MFID;
|
||||||
|
PDU[1] = PDU[1] ^ 0xC0; //flip bits to make it compatible with MAC_PDUs explicit format
|
||||||
|
|
||||||
|
//without any extra bytes filled in, we can atleast see what the MBT is supposed to be
|
||||||
|
//Hopefully, no 0 value things will cause issues -- call grants, iden_up, etc!
|
||||||
|
// fprintf (stderr, "%s",KYEL);
|
||||||
|
// process_MAC_VPDU(opts, state, 0, PDU);
|
||||||
|
// fprintf (stderr, "%s",KNRM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SKIPCALL: ; //do nothing
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KCYN);
|
||||||
|
fprintf (stderr, "\n P25 MBF Payload \n ");
|
||||||
|
for (i = 0; i < 36; i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", mpdu_byte[i]);
|
||||||
|
if (i == 11 || i == 23) fprintf (stderr, "\n ");
|
||||||
|
|
||||||
|
}
|
||||||
|
fprintf (stderr, " CRC EXT %08X CMP %08X", CRCExtracted, CRCComputed);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
fprintf (stderr, "\n ");
|
||||||
|
|
||||||
|
//Header
|
||||||
|
if (err[0] != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KRED);
|
||||||
|
fprintf (stderr, " (HDR CRC16 ERR)");
|
||||||
|
fprintf (stderr, "%s",KCYN);
|
||||||
|
}
|
||||||
|
//Completed MBF
|
||||||
|
if (err[1] != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KRED);
|
||||||
|
fprintf (stderr, " (MBF CRC32 ERR)");
|
||||||
|
fprintf (stderr, "%s",KCYN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
@@ -33,4 +33,13 @@ processTDU (dsd_opts* opts, dsd_state* state)
|
|||||||
status = getDibit (opts, state) + '0';
|
status = getDibit (opts, state) + '0';
|
||||||
// TODO: do something useful with the status bits...
|
// TODO: do something useful with the status bits...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//reset some strings -- since its a tdu, blank out any call strings, only want during actual call
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
|
||||||
|
//zero out on termination
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
state->payload_miP = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -390,11 +390,65 @@ processTDULC (dsd_opts* opts, dsd_state* state)
|
|||||||
lcinfo[53] = dodeca_data[0][ 9] + '0';
|
lcinfo[53] = dodeca_data[0][ 9] + '0';
|
||||||
lcinfo[54] = dodeca_data[0][10] + '0';
|
lcinfo[54] = dodeca_data[0][10] + '0';
|
||||||
lcinfo[55] = dodeca_data[0][11] + '0';
|
lcinfo[55] = dodeca_data[0][11] + '0';
|
||||||
/*
|
|
||||||
if (state->carrier == 1) //only update when carrier is present, otherwise, garbage values may be collected
|
int j;
|
||||||
|
uint8_t LCW_bytes[9];
|
||||||
|
uint8_t LCW_bits[72];
|
||||||
|
memset (LCW_bytes, 0, sizeof(LCW_bytes));
|
||||||
|
memset (LCW_bits, 0, sizeof(LCW_bits));
|
||||||
|
|
||||||
|
//load array above into byte form first
|
||||||
|
LCW_bytes[0] = (uint8_t) ConvertBitIntoBytes(&lcformat[0], 8);
|
||||||
|
LCW_bytes[1] = (uint8_t) ConvertBitIntoBytes(&mfid[0], 8);
|
||||||
|
for (i = 0; i < 7; i++) LCW_bytes[i+2] = (uint8_t) ConvertBitIntoBytes(&lcinfo[i*8], 8);
|
||||||
|
|
||||||
|
//load as bit array next (easier to process data in bit form)
|
||||||
|
for(i = 0, j = 0; i < 9; i++, j+=8)
|
||||||
{
|
{
|
||||||
state->payload_mfid = strtol (mfid, NULL, 2); //wonder if this is accurate info or not
|
LCW_bits[j + 0] = (LCW_bytes[i] >> 7) & 0x01;
|
||||||
|
LCW_bits[j + 1] = (LCW_bytes[i] >> 6) & 0x01;
|
||||||
|
LCW_bits[j + 2] = (LCW_bytes[i] >> 5) & 0x01;
|
||||||
|
LCW_bits[j + 3] = (LCW_bytes[i] >> 4) & 0x01;
|
||||||
|
LCW_bits[j + 4] = (LCW_bytes[i] >> 3) & 0x01;
|
||||||
|
LCW_bits[j + 5] = (LCW_bytes[i] >> 2) & 0x01;
|
||||||
|
LCW_bits[j + 6] = (LCW_bytes[i] >> 1) & 0x01;
|
||||||
|
LCW_bits[j + 7] = (LCW_bytes[i] >> 0) & 0x01;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
//send to new P25 LCW function
|
||||||
|
if (irrecoverable_errors == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
p25_lcw (opts, state, LCW_bits, 0);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KRED);
|
||||||
|
fprintf (stderr, " LCW FEC ERR ");
|
||||||
|
fprintf (stderr, "%s\n", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KCYN);
|
||||||
|
fprintf (stderr, " P25 LCW Payload ");
|
||||||
|
for (i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", LCW_bytes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fprintf (stderr, "%s\n", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset some strings -- since its a tdu, blank out any call strings, only want during actual call
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
|
||||||
|
//zero out on termination
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
state->payload_miP = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,40 @@
|
|||||||
*-----------------------------------------------------------------------------*/
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
void processTSBK(dsd_opts * opts, dsd_state * state)
|
void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//reset some strings when returning from a call in case they didn't get zipped already
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
|
||||||
|
//clear stale Active Channel messages here
|
||||||
|
if ( (time(NULL) - state->last_active_time) > 3 )
|
||||||
|
{
|
||||||
|
memset (state->active_channel, 0, sizeof(state->active_channel));
|
||||||
|
}
|
||||||
|
|
||||||
int tsbkbit[196] = {0}; //tsbk bit array, 196 trellis encoded bits
|
int tsbkbit[196]; //tsbk bit array, 196 trellis encoded bits
|
||||||
int tsbk_dibit[98] = {0};
|
int tsbk_dibit[98];
|
||||||
|
|
||||||
|
memset (tsbkbit, 0, sizeof(tsbkbit));
|
||||||
|
memset (tsbk_dibit, 0, sizeof(tsbk_dibit));
|
||||||
|
|
||||||
int dibit = 0;
|
int dibit = 0;
|
||||||
uint8_t tsbk_byte[12] = {0}; //12 byte return from bd_bridge (block_deinterleave)
|
|
||||||
unsigned long long int PDU[24] = {0}; //24 byte PDU to send to the tsbk_byte vPDU handler, should be same formats (mostly)
|
uint8_t tsbk_byte[12]; //12 byte return from bd_bridge (block_deinterleave)
|
||||||
int tsbk_decoded_bits[190] = {0}; //decoded bits from tsbk_bytes for sending to crc16_lb_bridge
|
memset (tsbk_byte, 0, sizeof(tsbk_byte));
|
||||||
|
|
||||||
|
unsigned long long int PDU[24]; //24 byte PDU to send to the tsbk_byte vPDU handler, should be same formats (mostly)
|
||||||
|
memset (PDU, 0, sizeof(PDU));
|
||||||
|
|
||||||
|
int tsbk_decoded_bits[190]; //decoded bits from tsbk_bytes for sending to crc16_lb_bridge
|
||||||
|
memset (tsbk_decoded_bits, 0, sizeof(tsbk_decoded_bits));
|
||||||
|
|
||||||
|
int flushing_bits[196]; //for flushing the trellis state machine
|
||||||
|
memset (flushing_bits, 0, sizeof(flushing_bits));
|
||||||
|
|
||||||
int i, j, k, b, x, y;
|
int i, j, k, b, x, y;
|
||||||
int ec = -2; //error value returned from (block_deinterleave)
|
int ec = -2; //error value returned from (block_deinterleave)
|
||||||
int err = -2; //error value returned from crc16_lb_bridge
|
int err = -2; //error value returned from crc16_lb_bridge
|
||||||
@@ -24,11 +48,7 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
int protectbit = 0;
|
int protectbit = 0;
|
||||||
int MFID = 0xFF; //Manufacturer ID - Might be beneficial to NOT send anything but standard 0x00 or 0x01 messages
|
int MFID = 0xFF; //Manufacturer ID - Might be beneficial to NOT send anything but standard 0x00 or 0x01 messages
|
||||||
|
|
||||||
//Update: Identified Bug in skipdibit incrememnt causing many block deinterleave/crc failures
|
|
||||||
//with correct values set, now we are decoding many more TSBKs
|
|
||||||
//TODO: Don't Print TSBKs/vPDUs unless verbosity levels set higher
|
//TODO: Don't Print TSBKs/vPDUs unless verbosity levels set higher
|
||||||
//TSBKs are always single block, but up to three can be transmitted in a row,
|
|
||||||
//Multi-Block PDUs are sent on a different DUID - 0xC (not handled currently)
|
|
||||||
|
|
||||||
//collect three reps of 101 dibits (98 valid dibits with status dibits interlaced)
|
//collect three reps of 101 dibits (98 valid dibits with status dibits interlaced)
|
||||||
for (j = 0; j < 3; j++)
|
for (j = 0; j < 3; j++)
|
||||||
@@ -49,11 +69,19 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
if (i+(j*101) == skipdibit)
|
if (i+(j*101) == skipdibit)
|
||||||
{
|
{
|
||||||
skipdibit += 36; //was set to 35, this was the bug
|
skipdibit += 36;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//flushing the trellis state machine
|
||||||
|
if (j == 0)
|
||||||
|
{
|
||||||
|
bd_bridge(flushing_bits, tsbk_byte);
|
||||||
|
//reset tsbk_byte afterwards
|
||||||
|
memset (tsbk_byte, 0, sizeof(tsbk_byte));
|
||||||
|
}
|
||||||
|
|
||||||
//send tsbkbit to block_deinterleave and return tsbk_byte
|
//send tsbkbit to block_deinterleave and return tsbk_byte
|
||||||
ec = bd_bridge(tsbkbit, tsbk_byte);
|
ec = bd_bridge(tsbkbit, tsbk_byte);
|
||||||
|
|
||||||
@@ -84,7 +112,7 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
tsbk_byte[i] = byte;
|
tsbk_byte[i] = byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
//convert tsbk_byte to PDU and send to vPDU handler
|
//convert tsbk_byte to vPDU and send to vPDU handler
|
||||||
//...may or may not be entirely compatible,
|
//...may or may not be entirely compatible,
|
||||||
MFID = tsbk_byte[1];
|
MFID = tsbk_byte[1];
|
||||||
PDU[0] = 0x07; //P25p1 TSBK Duid 0x07
|
PDU[0] = 0x07; //P25p1 TSBK Duid 0x07
|
||||||
@@ -106,7 +134,10 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
protectbit = (tsbk_byte[0] >> 6) & 0x1;
|
protectbit = (tsbk_byte[0] >> 6) & 0x1;
|
||||||
|
|
||||||
//Don't run NET_STS out of this, or will set wrong NAC/CC //&& PDU[1] != 0x49 && PDU[1] != 0x45
|
//Don't run NET_STS out of this, or will set wrong NAC/CC //&& PDU[1] != 0x49 && PDU[1] != 0x45
|
||||||
//0x49 is telephone grant, 0x46 Unit to Unit Channel Answer Request (seems bogus)
|
//0x49 is telephone grant, 0x46 Unit to Unit Channel Answer Request (seems bogus) (mfid 90)
|
||||||
|
// if (MFID < 0x2 && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
||||||
|
//running with A4 and 90 causing some false positives when sending to vPDU, issuing bad call grants to nowhere!
|
||||||
|
// if ( (MFID < 0x2 || MFID == 0xA4 || MFID == 0x90) && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
||||||
if (MFID < 0x2 && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
if (MFID < 0x2 && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s",KYEL);
|
fprintf (stderr, "%s",KYEL);
|
||||||
@@ -115,9 +146,9 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//set our WACN and SYSID here now that we have valid ec and crc/checksum
|
//set our WACN and SYSID here now that we have valid ec and crc/checksum
|
||||||
if (protectbit == 0 && err == 0 && ec == 0 && (tsbk_byte[0] & 0x3F) == 0x3B)
|
else if (protectbit == 0 && err == 0 && ec == 0 && (tsbk_byte[0] & 0x3F) == 0x3B)
|
||||||
{
|
{
|
||||||
long int wacn = (tsbk_byte[3] << 12) | (tsbk_byte[4] << 4) | (tsbk_byte[5] >> 4);;
|
long int wacn = (tsbk_byte[3] << 12) | (tsbk_byte[4] << 4) | (tsbk_byte[5] >> 4);
|
||||||
int sysid = ((tsbk_byte[5] & 0xF) << 8) | tsbk_byte[6];
|
int sysid = ((tsbk_byte[5] & 0xF) << 8) | tsbk_byte[6];
|
||||||
int channel = (tsbk_byte[7] << 8) | tsbk_byte[8];
|
int channel = (tsbk_byte[7] << 8) | tsbk_byte[8];
|
||||||
fprintf (stderr, "%s",KYEL);
|
fprintf (stderr, "%s",KYEL);
|
||||||
@@ -143,16 +174,43 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s",KCYN);
|
fprintf (stderr, "%s",KCYN);
|
||||||
fprintf (stderr, "\n P25 PDU Payload ");
|
fprintf (stderr, "\n P25 PDU Payload #%d ", j);
|
||||||
for (i = 0; i < 12; i++)
|
for (i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "[%02X]", tsbk_byte[i]);
|
fprintf (stderr, "[%02X]", tsbk_byte[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KCYN);
|
||||||
|
fprintf (stderr, "\n P25 PDU Payload #%d ", j+1);
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", tsbk_byte[i]);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n MFID %02X Protected: %d Last Block: %d", MFID, protectbit, (tsbk_byte[0] >> 7) );
|
||||||
|
|
||||||
|
if (ec != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KRED);
|
||||||
|
fprintf (stderr, " (FEC ERR)");
|
||||||
|
}
|
||||||
|
else if (err != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//reset for next rep
|
//reset for next rep
|
||||||
ec = -2;
|
ec = -2;
|
||||||
err = -2;
|
err = -2;
|
||||||
@@ -165,5 +223,7 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
j = 4; //set j to break the loop early
|
j = 4; //set j to break the loop early
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ int dibit = 0;
|
|||||||
int vc_counter = 0;
|
int vc_counter = 0;
|
||||||
int framing_counter = 0;
|
int framing_counter = 0;
|
||||||
|
|
||||||
unsigned long long int isch = 0;
|
uint64_t isch = 0;
|
||||||
unsigned long long int isch_decoded = 0;
|
int isch_decoded = -1;
|
||||||
int p2_duid[8] = {0};
|
int p2_duid[8] = {0};
|
||||||
int16_t duid_decoded = 0;
|
int16_t duid_decoded = 0;
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ void process_ISCH (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
isch_decoded = isch_lookup(isch);
|
isch_decoded = isch_lookup(isch);
|
||||||
|
|
||||||
if (isch_decoded > 0)
|
if (isch_decoded > -1)
|
||||||
{
|
{
|
||||||
int uf_count = isch_decoded & 0x3;
|
int uf_count = isch_decoded & 0x3;
|
||||||
int free = (isch_decoded >> 2) & 0x1;
|
int free = (isch_decoded >> 2) & 0x1;
|
||||||
@@ -411,6 +411,7 @@ void process_ISCH (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isch_decoded = -1; //reset to bad value after running
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,6 +475,16 @@ void process_4V (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AERO_BUILD //FUN FACT: OSS stutters only on Cygwin, using padsp in linux, it actually opens two virtual /dev/dsp audio streams for output
|
||||||
|
//Cygwin OSS Slot Preference Pre-emption shim
|
||||||
|
if (opts->audio_out_type == 5)
|
||||||
|
{
|
||||||
|
//set to 21 for MBE OSS shim to preempt audio
|
||||||
|
if (state->currentslot == 0) state->dmrburstL = 21;
|
||||||
|
else state->dmrburstR = 21;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr1, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr1, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
@@ -541,6 +552,7 @@ void process_ESS (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr, " ALG ID 0x%02X", state->payload_algid);
|
fprintf (stderr, " ALG ID 0x%02X", state->payload_algid);
|
||||||
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyid);
|
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyid);
|
||||||
fprintf (stderr, " MI 0x%016llX", state->payload_miP);
|
fprintf (stderr, " MI 0x%016llX", state->payload_miP);
|
||||||
|
if (state->R != 0 && state->payload_algid == 0xAA) fprintf (stderr, " Key 0x%010llX", state->R);
|
||||||
fprintf (stderr, " ESSB");
|
fprintf (stderr, " ESSB");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,6 +568,7 @@ void process_ESS (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr, " ALG ID 0x%02X", state->payload_algidR);
|
fprintf (stderr, " ALG ID 0x%02X", state->payload_algidR);
|
||||||
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyidR);
|
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyidR);
|
||||||
fprintf (stderr, " MI 0x%016llX", state->payload_miN);
|
fprintf (stderr, " MI 0x%016llX", state->payload_miN);
|
||||||
|
if (state->RR != 0 && state->payload_algidR == 0xAA) fprintf (stderr, " Key 0x%010llX", state->RR);
|
||||||
fprintf (stderr, " ESSB");
|
fprintf (stderr, " ESSB");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,7 +576,11 @@ void process_ESS (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
if (ec == -1 || ec >= 15)
|
if (ec == -1 || ec >= 15)
|
||||||
{
|
{
|
||||||
//ESS R-S Failure
|
//ESS R-S Failure -- run LFSR on current MI if applicable
|
||||||
|
if (state->currentslot == 0 && state->payload_algid != 0x80 && state->payload_keyid != 0 && state->payload_miP != 0)
|
||||||
|
LFSRP(state);
|
||||||
|
if (state->currentslot == 1 && state->payload_algidR != 0x80 && state->payload_keyidR != 0 && state->payload_miN != 0)
|
||||||
|
LFSRP(state);
|
||||||
}
|
}
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
@@ -625,11 +642,28 @@ void process_2V (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AERO_BUILD //FUN FACT: OSS stutters only on Cygwin, using padsp in linux, it actually opens two virtual /dev/dsp audio streams for output
|
||||||
|
//Cygwin OSS Slot Preference Pre-emption shim
|
||||||
|
if (opts->audio_out_type == 5)
|
||||||
|
{
|
||||||
|
//set to 21 for MBE OSS shim to preempt audio
|
||||||
|
if (state->currentslot == 0) state->dmrburstL = 21;
|
||||||
|
else state->dmrburstR = 21;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr1, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr1, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
|
||||||
process_ESS(opts, state);
|
process_ESS(opts, state);
|
||||||
|
|
||||||
|
//reset drop bytes after a 2V
|
||||||
|
if (state->currentslot == 0 && state->payload_algid == 0xAA)
|
||||||
|
state->dropL = 256;
|
||||||
|
|
||||||
|
if (state->currentslot == 1 && state->payload_algidR == 0xAA)
|
||||||
|
state->dropR = 256;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//P2 Data Unit ID
|
//P2 Data Unit ID
|
||||||
@@ -685,19 +719,41 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->currentslot == 0 && duid_decoded != 3 && duid_decoded != 12)
|
if (state->currentslot == 0 && duid_decoded != 3 && duid_decoded != 12)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "VCH 0 ");
|
fprintf (stderr, "VCH 0 ");
|
||||||
|
//open MBEout file - slot 1 - USE WITH CAUTION on Phase 2! Consider using a symbol capture bin instead!
|
||||||
|
if (duid_decoded == 0 || duid_decoded == 6) //4V or 2V (voice)
|
||||||
|
{
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (state->currentslot == 1 && duid_decoded != 3 && duid_decoded != 12)
|
else if (state->currentslot == 1 && duid_decoded != 3 && duid_decoded != 12)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "VCH 1 ");
|
fprintf (stderr, "VCH 1 ");
|
||||||
|
//open MBEout file - slot 2 - USE WITH CAUTION on Phase 2! Consider using a symbol capture bin instead!
|
||||||
|
if (duid_decoded == 0 || duid_decoded == 6) //4V or 2V (voice)
|
||||||
|
{
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else fprintf (stderr, "VCH S ");
|
else fprintf (stderr, "VCH S ");
|
||||||
|
|
||||||
|
//check to see when last voice activity occurred in order to allow tuning on phase 2
|
||||||
|
//mac_signal or mac_idle when no more voice activity on current channel
|
||||||
|
//this is primarily a fix for TDMA control channels that carry voice (Duke P25)
|
||||||
|
//but may also allow for chain tuning without returning to the control channel <--may be problematic since we can assign a p25_cc_freq from the pdu
|
||||||
|
if (duid_decoded == 13 && opts->p25_is_tuned == 1 && ((time(NULL) - state->last_vc_sync_time) > opts->trunk_hangtime) ) //version for MAC_SIGNAL only, no idle
|
||||||
|
{
|
||||||
|
opts->p25_is_tuned = 0;
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||||
|
memset (state->active_channel, 0, sizeof (state->active_channel)); //zero out here? I think this will be fine
|
||||||
|
}
|
||||||
|
|
||||||
if (duid_decoded == 0)
|
if (duid_decoded == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, " 4V %d", state->fourv_counter[state->currentslot]+1);
|
fprintf (stderr, " 4V %d", state->fourv_counter[state->currentslot]+1);
|
||||||
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0 &&
|
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0 &&
|
||||||
state->p2_wacn != 0xFFFFF && state->p2_cc != 0xFFF && state->p2_sysid != 0xFFF)
|
state->p2_wacn != 0xFFFFF && state->p2_cc != 0xFFF && state->p2_sysid != 0xFFF)
|
||||||
{
|
{
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
process_4V (opts, state);
|
process_4V (opts, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -707,6 +763,7 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0 &&
|
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0 &&
|
||||||
state->p2_wacn != 0xFFFFF && state->p2_cc != 0xFFF && state->p2_sysid != 0xFFF)
|
state->p2_wacn != 0xFFFFF && state->p2_cc != 0xFFF && state->p2_sysid != 0xFFF)
|
||||||
{
|
{
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
process_2V (opts, state);
|
process_2V (opts, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -804,22 +861,21 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state)
|
|||||||
void processP2 (dsd_opts * opts, dsd_state * state)
|
void processP2 (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
state->dmr_stereo = 1;
|
state->dmr_stereo = 1;
|
||||||
//state->currentslot = 1;
|
|
||||||
|
|
||||||
p2_dibit_buffer (opts, state);
|
p2_dibit_buffer (opts, state);
|
||||||
|
|
||||||
//look at our ISCH values and determine location in superframe before running frame scramble
|
//look at our ISCH values and determine location in superframe before running frame scramble
|
||||||
for (framing_counter = 0; framing_counter < 4; framing_counter++) //12
|
for (framing_counter = 0; framing_counter < 4; framing_counter++)
|
||||||
{
|
{
|
||||||
process_ISCH (opts, state); //run ISCH in here so we know when to start descramble offset
|
//run ISCH in here so we know when to start descramble offset
|
||||||
|
process_ISCH (opts, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
//set initial current slot depending on offset value
|
//set initial current slot depending on offset value
|
||||||
if (state->p2_scramble_offset % 2)
|
if (state->p2_scramble_offset % 2)
|
||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 1;
|
||||||
}
|
}
|
||||||
else state->currentslot = 1;
|
else state->currentslot = 0;
|
||||||
|
|
||||||
//frame_scramble runs lfsr and creates an array of unscrambled bits to pull from
|
//frame_scramble runs lfsr and creates an array of unscrambled bits to pull from
|
||||||
process_Frame_Scramble (opts, state);
|
process_Frame_Scramble (opts, state);
|
||||||
@@ -827,6 +883,7 @@ void processP2 (dsd_opts * opts, dsd_state * state)
|
|||||||
//process DUID will run through all collected frames and handle them appropriately
|
//process DUID will run through all collected frames and handle them appropriately
|
||||||
process_P2_DUID (opts, state);
|
process_P2_DUID (opts, state);
|
||||||
state->dmr_stereo = 0;
|
state->dmr_stereo = 0;
|
||||||
state->p2_is_lcch = 0;
|
state->p2_is_lcch = 0;
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
528
src/p25p2_vpdu.c
528
src/p25p2_vpdu.c
@@ -84,7 +84,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
//MFID90 Voice Grants, A3, A4, and A5
|
//MFID90 Voice Grants, A3, A4, and A5 <--I bet A4 here was triggering a phantom call when TSBK sent PDUs here
|
||||||
//MFID90 Group Regroup Channel Grant - Implicit
|
//MFID90 Group Regroup Channel Grant - Implicit
|
||||||
if (MAC[1+len_a] == 0xA3 && MAC[2+len_a] == 0x90)
|
if (MAC[1+len_a] == 0xA3 && MAC[2+len_a] == 0x90)
|
||||||
{
|
{
|
||||||
@@ -96,6 +96,10 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, "\n CHAN [%04X] Group [%d][%04X]", channel, sgroup, sgroup);
|
fprintf (stderr, "\n CHAN [%04X] Group [%d][%04X]", channel, sgroup, sgroup);
|
||||||
freq = process_channel_to_freq (opts, state, channel);
|
freq = process_channel_to_freq (opts, state, channel);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "MFID90 Active Ch: %04X SG: %d; ", channel, sgroup);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
{
|
{
|
||||||
if (state->group_array[i].groupNumber == sgroup)
|
if (state->group_array[i].groupNumber == sgroup)
|
||||||
@@ -105,36 +109,43 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
//tune if tuning available
|
//tune if tuning available
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
||||||
{
|
{
|
||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
if (opts->mod_qpsk == 1)
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[channel >> 12];
|
if (state->p25_chan_tdma[channel >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//rigctl
|
//rigctl
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
{
|
{
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, freq);
|
SetFreq(opts->rigctl_sockfd, freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, freq);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,6 +172,10 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, "\n CHAN [%04X] Group [%d][%04X]", channel, sgroup, sgroup);
|
fprintf (stderr, "\n CHAN [%04X] Group [%d][%04X]", channel, sgroup, sgroup);
|
||||||
freq = process_channel_to_freq (opts, state, channel);
|
freq = process_channel_to_freq (opts, state, channel);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "MFID90 Active Ch: %04X SG: %d ", channel, sgroup);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
{
|
{
|
||||||
if (state->group_array[i].groupNumber == sgroup)
|
if (state->group_array[i].groupNumber == sgroup)
|
||||||
@@ -170,21 +185,23 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
//tune if tuning available
|
//tune if tuning available
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
||||||
{
|
{
|
||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
if (opts->mod_qpsk == 1)
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[channel >> 12];
|
if (state->p25_chan_tdma[channel >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
@@ -192,14 +209,18 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, freq);
|
SetFreq(opts->rigctl_sockfd, freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, freq);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,6 +254,13 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
||||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
freq2 = process_channel_to_freq (opts, state, channel2);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "MFID90 Active Ch: %04X SG: %d; Ch: %04X SG: %d; ", channel1, group1, channel2, group2);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
//monstrocity below should get us evaluating and tuning groups...hopefully, will be first come first served though, no priority
|
//monstrocity below should get us evaluating and tuning groups...hopefully, will be first come first served though, no priority
|
||||||
//see how many loops we need to run on when to tune if first group is blocked
|
//see how many loops we need to run on when to tune if first group is blocked
|
||||||
int loop = 1;
|
int loop = 1;
|
||||||
@@ -274,16 +302,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
if (opts->mod_qpsk == 1)
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
if (state->p25_chan_tdma[tunable_chan >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//rigctl
|
//rigctl
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
{
|
{
|
||||||
@@ -291,17 +319,21 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
||||||
//probably best to only set these when really tuning
|
//probably best to only set these when really tuning
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
j = 8; //break loop
|
j = 8; //break loop
|
||||||
|
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, tunable_freq);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, tunable_freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
j = 8; //break loop
|
j = 8; //break loop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,10 +362,27 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
int source = (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | MAC[9+len_a];
|
int source = (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | MAC[9+len_a];
|
||||||
long int freq = 0;
|
long int freq = 0;
|
||||||
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update");
|
fprintf (stderr, "\n");
|
||||||
|
if (svc & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (svc & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (svc & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (svc & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, " Group Voice Channel Grant Update");
|
||||||
fprintf (stderr, "\n SVC [%02X] CHAN [%04X] Group [%d] Source [%d]", svc, channel, group, source);
|
fprintf (stderr, "\n SVC [%02X] CHAN [%04X] Group [%d] Source [%d]", svc, channel, group, source);
|
||||||
freq = process_channel_to_freq (opts, state, channel);
|
freq = process_channel_to_freq (opts, state, channel);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d; ", channel, group);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
{
|
{
|
||||||
if (state->group_array[i].groupNumber == group)
|
if (state->group_array[i].groupNumber == group)
|
||||||
@@ -343,22 +392,28 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//Skip tuning encrypted calls if enc calls are disabled
|
||||||
|
if ( (svc & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
//tune if tuning available
|
//tune if tuning available
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
||||||
{
|
{
|
||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
if (opts->mod_qpsk == 1)
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[channel >> 12];
|
if (state->p25_chan_tdma[channel >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//rigctl
|
//rigctl
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
{
|
{
|
||||||
@@ -366,13 +421,17 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
SetFreq(opts->rigctl_sockfd, freq);
|
SetFreq(opts->rigctl_sockfd, freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, freq);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,7 +450,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Unit-to-Unit Voice Service Channel Grant (UU_V_CH_GRANT), or Grant Update (same format)
|
//Unit-to-Unit Voice Service Channel Grant (UU_V_CH_GRANT), or Grant Update (same format)
|
||||||
if (MAC[1+len_a] == 0x44 || MAC[1+len_a] == 0x46)
|
if (MAC[1+len_a] == 0x44 || MAC[1+len_a] == 0x46) //double check these opcodes
|
||||||
{
|
{
|
||||||
int channel = (MAC[2+len_a] << 8) | MAC[3+len_a];
|
int channel = (MAC[2+len_a] << 8) | MAC[3+len_a];
|
||||||
int target = (MAC[4+len_a] << 16) | (MAC[5+len_a] << 8) | MAC[6+len_a];
|
int target = (MAC[4+len_a] << 16) | (MAC[5+len_a] << 8) | MAC[6+len_a];
|
||||||
@@ -403,9 +462,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, "\n CHAN [%04X] Source [%d] Target [%d]", channel, source, target);
|
fprintf (stderr, "\n CHAN [%04X] Source [%d] Target [%d]", channel, source, target);
|
||||||
freq = process_channel_to_freq (opts, state, channel);
|
freq = process_channel_to_freq (opts, state, channel);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "Active Ch: %04X TGT: %d; ", channel, target);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
//Skip tuning private calls if private calls is disabled
|
//Skip tuning private calls if private calls is disabled
|
||||||
if (opts->trunk_tune_private_calls == 0) goto SKIPCALL;
|
if (opts->trunk_tune_private_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//Skip tuning encrypted calls if enc calls are disabled -- abb formats do not carry svc bits :(
|
||||||
|
if (opts->trunk_tune_enc_calls == 0) goto SKIPCALL; //enable, or disable?
|
||||||
|
|
||||||
//unit to unit needs work, may fail under certain conditions (first blocked, second allowed, etc) (labels should still work though)
|
//unit to unit needs work, may fail under certain conditions (first blocked, second allowed, etc) (labels should still work though)
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
{
|
{
|
||||||
@@ -422,30 +488,34 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
if (opts->mod_qpsk == 1)
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[channel >> 12];
|
if (state->p25_chan_tdma[channel >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//rigctl
|
//rigctl
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
{
|
{
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, freq);
|
SetFreq(opts->rigctl_sockfd, freq);
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, freq);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, freq);
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -460,7 +530,6 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SKIPCALL: ; //do nothing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Group Voice Channel Grant Update Multiple - Explicit
|
//Group Voice Channel Grant Update Multiple - Explicit
|
||||||
@@ -478,13 +547,45 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
long int freq1r = 0;
|
long int freq1r = 0;
|
||||||
long int freq2t = 0;
|
long int freq2t = 0;
|
||||||
long int freq2r = 0;
|
long int freq2r = 0;
|
||||||
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update Multiple - Explicit");
|
fprintf (stderr, "\n Group Voice Channel Grant Update Multiple - Explicit");
|
||||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc1, channelt1, channelr1, group1, group1);
|
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc1, channelt1, channelr1, group1, group1);
|
||||||
|
if (svc1 & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (svc1 & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (svc1 & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (svc1 & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (svc1 & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", svc1 & 0x7); //call priority
|
||||||
|
}
|
||||||
freq1t = process_channel_to_freq (opts, state, channelt1);
|
freq1t = process_channel_to_freq (opts, state, channelt1);
|
||||||
freq1r = process_channel_to_freq (opts, state, channelr1);
|
if (channelr1 != 0 && channelr1 != 0xFFFF) freq1r = process_channel_to_freq (opts, state, channelr1);
|
||||||
|
|
||||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc2, channelt2, channelr2, group2, group2);
|
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc2, channelt2, channelr2, group2, group2);
|
||||||
|
if (svc2 & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (svc2 & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (svc2 & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (svc2 & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (svc2 & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", svc2 & 0x7); //call priority
|
||||||
|
}
|
||||||
freq1t = process_channel_to_freq (opts, state, channelt2);
|
freq1t = process_channel_to_freq (opts, state, channelt2);
|
||||||
freq1r = process_channel_to_freq (opts, state, channelr2);
|
if (channelr2 != 0 && channelr2 != 0xFFFF) freq1r = process_channel_to_freq (opts, state, channelr2);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d; Ch: %04X TG: %d; ", channelt1, group1, channelt2, group2);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//Skip tuning encrypted calls if enc calls are disabled
|
||||||
|
if ( (svc1 & 0x40) && (svc2 & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
int loop = 1;
|
int loop = 1;
|
||||||
if (channelt2 == channelt2) loop = 1;
|
if (channelt2 == channelt2) loop = 1;
|
||||||
@@ -496,6 +597,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
|
|
||||||
for (int j = 0; j < loop; j++)
|
for (int j = 0; j < loop; j++)
|
||||||
{
|
{
|
||||||
|
//test svc opts for enc to tune or skip
|
||||||
|
if (j == 0)
|
||||||
|
{
|
||||||
|
if ( (svc1 & 0x40) && opts->trunk_tune_enc_calls == 0) j++; //skip to next
|
||||||
|
}
|
||||||
|
if (j == 1)
|
||||||
|
{
|
||||||
|
if (( svc2 & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL; //skip to end
|
||||||
|
}
|
||||||
|
|
||||||
//assign our internal variables for check down on if to tune one freq/group or not
|
//assign our internal variables for check down on if to tune one freq/group or not
|
||||||
if (j == 0)
|
if (j == 0)
|
||||||
{
|
{
|
||||||
@@ -524,15 +635,14 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
if (opts->mod_qpsk == 1)
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
if (state->p25_chan_tdma[tunable_chan >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
@@ -540,16 +650,20 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
j = 8; //break loop
|
j = 8; //break loop
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, tunable_freq);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, tunable_freq);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
j = 8; //break loop
|
j = 8; //break loop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -567,7 +681,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Group Voice Channel Grant Update Multiple - Implicit
|
//Group Voice Channel Grant Update Multiple - Implicit
|
||||||
if (MAC[1+len_a] == 0x05) //wonder if this is MAC_SIGNAL only? Too long for a TSBK
|
if (MAC[1+len_a] == 0x05)
|
||||||
{
|
{
|
||||||
int so1 = MAC[2+len_a];
|
int so1 = MAC[2+len_a];
|
||||||
int channel1 = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
int channel1 = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
||||||
@@ -584,12 +698,54 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update Multiple - Implicit");
|
fprintf (stderr, "\n Group Voice Channel Grant Update Multiple - Implicit");
|
||||||
fprintf (stderr, "\n Channel 1 [%04X] Group 1 [%d][%04X]", channel1, group1, group1);
|
fprintf (stderr, "\n Channel 1 [%04X] Group 1 [%d][%04X]", channel1, group1, group1);
|
||||||
|
if (so1 & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (so1 & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (so1 & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (so1 & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (so1 & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", so1 & 0x7); //call priority
|
||||||
|
}
|
||||||
freq1 = process_channel_to_freq (opts, state, channel1);
|
freq1 = process_channel_to_freq (opts, state, channel1);
|
||||||
|
|
||||||
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
||||||
|
if (so2 & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (so2 & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (so2 & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (so2 & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (so2 & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", so2 & 0x7); //call priority
|
||||||
|
}
|
||||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
freq2 = process_channel_to_freq (opts, state, channel2);
|
||||||
|
|
||||||
fprintf (stderr, "\n Channel 3 [%04X] Group 3 [%d][%04X]", channel3, group3, group3);
|
fprintf (stderr, "\n Channel 3 [%04X] Group 3 [%d][%04X]", channel3, group3, group3);
|
||||||
|
if (so3 & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (so3 & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (so3 & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (so3 & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (so3 & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", so3 & 0x7); //call priority
|
||||||
|
}
|
||||||
freq3 = process_channel_to_freq (opts, state, channel3);
|
freq3 = process_channel_to_freq (opts, state, channel3);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d; Ch: %04X TG: %d; Ch: %04X TG: %d; ", channel1, group1, channel2, group2, channel3, group3);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//Skip tuning encrypted calls if enc calls are disabled
|
||||||
|
if ( (so1 & 0x40) && (so2 & 0x40) && (so3 & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
int loop = 3;
|
int loop = 3;
|
||||||
|
|
||||||
long int tunable_freq = 0;
|
long int tunable_freq = 0;
|
||||||
@@ -598,6 +754,20 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
|
|
||||||
for (int j = 0; j < loop; j++)
|
for (int j = 0; j < loop; j++)
|
||||||
{
|
{
|
||||||
|
//test svc opts for enc to tune or skip
|
||||||
|
if (j == 0)
|
||||||
|
{
|
||||||
|
if ( (so1 & 0x40) && opts->trunk_tune_enc_calls == 0) j++; //skip to next
|
||||||
|
}
|
||||||
|
if (j == 1)
|
||||||
|
{
|
||||||
|
if ( (so2 & 0x40) && opts->trunk_tune_enc_calls == 0) j++; //skip to next
|
||||||
|
}
|
||||||
|
if (j == 2)
|
||||||
|
{
|
||||||
|
if ( (so3 & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL; //skip to end
|
||||||
|
}
|
||||||
|
|
||||||
//assign our internal variables for check down on if to tune one freq/group or not
|
//assign our internal variables for check down on if to tune one freq/group or not
|
||||||
if (j == 0)
|
if (j == 0)
|
||||||
{
|
{
|
||||||
@@ -633,16 +803,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
if (opts->mod_qpsk == 1)
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
if (state->p25_chan_tdma[tunable_chan >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//rigctl
|
//rigctl
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
{
|
{
|
||||||
@@ -650,17 +820,21 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
||||||
//probably best to only set these when really tuning
|
//probably best to only set these when really tuning
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
j = 8; //break loop
|
j = 8; //break loop
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, tunable_freq);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, tunable_freq);
|
||||||
//probably best to only set these when really tuning
|
//probably best to only set these when really tuning
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
j = 8; //break loop
|
j = 8; //break loop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -693,6 +867,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
||||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
freq2 = process_channel_to_freq (opts, state, channel2);
|
||||||
|
|
||||||
|
//add active channel to string for ncurses display
|
||||||
|
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d; Ch: %04X TG: %d; ", channel1, group1, channel2, group2);
|
||||||
|
state->last_active_time = time(NULL);
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//Skip tuning encrypted calls if enc calls are disabled -- abb formats do not carry svc bits :(
|
||||||
|
if (opts->trunk_tune_enc_calls == 0) goto SKIPCALL; //enable, or disable?
|
||||||
|
|
||||||
int loop = 1;
|
int loop = 1;
|
||||||
if (channel1 == channel2) loop = 1;
|
if (channel1 == channel2) loop = 1;
|
||||||
else loop = 2;
|
else loop = 2;
|
||||||
@@ -732,16 +916,16 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
if (opts->mod_qpsk == 1)
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
if (state->p25_chan_tdma[tunable_chan >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//rigctl
|
//rigctl
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
{
|
{
|
||||||
@@ -750,16 +934,20 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
//probably best to only set these when really tuning
|
//probably best to only set these when really tuning
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
j = 8; //break loop
|
j = 8; //break loop
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, tunable_freq);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, tunable_freq);
|
||||||
//probably best to only set these when really tuning
|
//probably best to only set these when really tuning
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
j = 8; //break loop
|
j = 8; //break loop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -786,15 +974,30 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
long int freq1 = 0;
|
long int freq1 = 0;
|
||||||
long int freq2 = 0;
|
long int freq2 = 0;
|
||||||
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update - Explicit");
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
if (svc & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (svc & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (svc & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (svc & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
|
||||||
|
}
|
||||||
|
fprintf (stderr, " Group Voice Channel Grant Update - Explicit");
|
||||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc, channelt, channelr, group, group);
|
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc, channelt, channelr, group, group);
|
||||||
freq1 = process_channel_to_freq (opts, state, channelt);
|
freq1 = process_channel_to_freq (opts, state, channelt);
|
||||||
freq2 = process_channel_to_freq (opts, state, channelr);
|
if (channelr != 0 && channelr != 0xFFFF) freq2 = process_channel_to_freq (opts, state, channelr); //one system had this as channel 0xFFFF -- look up any particular meaning for that
|
||||||
if (slot == 0)
|
|
||||||
{
|
//don't set the tg here, one multiple grant updates, will mess up the TG value on current call
|
||||||
state->lasttg = group;
|
// if (slot == 0)
|
||||||
}
|
// {
|
||||||
else state->lasttgR = group;
|
// state->lasttg = group;
|
||||||
|
// }
|
||||||
|
// else state->lasttgR = group;
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
{
|
{
|
||||||
@@ -805,36 +1008,47 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Skip tuning group calls if group calls are disabled
|
||||||
|
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
|
//Skip tuning encrypted calls if enc calls are disabled
|
||||||
|
if ( (svc & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
|
||||||
|
|
||||||
//tune if tuning available
|
//tune if tuning available
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
||||||
{
|
{
|
||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
|
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
|
||||||
{
|
{
|
||||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
|
||||||
if (opts->mod_qpsk == 1)
|
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
|
||||||
|
if (1 == 1)
|
||||||
{
|
{
|
||||||
int spacing = state->p25_chan_spac[channelt >> 12];
|
if (state->p25_chan_tdma[channelt >> 12] == 1)
|
||||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
|
||||||
{
|
{
|
||||||
state->samplesPerSymbol = 8;
|
state->samplesPerSymbol = 8;
|
||||||
state->symbolCenter = 3;
|
state->symbolCenter = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//rigctl
|
//rigctl
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
{
|
{
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, freq1);
|
SetFreq(opts->rigctl_sockfd, freq1);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
}
|
}
|
||||||
//rtl_udp
|
//rtl
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, freq1);
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune (opts, freq1);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -892,6 +1106,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, "\n RFSS Status Broadcast - Implicit \n");
|
fprintf (stderr, "\n RFSS Status Broadcast - Implicit \n");
|
||||||
fprintf (stderr, " LRA [%02X] SYSID [%03X] RFSS ID [%02X] SITE ID [%02X] CHAN [%04X] SSC [%02X] ", lra, lsysid, rfssid, siteid, channel, sysclass);
|
fprintf (stderr, " LRA [%02X] SYSID [%03X] RFSS ID [%02X] SITE ID [%02X] CHAN [%04X] SSC [%02X] ", lra, lsysid, rfssid, siteid, channel, sysclass);
|
||||||
process_channel_to_freq (opts, state, channel);
|
process_channel_to_freq (opts, state, channel);
|
||||||
|
|
||||||
|
state->p2_siteid = siteid;
|
||||||
|
state->p2_rfssid = rfssid;
|
||||||
}
|
}
|
||||||
|
|
||||||
//RFSS Status Broadcast - Explicit
|
//RFSS Status Broadcast - Explicit
|
||||||
@@ -908,10 +1125,13 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, " LRA [%02X] SYSID [%03X] RFSS ID [%02X] SITE ID [%02X]\n CHAN-T [%04X] CHAN-R [%02X] SSC [%02X] ", lra, lsysid, rfssid, siteid, channelt, channelr, sysclass);
|
fprintf (stderr, " LRA [%02X] SYSID [%03X] RFSS ID [%02X] SITE ID [%02X]\n CHAN-T [%04X] CHAN-R [%02X] SSC [%02X] ", lra, lsysid, rfssid, siteid, channelt, channelr, sysclass);
|
||||||
process_channel_to_freq (opts, state, channelt);
|
process_channel_to_freq (opts, state, channelt);
|
||||||
process_channel_to_freq (opts, state, channelr);
|
process_channel_to_freq (opts, state, channelr);
|
||||||
|
|
||||||
|
state->p2_siteid = siteid;
|
||||||
|
state->p2_rfssid = rfssid;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Unit-to-Unit Answer Request (UU_ANS_REQ)
|
//Unit-to-Unit Answer Request (UU_ANS_REQ)
|
||||||
if (MAC[1+len_a] == 0x45)
|
if (MAC[1+len_a] == 0x45 && MAC[2+len_a] != 0x90)
|
||||||
{
|
{
|
||||||
int svc = MAC[2+len_a];
|
int svc = MAC[2+len_a];
|
||||||
int answer = MAC[3+len_a];
|
int answer = MAC[3+len_a];
|
||||||
@@ -923,17 +1143,17 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Telephone Interconnect Voice Channel Grant (TELE_INT_CH_GRANT)
|
//Telephone Interconnect Voice Channel Grant (TELE_INT_CH_GRANT) //look at PDU for this, see if this is tunable
|
||||||
if (MAC[1+len_a] == 0x48)
|
// if (MAC[1+len_a] == 0x48 && MAC[2+len_a] != 0x90)
|
||||||
{
|
// {
|
||||||
fprintf (stderr, "\n Telephone Interconnect Voice Channel Grant ");
|
// fprintf (stderr, "\n Telephone Interconnect Voice Channel Grant ");
|
||||||
}
|
// }
|
||||||
|
|
||||||
//Telephone Interconnect Voice Channel Grant Update (TELE_INT_CH_GRANT_UPDT)
|
// //Telephone Interconnect Voice Channel Grant Update (TELE_INT_CH_GRANT_UPDT)
|
||||||
if (MAC[1+len_a] == 0x49)
|
// if (MAC[1+len_a] == 0x49 && MAC[2+len_a] != 0x90)
|
||||||
{
|
// {
|
||||||
fprintf (stderr, "\n Telephone Interconnect Voice Channel Grant Update ");
|
// fprintf (stderr, "\n Telephone Interconnect Voice Channel Grant Update ");
|
||||||
}
|
// }
|
||||||
|
|
||||||
//Synchronization Broadcast (SYNC_BCST)
|
//Synchronization Broadcast (SYNC_BCST)
|
||||||
if (MAC[1+len_a] == 0x70)
|
if (MAC[1+len_a] == 0x70)
|
||||||
@@ -957,12 +1177,12 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
else offhour = ( (ltoff & 0b11111 ) / 2);
|
else offhour = ( (ltoff & 0b11111 ) / 2);
|
||||||
|
|
||||||
int seconds = slots / 135; //very rough estimation, but may be close enough for grins
|
int seconds = slots / 135; //very rough estimation, but may be close enough for grins
|
||||||
if (seconds > 60) seconds = 60; //sanity check for rounding error
|
if (seconds > 59) seconds = 59; //sanity check for rounding error
|
||||||
fprintf (stderr, " Date: 20%02d.%02d.%02d Time: %02d:%02d:%02d UTC\n",
|
fprintf (stderr, " Date: 20%02d.%02d.%02d Time: %02d:%02d:%02d UTC\n",
|
||||||
year, month, day, hour, min, seconds);
|
year, month, day, hour, min, seconds);
|
||||||
fprintf (stderr, " Local Time Offset: %.01f Hours;", offhour);
|
fprintf (stderr, " Local Time Offset: %.01f Hours;", offhour);
|
||||||
//if ist bit is set, then time on system may be considered invalid (i.e., no external time sync)
|
//if ist bit is set, then time on system may be considered invalid (i.e., no external time sync)
|
||||||
if (ist % 1)
|
if (ist == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, " Invalid System Time ");
|
fprintf (stderr, " Invalid System Time ");
|
||||||
}
|
}
|
||||||
@@ -970,18 +1190,31 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//identifier update VHF/UHF
|
//identifier update VHF/UHF
|
||||||
if (MAC[1+len_a] == 0x74)
|
if (MAC[1+len_a] == 0x74)
|
||||||
{
|
{
|
||||||
state->p25_chan_iden = MAC[2+len_a] >> 4;
|
state->p25_chan_iden = MAC[2+len_a] >> 4;
|
||||||
int iden = state->p25_chan_iden;
|
int iden = state->p25_chan_iden;
|
||||||
|
|
||||||
state->p25_chan_type[iden] = 1;
|
|
||||||
int bw_vu = (MAC[2+len_a] & 0xF);
|
int bw_vu = (MAC[2+len_a] & 0xF);
|
||||||
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
||||||
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
||||||
state->p25_base_freq[iden] = (MAC[6+len_a] << 24) | (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | (MAC[9+len_a] << 0);
|
state->p25_base_freq[iden] = (MAC[6+len_a] << 24) | (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | (MAC[9+len_a] << 0);
|
||||||
|
|
||||||
|
//this is causing more issues -- need a different way to check on this
|
||||||
|
// if (state->p25_chan_spac[iden] == 0x64) //tdma
|
||||||
|
// {
|
||||||
|
// state->p25_chan_type[iden] = 4; //
|
||||||
|
// state->p25_chan_tdma[iden] = 1; //
|
||||||
|
// }
|
||||||
|
// else //fdma
|
||||||
|
// {
|
||||||
|
// state->p25_chan_type[iden] = 1; //
|
||||||
|
// state->p25_chan_tdma[iden] = 0; //
|
||||||
|
// }
|
||||||
|
|
||||||
|
state->p25_chan_type[iden] = 1; //set as old values for now
|
||||||
|
state->p25_chan_tdma[iden] = 0; //set as old values for now
|
||||||
|
|
||||||
fprintf (stderr, "\n Identifier Update UHF/VHF\n");
|
fprintf (stderr, "\n Identifier Update UHF/VHF\n");
|
||||||
fprintf (stderr, " Channel Identifier [%01X] BW [%01X] Transmit Offset [%04X]\n Channel Spacing [%03X] Base Frequency [%08lX] [%09ld]",
|
fprintf (stderr, " Channel Identifier [%01X] BW [%01X] Transmit Offset [%04X]\n Channel Spacing [%03X] Base Frequency [%08lX] [%09ld]",
|
||||||
state->p25_chan_iden, bw_vu, state->p25_trans_off[iden], state->p25_chan_spac[iden], state->p25_base_freq[iden], state->p25_base_freq[iden] * 5);
|
state->p25_chan_iden, bw_vu, state->p25_trans_off[iden], state->p25_chan_spac[iden], state->p25_base_freq[iden], state->p25_base_freq[iden] * 5);
|
||||||
@@ -994,6 +1227,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
int iden = state->p25_chan_iden;
|
int iden = state->p25_chan_iden;
|
||||||
|
|
||||||
state->p25_chan_type[iden] = 1;
|
state->p25_chan_type[iden] = 1;
|
||||||
|
state->p25_chan_tdma[iden] = 0;
|
||||||
int bw = ((MAC[2+len_a] & 0xF) << 5) | ((MAC[3+len_a] & 0xF8) >> 2);
|
int bw = ((MAC[2+len_a] & 0xF) << 5) | ((MAC[3+len_a] & 0xF8) >> 2);
|
||||||
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
||||||
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
||||||
@@ -1009,6 +1243,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
{
|
{
|
||||||
state->p25_chan_iden = MAC[2+len_a] >> 4;
|
state->p25_chan_iden = MAC[2+len_a] >> 4;
|
||||||
int iden = state->p25_chan_iden;
|
int iden = state->p25_chan_iden;
|
||||||
|
state->p25_chan_tdma[iden] = 1;
|
||||||
state->p25_chan_type[iden] = MAC[2+len_a] & 0xF;
|
state->p25_chan_type[iden] = MAC[2+len_a] & 0xF;
|
||||||
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
||||||
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
||||||
@@ -1024,6 +1259,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
{
|
{
|
||||||
state->p25_chan_iden = MAC[3+len_a] >> 4;
|
state->p25_chan_iden = MAC[3+len_a] >> 4;
|
||||||
int iden = state->p25_chan_iden;
|
int iden = state->p25_chan_iden;
|
||||||
|
state->p25_chan_tdma[iden] = 1;
|
||||||
state->p25_chan_type[iden] = MAC[3+len_a] & 0xF;
|
state->p25_chan_type[iden] = MAC[3+len_a] & 0xF;
|
||||||
state->p25_trans_off[iden] = (MAC[4+len_a] << 6) | (MAC[5+len_a] >> 2);
|
state->p25_trans_off[iden] = (MAC[4+len_a] << 6) | (MAC[5+len_a] >> 2);
|
||||||
state->p25_chan_spac[iden] = ((MAC[5+len_a] & 0x3) << 8) | MAC[6+len_a];
|
state->p25_chan_spac[iden] = ((MAC[5+len_a] & 0x3) << 8) | MAC[6+len_a];
|
||||||
@@ -1050,12 +1286,15 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
{
|
{
|
||||||
|
|
||||||
fprintf (stderr, "\n Secondary Control Channel Broadcast - Explicit\n");
|
fprintf (stderr, "\n Secondary Control Channel Broadcast - Explicit\n");
|
||||||
fprintf (stderr, " RFSS[%03d] SITE ID [%03X] CHAN-T [%04X] CHAN-R [%04X] SSC [%02X]", rfssid, siteid, channelt, channelr, sysclass);
|
fprintf (stderr, " RFSS [%03d] SITE ID [%03X] CHAN-T [%04X] CHAN-R [%04X] SSC [%02X]", rfssid, siteid, channelt, channelr, sysclass);
|
||||||
|
|
||||||
process_channel_to_freq (opts, state, channelt);
|
process_channel_to_freq (opts, state, channelt);
|
||||||
process_channel_to_freq (opts, state, channelr);
|
process_channel_to_freq (opts, state, channelr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state->p2_siteid = siteid;
|
||||||
|
state->p2_rfssid = rfssid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Secondary Control Channel Broadcast, Implicit
|
//Secondary Control Channel Broadcast, Implicit
|
||||||
@@ -1085,7 +1324,10 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
state->trunk_lcn_freq[1] = freq1;
|
state->trunk_lcn_freq[1] = freq1;
|
||||||
state->trunk_lcn_freq[2] = freq2;
|
state->trunk_lcn_freq[2] = freq2;
|
||||||
state->lcn_freq_count = 3; //increment to three
|
state->lcn_freq_count = 3; //increment to three
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state->p2_siteid = siteid;
|
||||||
|
state->p2_rfssid = rfssid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1188,7 +1430,25 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
int src = (MAC[5+len_a] << 16) | (MAC[6+len_a] << 8) | MAC[7+len_a];
|
int src = (MAC[5+len_a] << 16) | (MAC[6+len_a] << 8) | MAC[7+len_a];
|
||||||
|
|
||||||
fprintf (stderr, "\n VCH %d - TG %d SRC %d ", slot, gr, src);
|
fprintf (stderr, "\n VCH %d - TG %d SRC %d ", slot, gr, src);
|
||||||
fprintf (stderr, "Group Voice");
|
|
||||||
|
if (svc & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (svc & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (svc & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (svc & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, " Group Voice");
|
||||||
|
|
||||||
|
sprintf (state->call_string[slot], " Group ");
|
||||||
|
if (svc & 0x80) strcat (state->call_string[slot], " Emergency ");
|
||||||
|
else if (svc & 0x40) strcat (state->call_string[slot], " Encrypted ");
|
||||||
|
else strcat (state->call_string[slot], " ");
|
||||||
|
|
||||||
if (slot == 0)
|
if (slot == 0)
|
||||||
{
|
{
|
||||||
@@ -1209,7 +1469,25 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
int src = (MAC[6+len_a] << 16) | (MAC[7+len_a] << 8) | MAC[8+len_a];
|
int src = (MAC[6+len_a] << 16) | (MAC[7+len_a] << 8) | MAC[8+len_a];
|
||||||
|
|
||||||
fprintf (stderr, "\n VCH %d - TG %d SRC %d ", slot, gr, src);
|
fprintf (stderr, "\n VCH %d - TG %d SRC %d ", slot, gr, src);
|
||||||
fprintf (stderr, "Unit to Unit Voice");
|
|
||||||
|
if (svc & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (svc & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
|
{
|
||||||
|
if (svc & 0x20) fprintf (stderr, " Duplex");
|
||||||
|
if (svc & 0x10) fprintf (stderr, " Packet");
|
||||||
|
else fprintf (stderr, " Circuit");
|
||||||
|
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
|
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, " Unit to Unit Voice");
|
||||||
|
|
||||||
|
sprintf (state->call_string[slot], " Private ");
|
||||||
|
if (svc & 0x80) strcat (state->call_string[slot], " Emergency ");
|
||||||
|
else if (svc & 0x40) strcat (state->call_string[slot], " Encrypted ");
|
||||||
|
else strcat (state->call_string[slot], " ");
|
||||||
|
|
||||||
if (slot == 0)
|
if (slot == 0)
|
||||||
{
|
{
|
||||||
@@ -1265,7 +1543,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
int lcolorcode = ((MAC[12+len_a] & 0xF) << 8) | MAC[13+len_a];
|
int lcolorcode = ((MAC[12+len_a] & 0xF) << 8) | MAC[13+len_a];
|
||||||
fprintf (stderr, "\n Network Status Broadcast - Extended \n");
|
fprintf (stderr, "\n Network Status Broadcast - Extended \n");
|
||||||
fprintf (stderr, " LRA [%02X] WACN [%05X] SYSID [%03X] NAC [%03X] CHAN-T [%04X] CHAN-R [%04X]", lra, lwacn, lsysid, lcolorcode, channelt, channelr);
|
fprintf (stderr, " LRA [%02X] WACN [%05X] SYSID [%03X] NAC [%03X] CHAN-T [%04X] CHAN-R [%04X]", lra, lwacn, lsysid, lcolorcode, channelt, channelr);
|
||||||
process_channel_to_freq (opts, state, channelt);
|
state->p25_cc_freq = process_channel_to_freq (opts, state, channelt);
|
||||||
process_channel_to_freq (opts, state, channelr);
|
process_channel_to_freq (opts, state, channelr);
|
||||||
state->p25_cc_is_tdma = 1; //flag on for CC tuning purposes when system is qpsk
|
state->p25_cc_is_tdma = 1; //flag on for CC tuning purposes when system is qpsk
|
||||||
if (state->p2_hardset == 0 ) //state->p2_is_lcch == 1 shim until CRC is working, prevent bogus data
|
if (state->p2_hardset == 0 ) //state->p2_is_lcch == 1 shim until CRC is working, prevent bogus data
|
||||||
@@ -1281,17 +1559,21 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
if (MAC[1+len_a] == 0x7C)
|
if (MAC[1+len_a] == 0x7C)
|
||||||
{
|
{
|
||||||
int lra = MAC[2+len_a];
|
int lra = MAC[2+len_a];
|
||||||
|
int cfva = MAC[3+len_a] >> 4;
|
||||||
int lsysid = ((MAC[3+len_a] & 0xF) << 8) | MAC[4+len_a];
|
int lsysid = ((MAC[3+len_a] & 0xF) << 8) | MAC[4+len_a];
|
||||||
int rfssid = MAC[5+len_a];
|
int rfssid = MAC[5+len_a];
|
||||||
int siteid = MAC[6+len_a];
|
int siteid = MAC[6+len_a];
|
||||||
int channelt = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
int channelt = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
||||||
int sysclass = MAC[9+len_a];
|
int sysclass = MAC[9+len_a];
|
||||||
if (1 == 1) //state->p2_is_lcch == 1
|
fprintf (stderr, "\n Adjacent Status Broadcast - Abbreviated\n");
|
||||||
{
|
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] SITE [%03X] CHAN-T [%04X] SSC [%02X]\n ", lra, rfssid, lsysid, siteid, channelt, sysclass);
|
||||||
fprintf (stderr, "\n Adjacent Status Broadcast - Abbreviated\n");
|
if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||||
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] SITE [%03X] CHAN-T [%04X] SSC [%02X]", lra, rfssid, lsysid, siteid, channelt, sysclass);
|
if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||||
process_channel_to_freq (opts, state, channelt);
|
if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||||
}
|
else fprintf (stderr, " Last Known");
|
||||||
|
if (cfva & 0x1) fprintf (stderr, " Valid RFSS Connection Active");
|
||||||
|
process_channel_to_freq (opts, state, channelt);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1299,22 +1581,28 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
if (MAC[1+len_a] == 0xFC)
|
if (MAC[1+len_a] == 0xFC)
|
||||||
{
|
{
|
||||||
int lra = MAC[2+len_a];
|
int lra = MAC[2+len_a];
|
||||||
|
int cfva = MAC[3+len_a] >> 4;
|
||||||
int lsysid = ((MAC[3+len_a] & 0xF) << 8) | MAC[4+len_a];
|
int lsysid = ((MAC[3+len_a] & 0xF) << 8) | MAC[4+len_a];
|
||||||
int rfssid = MAC[5+len_a];
|
int rfssid = MAC[5+len_a];
|
||||||
int siteid = MAC[6+len_a];
|
int siteid = MAC[6+len_a];
|
||||||
int channelt = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
int channelt = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
||||||
int channelr = (MAC[9+len_a] << 8) | MAC[10+len_a];
|
int channelr = (MAC[9+len_a] << 8) | MAC[10+len_a];
|
||||||
int sysclass = MAC[9+len_a];
|
int sysclass = MAC[11+len_a]; //need to re-check this
|
||||||
if (1 == 1) //state->p2_is_lcch == 1
|
fprintf (stderr, "\n Adjacent Status Broadcast - Extended\n");
|
||||||
{
|
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] SITE [%03X] CHAN-T [%04X] CHAN-R [%04X] SSC [%02X]\n ", lra, rfssid, lsysid, siteid, channelt, channelr, sysclass);
|
||||||
fprintf (stderr, "\n Adjacent Status Broadcast - Extended\n");
|
if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||||
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] SITE [%03X] CHAN-T [%04X] CHAN-R [%04X] SSC [%02X]", lra, rfssid, lsysid, siteid, channelt, channelr, sysclass);
|
if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||||
process_channel_to_freq (opts, state, channelt);
|
if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||||
process_channel_to_freq (opts, state, channelr);
|
else fprintf (stderr, " Last Known");
|
||||||
}
|
if (cfva & 0x1) fprintf (stderr, " Valid RFSS Connection Active");
|
||||||
|
process_channel_to_freq (opts, state, channelt);
|
||||||
|
process_channel_to_freq (opts, state, channelr);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SKIPCALL: ; //do nothing
|
||||||
|
|
||||||
if ( (len_b + len_c) < 24 && len_c != 0)
|
if ( (len_b + len_c) < 24 && len_c != 0)
|
||||||
{
|
{
|
||||||
len_a = len_b;
|
len_a = len_b;
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
//Figure out which PDU we are looking at, see above info on 8.4.1
|
//Figure out which PDU we are looking at, see above info on 8.4.1
|
||||||
//reorganize bits into bytes and process accordingly
|
//reorganize bits into bytes and process accordingly
|
||||||
|
|
||||||
|
//new slot variable with flipped assignment for SACCH
|
||||||
|
uint8_t slot = (state->currentslot ^ 1) & 1;
|
||||||
|
|
||||||
unsigned long long int SMAC[24] = {0}; //22.5 bytes for SACCH MAC PDUs
|
unsigned long long int SMAC[24] = {0}; //22.5 bytes for SACCH MAC PDUs
|
||||||
int byte = 0;
|
int byte = 0;
|
||||||
int k = 0;
|
int k = 0;
|
||||||
@@ -117,6 +120,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
{
|
{
|
||||||
//reset fourv_counter and dropbyte on PTT
|
//reset fourv_counter and dropbyte on PTT
|
||||||
state->fourv_counter[0] = 0;
|
state->fourv_counter[0] = 0;
|
||||||
|
state->dropL = 256;
|
||||||
|
|
||||||
state->dmrburstL = 20;
|
state->dmrburstL = 20;
|
||||||
fprintf (stderr, "\n VCH 0 - ");
|
fprintf (stderr, "\n VCH 0 - ");
|
||||||
@@ -139,6 +143,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
fprintf (stderr, "\n ALG ID 0x%02X", state->payload_algid);
|
fprintf (stderr, "\n ALG ID 0x%02X", state->payload_algid);
|
||||||
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyid);
|
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyid);
|
||||||
fprintf (stderr, " MI 0x%016llX", state->payload_miP);
|
fprintf (stderr, " MI 0x%016llX", state->payload_miP);
|
||||||
|
if (state->R != 0 && state->payload_algid == 0xAA) fprintf (stderr, " Key 0x%010llX", state->R);
|
||||||
fprintf(stderr, " MPTT");
|
fprintf(stderr, " MPTT");
|
||||||
// fprintf (stderr, " %s", KRED);
|
// fprintf (stderr, " %s", KRED);
|
||||||
// fprintf (stderr, "ENC");
|
// fprintf (stderr, "ENC");
|
||||||
@@ -150,6 +155,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
{
|
{
|
||||||
//reset fourv_counter and dropbyte on PTT
|
//reset fourv_counter and dropbyte on PTT
|
||||||
state->fourv_counter[1] = 0;
|
state->fourv_counter[1] = 0;
|
||||||
|
state->dropR = 256;
|
||||||
state->payload_algidR = 0; //zero this out as well
|
state->payload_algidR = 0; //zero this out as well
|
||||||
|
|
||||||
state->dmrburstR = 20;
|
state->dmrburstR = 20;
|
||||||
@@ -173,6 +179,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
fprintf (stderr, "\n ALG ID 0x%02X", state->payload_algidR);
|
fprintf (stderr, "\n ALG ID 0x%02X", state->payload_algidR);
|
||||||
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyidR);
|
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyidR);
|
||||||
fprintf (stderr, " MI 0x%016llX", state->payload_miN);
|
fprintf (stderr, " MI 0x%016llX", state->payload_miN);
|
||||||
|
if (state->RR != 0 && state->payload_algidR == 0xAA) fprintf (stderr, " Key 0x%010llX", state->RR);
|
||||||
fprintf(stderr, " MPTT");
|
fprintf(stderr, " MPTT");
|
||||||
// fprintf (stderr, " %s", KRED);
|
// fprintf (stderr, " %s", KRED);
|
||||||
// fprintf (stderr, "ENC");
|
// fprintf (stderr, "ENC");
|
||||||
@@ -191,6 +198,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
{
|
{
|
||||||
|
|
||||||
state->fourv_counter[0] = 0;
|
state->fourv_counter[0] = 0;
|
||||||
|
state->dropL = 256;
|
||||||
state->dmrburstL = 23;
|
state->dmrburstL = 23;
|
||||||
state->payload_algid = 0;
|
state->payload_algid = 0;
|
||||||
state->payload_keyid = 0;
|
state->payload_keyid = 0;
|
||||||
@@ -202,11 +210,19 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
//print it and then zero out
|
//print it and then zero out
|
||||||
state->lastsrc = 0;
|
state->lastsrc = 0;
|
||||||
state->lasttg = 0;
|
state->lasttg = 0;
|
||||||
|
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
|
||||||
|
//blank the call string here -- slot variable is already flipped accordingly for sacch
|
||||||
|
sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||||
|
|
||||||
}
|
}
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
state->fourv_counter[1] = 0;
|
state->fourv_counter[1] = 0;
|
||||||
|
state->dropR = 256;
|
||||||
state->dmrburstR = 23;
|
state->dmrburstR = 23;
|
||||||
state->payload_algidR = 0;
|
state->payload_algidR = 0;
|
||||||
state->payload_keyidR = 0;
|
state->payload_keyidR = 0;
|
||||||
@@ -218,6 +234,9 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
//print it and then zero out
|
//print it and then zero out
|
||||||
state->lastsrcR = 0;
|
state->lastsrcR = 0;
|
||||||
state->lasttgR = 0;
|
state->lasttgR = 0;
|
||||||
|
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
}
|
}
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -229,11 +248,25 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
process_MAC_VPDU(opts, state, 1, SMAC);
|
process_MAC_VPDU(opts, state, 1, SMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//blank the call string here -- slot variable is already flipped accordingly for sacch
|
||||||
|
sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||||
|
|
||||||
}
|
}
|
||||||
if (opcode == 0x4 && err == 0)
|
if (opcode == 0x4 && err == 0)
|
||||||
{
|
{
|
||||||
|
//disable to prevent blinking in ncurses terminal due to OSS preemption shim
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
if (opts->audio_out_type != 5)
|
||||||
|
{
|
||||||
|
if (state->currentslot == 1) state->dmrburstL = 21;
|
||||||
|
else state->dmrburstR = 21;
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (state->currentslot == 1) state->dmrburstL = 21;
|
if (state->currentslot == 1) state->dmrburstL = 21;
|
||||||
else state->dmrburstR = 21;
|
else state->dmrburstR = 21;
|
||||||
|
#endif
|
||||||
|
|
||||||
fprintf (stderr, " MAC_ACTIVE ");
|
fprintf (stderr, " MAC_ACTIVE ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
process_MAC_VPDU(opts, state, 1, SMAC);
|
process_MAC_VPDU(opts, state, 1, SMAC);
|
||||||
@@ -241,8 +274,18 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
}
|
}
|
||||||
if (opcode == 0x6 && err == 0)
|
if (opcode == 0x6 && err == 0)
|
||||||
{
|
{
|
||||||
if (state->currentslot == 1) state->dmrburstL = 22;
|
if (state->currentslot == 1)
|
||||||
else state->dmrburstR = 22;
|
{
|
||||||
|
state->dmrburstL = 22;
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->dmrburstR = 22;
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
fprintf (stderr, " MAC_HANGTIME ");
|
fprintf (stderr, " MAC_HANGTIME ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
process_MAC_VPDU(opts, state, 1, SMAC);
|
process_MAC_VPDU(opts, state, 1, SMAC);
|
||||||
@@ -262,6 +305,9 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
//Figure out which PDU we are looking at, see above info on 8.4.1
|
//Figure out which PDU we are looking at, see above info on 8.4.1
|
||||||
//reorganize bits into bytes and process accordingly
|
//reorganize bits into bytes and process accordingly
|
||||||
|
|
||||||
|
//new slot variable
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
|
||||||
unsigned long long int FMAC[24] = {0}; //19.5 bytes for FACCH MAC PDUs, add padding to end
|
unsigned long long int FMAC[24] = {0}; //19.5 bytes for FACCH MAC PDUs, add padding to end
|
||||||
int byte = 0;
|
int byte = 0;
|
||||||
int k = 0;
|
int k = 0;
|
||||||
@@ -329,6 +375,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
{
|
{
|
||||||
//reset fourv_counter and dropbyte on PTT
|
//reset fourv_counter and dropbyte on PTT
|
||||||
state->fourv_counter[0] = 0;
|
state->fourv_counter[0] = 0;
|
||||||
|
state->dropL = 256;
|
||||||
|
|
||||||
state->dmrburstL = 20;
|
state->dmrburstL = 20;
|
||||||
fprintf (stderr, "\n VCH 0 - ");
|
fprintf (stderr, "\n VCH 0 - ");
|
||||||
@@ -351,6 +398,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
fprintf (stderr, "\n ALG ID 0x%02X", state->payload_algid);
|
fprintf (stderr, "\n ALG ID 0x%02X", state->payload_algid);
|
||||||
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyid);
|
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyid);
|
||||||
fprintf (stderr, " MI 0x%016llX", state->payload_miP);
|
fprintf (stderr, " MI 0x%016llX", state->payload_miP);
|
||||||
|
if (state->R != 0 && state->payload_algid == 0xAA) fprintf (stderr, " Key 0x%010llX", state->R);
|
||||||
fprintf(stderr, " MPTT");
|
fprintf(stderr, " MPTT");
|
||||||
// fprintf (stderr, " %s", KRED);
|
// fprintf (stderr, " %s", KRED);
|
||||||
// fprintf (stderr, "ENC");
|
// fprintf (stderr, "ENC");
|
||||||
@@ -362,6 +410,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
{
|
{
|
||||||
//reset fourv_counter and dropbyte on PTT
|
//reset fourv_counter and dropbyte on PTT
|
||||||
state->fourv_counter[1] = 0;
|
state->fourv_counter[1] = 0;
|
||||||
|
state->dropR = 256;
|
||||||
|
|
||||||
state->dmrburstR = 20;
|
state->dmrburstR = 20;
|
||||||
fprintf (stderr, "\n VCH 1 - ");
|
fprintf (stderr, "\n VCH 1 - ");
|
||||||
@@ -384,6 +433,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
fprintf (stderr, "\n ALG ID 0x%02X", state->payload_algidR);
|
fprintf (stderr, "\n ALG ID 0x%02X", state->payload_algidR);
|
||||||
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyidR);
|
fprintf (stderr, " KEY ID 0x%04X", state->payload_keyidR);
|
||||||
fprintf (stderr, " MI 0x%016llX", state->payload_miN);
|
fprintf (stderr, " MI 0x%016llX", state->payload_miN);
|
||||||
|
if (state->RR != 0 && state->payload_algidR == 0xAA) fprintf (stderr, " Key 0x%010llX", state->RR);
|
||||||
fprintf(stderr, " MPTT");
|
fprintf(stderr, " MPTT");
|
||||||
// fprintf (stderr, " %s", KRED);
|
// fprintf (stderr, " %s", KRED);
|
||||||
// fprintf (stderr, "ENC");
|
// fprintf (stderr, "ENC");
|
||||||
@@ -401,6 +451,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
{
|
{
|
||||||
|
|
||||||
state->fourv_counter[0] = 0;
|
state->fourv_counter[0] = 0;
|
||||||
|
state->dropL = 256;
|
||||||
state->dmrburstL = 23;
|
state->dmrburstL = 23;
|
||||||
state->payload_algid = 0; //zero this out as well
|
state->payload_algid = 0; //zero this out as well
|
||||||
state->payload_keyid = 0;
|
state->payload_keyid = 0;
|
||||||
@@ -412,11 +463,19 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
//print it and then zero out
|
//print it and then zero out
|
||||||
state->lastsrc = 0;
|
state->lastsrc = 0;
|
||||||
state->lasttg = 0;
|
state->lasttg = 0;
|
||||||
|
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
|
||||||
|
//blank the call string here
|
||||||
|
sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||||
|
|
||||||
}
|
}
|
||||||
if (state->currentslot == 1)
|
if (state->currentslot == 1)
|
||||||
{
|
{
|
||||||
|
|
||||||
state->fourv_counter[1] = 0;
|
state->fourv_counter[1] = 0;
|
||||||
|
state->dropR = 256;
|
||||||
state->dmrburstR = 23;
|
state->dmrburstR = 23;
|
||||||
state->payload_algidR = 0; //zero this out as well
|
state->payload_algidR = 0; //zero this out as well
|
||||||
state->payload_keyidR = 0;
|
state->payload_keyidR = 0;
|
||||||
@@ -428,6 +487,9 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
//print it and then zero out
|
//print it and then zero out
|
||||||
state->lastsrcR = 0;
|
state->lastsrcR = 0;
|
||||||
state->lasttgR = 0;
|
state->lasttgR = 0;
|
||||||
|
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
}
|
}
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -459,11 +521,25 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
process_MAC_VPDU(opts, state, 0, FMAC);
|
process_MAC_VPDU(opts, state, 0, FMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//blank the call string here
|
||||||
|
sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||||
|
|
||||||
}
|
}
|
||||||
if (opcode == 0x4 && err == 0)
|
if (opcode == 0x4 && err == 0)
|
||||||
{
|
{
|
||||||
|
//disable to prevent blinking in ncurses terminal due to OSS preemption shim
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
if (opts->audio_out_type != 5)
|
||||||
|
{
|
||||||
|
if (state->currentslot == 0) state->dmrburstL = 21;
|
||||||
|
else state->dmrburstR = 21;
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (state->currentslot == 0) state->dmrburstL = 21;
|
if (state->currentslot == 0) state->dmrburstL = 21;
|
||||||
else state->dmrburstR = 21;
|
else state->dmrburstR = 21;
|
||||||
|
#endif
|
||||||
|
|
||||||
fprintf (stderr, " MAC_ACTIVE ");
|
fprintf (stderr, " MAC_ACTIVE ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
process_MAC_VPDU(opts, state, 0, FMAC);
|
process_MAC_VPDU(opts, state, 0, FMAC);
|
||||||
@@ -471,8 +547,18 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
}
|
}
|
||||||
if (opcode == 0x6 && err == 0)
|
if (opcode == 0x6 && err == 0)
|
||||||
{
|
{
|
||||||
if (state->currentslot == 0) state->dmrburstL = 22;
|
if (state->currentslot == 0)
|
||||||
else state->dmrburstR = 22;
|
{
|
||||||
|
state->dmrburstL = 22;
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->dmrburstR = 22;
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
fprintf (stderr, " MAC_HANGTIME ");
|
fprintf (stderr, " MAC_HANGTIME ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
process_MAC_VPDU(opts, state, 0, FMAC);
|
process_MAC_VPDU(opts, state, 0, FMAC);
|
||||||
|
|||||||
748
src/provoice.c
748
src/provoice.c
@@ -1,582 +1,432 @@
|
|||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
#include "provoice_const.h"
|
#include "provoice_const.h"
|
||||||
|
|
||||||
void
|
// #define PVDEBUG
|
||||||
processProVoice (dsd_opts * opts, dsd_state * state)
|
|
||||||
|
void processProVoice (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
int i, j, dibit, k;
|
int i, j, dibit;
|
||||||
uint8_t lid[80];
|
|
||||||
uint8_t lidbyte[10];
|
|
||||||
uint8_t init[64];
|
|
||||||
uint8_t initbyte[8];
|
|
||||||
char imbe7100_fr1[7][24];
|
char imbe7100_fr1[7][24];
|
||||||
char imbe7100_fr2[7][24];
|
char imbe7100_fr2[7][24];
|
||||||
const int *w, *x;
|
const int *w, *x;
|
||||||
|
|
||||||
|
//raw bits storage for analysis
|
||||||
|
uint8_t raw_bits[800];
|
||||||
|
memset (raw_bits, 0, sizeof(raw_bits));
|
||||||
|
//raw bytes storage for analysis
|
||||||
|
uint8_t raw_bytes[100];
|
||||||
|
memset (raw_bytes, 0, sizeof(raw_bytes));
|
||||||
|
|
||||||
|
unsigned long long int initial = 0; //initial 64-bits before the lid
|
||||||
|
uint16_t lid = 0; //lid value 16-bit
|
||||||
|
unsigned long long int secondary = 0; //secondary 64-bits after lid, before voice
|
||||||
|
|
||||||
|
uint8_t spacer_bit[8]; //check this value to see if its a status thing (like P25 P1)
|
||||||
|
memset (spacer_bit, 0, sizeof (spacer_bit));
|
||||||
|
uint8_t spacer = 0;
|
||||||
|
uint16_t bf = 0; //the 16-bit value in-between imbe 2 and imbe 3 that is usually 2175
|
||||||
|
|
||||||
fprintf (stderr," VOICE");
|
fprintf (stderr," VOICE");
|
||||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1)
|
|
||||||
|
//print group and source values if EA trunked
|
||||||
|
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->ea_mode == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf (stderr, "%s", KGRN);
|
||||||
fprintf (stderr, " Site [%02llX][%03lld] Group [%05d] Source [%08d] LCN[%02d] ",
|
fprintf (stderr, " Site: %lld Group: %d Source: %d LCN: %d ",
|
||||||
state->edacs_site_id, state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn);
|
state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
//print afs value if standard/networked trunked
|
||||||
|
else if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->ea_mode == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, " Site: %lld AFS: %d-%d LCN: %d ",
|
||||||
|
state->edacs_site_id, (state->lasttg >> 7) & 0xF, state->lasttg & 0x7F, state->edacs_tuned_lcn);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
//load all initial bits before voice into raw_bits array for analysis/handling
|
||||||
{
|
for (i = 0; i < 64+16+64; i++)
|
||||||
dibit = getDibit (opts, state);
|
raw_bits[i] = getDibit (opts, state);
|
||||||
init[i] = dibit;
|
|
||||||
#ifdef PROVOICE_DUMP
|
//Note: the initial 144-bits seem to be provisioned differently depending on system type
|
||||||
fprintf (stderr,"%i", dibit);
|
initial = (unsigned long long int)ConvertBitIntoBytes(&raw_bits[0], 64);
|
||||||
#endif
|
lid = (uint16_t)ConvertBitIntoBytes(&raw_bits[64], 16);
|
||||||
}
|
secondary = (unsigned long long int)ConvertBitIntoBytes(&raw_bits[80], 64);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr," ");
|
|
||||||
#endif
|
|
||||||
//init Bits (64)...MI?
|
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
k = 0;
|
fprintf (stderr, "\n N64: %016llX", initial);
|
||||||
//fprintf (stderr, "\nInit 64 ");
|
fprintf (stderr, "\n LID: %04X", lid);
|
||||||
for(i = 0; i < 8; i++)
|
fprintf (stderr, " %016llX", secondary);
|
||||||
{
|
|
||||||
initbyte[i] = 0;
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
initbyte[i] = initbyte[i] << 1;
|
|
||||||
initbyte[i] = initbyte[i] | init[k];
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
//fprintf (stderr, "%02X", initbyte[i]);
|
|
||||||
}
|
|
||||||
//fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
// lid
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lid[i] = dibit;
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr," ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lid[i+16] = dibit;
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr," ");
|
|
||||||
#endif
|
|
||||||
//load lid (bits) into lidbyte
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
k = 0;
|
|
||||||
//fprintf (stderr, "LID ");
|
|
||||||
for(i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
lidbyte[i] = 0;
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
lidbyte[i] = lidbyte[i] << 1;
|
|
||||||
lidbyte[i] = lidbyte[i] | lid[k];
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
//fprintf (stderr, "%02X", lidbyte[i]);
|
|
||||||
}
|
|
||||||
//fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// imbe frames 1,2 first half
|
// imbe frames 1,2 first half
|
||||||
w = pW;
|
w = pW;
|
||||||
x = pX;
|
x = pX;
|
||||||
|
|
||||||
for (i = 0; i < 11; i++)
|
for (i = 0; i < 11; i++)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 6; j++)
|
for (j = 0; j < 6; j++)
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[144+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
w -= 6;
|
||||||
#endif
|
x -= 6;
|
||||||
|
for (j = 0; j < 6; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[150+j+(i*12)] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < 6; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+282] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
w -= 6;
|
w -= 6;
|
||||||
x -= 6;
|
x -= 6;
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
fprintf (stderr,"%i", dibit);
|
w++;
|
||||||
#endif
|
x++;
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
raw_bits[j+288] = dibit;
|
||||||
w++;
|
}
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr," ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// spacer bits
|
// spacer bits
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
spacer_bit[0] = dibit;
|
||||||
fprintf (stderr,"%i", dibit);
|
raw_bits[292] = dibit;
|
||||||
#endif
|
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
spacer_bit[1] = dibit;
|
||||||
fprintf (stderr,"%i", dibit);
|
raw_bits[293] = dibit;
|
||||||
fprintf (stderr," ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// imbe frames 1,2 second half
|
// imbe frames 1,2 second half
|
||||||
|
|
||||||
for (j = 0; j < 2; j++)
|
for (j = 0; j < 2; j++)
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
fprintf (stderr,"%i", dibit);
|
w++;
|
||||||
#endif
|
x++;
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
raw_bits[j+294] = dibit;
|
||||||
w++;
|
}
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 6; j++)
|
for (j = 0; j < 6; j++)
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[296+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
w -= 6;
|
||||||
fprintf (stderr,"_");
|
x -= 6;
|
||||||
#endif
|
for (j = 0; j < 6; j++)
|
||||||
w -= 5;
|
|
||||||
x -= 5;
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[302+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
}
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+338] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
|
w -= 5;
|
||||||
|
x -= 5;
|
||||||
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+343] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
for (i = 0; i < 7; i++)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 6; j++)
|
for (j = 0; j < 6; j++)
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[348+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
w -= 6;
|
||||||
#endif
|
x -= 6;
|
||||||
w -= 5;
|
for (j = 0; j < 6; j++)
|
||||||
x -= 5;
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[354+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr," ");
|
}
|
||||||
#endif
|
|
||||||
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+438] = dibit;
|
||||||
|
}
|
||||||
|
w -= 5;
|
||||||
|
x -= 5;
|
||||||
|
|
||||||
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+443] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1);
|
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1);
|
||||||
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr2);
|
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr2);
|
||||||
|
|
||||||
// spacer bits
|
// spacer bits
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
spacer_bit[2] = dibit;
|
||||||
fprintf (stderr,"%i", dibit);
|
raw_bits[448] = dibit;
|
||||||
#endif
|
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
spacer_bit[3] = dibit;
|
||||||
fprintf (stderr,"%i", dibit);
|
raw_bits[449] = dibit;
|
||||||
fprintf (stderr," ");
|
|
||||||
#endif
|
//resume raw_bits at +6 current value to round out payload bytes
|
||||||
|
//easier to visualize patterns
|
||||||
|
|
||||||
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
||||||
{
|
raw_bits[i+450+6] = getDibit (opts, state);
|
||||||
dibit = getDibit (opts, state); //HERE
|
|
||||||
init[i] = dibit; //recycle
|
bf = (uint16_t)ConvertBitIntoBytes(&raw_bits[456], 16);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
if (opts->payload == 1)
|
||||||
#endif
|
fprintf (stderr, "\n BF: %04X ", bf);
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr," ");
|
|
||||||
#endif
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
k = 0;
|
|
||||||
//fprintf (stderr, "16 bits? ");
|
|
||||||
for(i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
initbyte[i] = 0;
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
initbyte[i] = initbyte[i] << 1;
|
|
||||||
initbyte[i] = initbyte[i] | init[k];
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
//fprintf (stderr, "%02X", initbyte[i]);
|
|
||||||
}
|
|
||||||
//fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
// imbe frames 3,4 first half
|
// imbe frames 3,4 first half
|
||||||
w = pW;
|
w = pW;
|
||||||
x = pX;
|
x = pX;
|
||||||
for (i = 0; i < 11; i++)
|
for (i = 0; i < 11; i++)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 6; j++)
|
for (j = 0; j < 6; j++)
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[6+466+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr,"_");
|
w -= 6;
|
||||||
#endif
|
x -= 6;
|
||||||
|
for (j = 0; j < 6; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[6+472+j+(i*12)] = dibit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < 6; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+604+6] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
w -= 6;
|
w -= 6;
|
||||||
x -= 6;
|
x -= 6;
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
fprintf (stderr,"%i", dibit);
|
w++;
|
||||||
#endif
|
x++;
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
raw_bits[i+610+6] = dibit;
|
||||||
w++;
|
}
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr," ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// spacer bits
|
// spacer bits
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
spacer_bit[4];
|
||||||
//fprintf (stderr,"%i", dibit);
|
raw_bits[614+2] = dibit;
|
||||||
#endif
|
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
spacer_bit[5];
|
||||||
//fprintf (stderr,"%i", dibit);
|
raw_bits[615+2] = dibit;
|
||||||
//fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// imbe frames 3,4 second half
|
// imbe frames 3,4 second half
|
||||||
for (j = 0; j < 2; j++)
|
for (j = 0; j < 2; j++)
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
//fprintf (stderr,"%i", dibit);
|
w++;
|
||||||
#endif
|
x++;
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
raw_bits[j+616+6] = dibit;
|
||||||
w++;
|
}
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 6; j++)
|
for (j = 0; j < 6; j++)
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[6+618+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
w -= 6;
|
||||||
//fprintf (stderr,"_");
|
x -= 6;
|
||||||
#endif
|
for (j = 0; j < 6; j++)
|
||||||
w -= 5;
|
|
||||||
x -= 5;
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[6+624+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
}
|
||||||
//fprintf (stderr," ");
|
|
||||||
#endif
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+660+6] = dibit;
|
||||||
|
}
|
||||||
|
w -= 5;
|
||||||
|
x -= 5;
|
||||||
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+665+6] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
for (i = 0; i < 7; i++)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 6; j++)
|
for (j = 0; j < 6; j++)
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[6+670+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
w -= 6;
|
||||||
//fprintf (stderr,"_");
|
x -= 6;
|
||||||
#endif
|
for (j = 0; j < 6; j++)
|
||||||
w -= 5;
|
|
||||||
x -= 5;
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
//fprintf (stderr,"%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
|
raw_bits[6+676+j+(i*12)] = dibit;
|
||||||
}
|
}
|
||||||
#ifdef PROVOICE_DUMP
|
}
|
||||||
//fprintf (stderr," ");
|
|
||||||
#endif
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr1[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+760+6] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
|
w -= 5;
|
||||||
|
x -= 5;
|
||||||
|
for (j = 0; j < 5; j++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
imbe7100_fr2[*w][*x] = dibit;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
raw_bits[j+765+6] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1);
|
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1);
|
||||||
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr2);
|
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr2);
|
||||||
|
|
||||||
// spacer bits
|
// spacer bits
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
spacer_bit[6] = dibit;
|
||||||
//fprintf (stderr,"%i", dibit);
|
raw_bits[782] = dibit;
|
||||||
#endif
|
|
||||||
dibit = getDibit (opts, state);
|
dibit = getDibit (opts, state);
|
||||||
#ifdef PROVOICE_DUMP
|
spacer_bit[7] = dibit;
|
||||||
//fprintf (stderr,"%i", dibit);
|
raw_bits[783] = dibit;
|
||||||
//fprintf (stderr," ");
|
|
||||||
|
#ifdef PVDEBUG
|
||||||
|
|
||||||
|
spacer = (uint8_t)ConvertBitIntoBytes(&spacer_bit[0], 8);
|
||||||
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "\n SP: %X", spacer);
|
||||||
|
|
||||||
|
//convert raw bits into raw bytes for payload analysis
|
||||||
|
for (j = 0; j < 98; j++)
|
||||||
|
{
|
||||||
|
raw_bytes[j] = (uint8_t)ConvertBitIntoBytes(&raw_bits[(j*8)], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
//payload on all raw bytes for analysis...its a lot of 'em
|
||||||
|
if (opts->payload == 1) //find better thingy later or change this
|
||||||
|
// if (opcode != 0x3333)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, "\n pV Payload Dump: \n ");
|
||||||
|
for (j = 0; j < 98; j++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", raw_bytes[j]);
|
||||||
|
if (j == 17 || j == 35 || j == 53 || j == 71 || j == 89)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
//line break at end of frame
|
||||||
{
|
fprintf (stderr,"\n");
|
||||||
fprintf (stderr,"\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,12 +31,12 @@
|
|||||||
#include <rtl-sdr.h>
|
#include <rtl-sdr.h>
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
//#define DEFAULT_SAMPLE_RATE 48000 //moved to opts->rtl_bandwidth
|
#define DEFAULT_SAMPLE_RATE 48000
|
||||||
#define DEFAULT_BUF_LENGTH (1 * 16384)
|
#define DEFAULT_BUF_LENGTH (1 * 16384)
|
||||||
#define MAXIMUM_OVERSAMPLE 16 //16
|
#define MAXIMUM_OVERSAMPLE 16
|
||||||
#define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH)
|
#define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH)
|
||||||
#define AUTO_GAIN -100
|
#define AUTO_GAIN -100
|
||||||
#define BUFFER_DUMP 4096
|
#define BUFFER_DUMP 4096
|
||||||
|
|
||||||
#define FREQUENCIES_LIMIT 1000
|
#define FREQUENCIES_LIMIT 1000
|
||||||
|
|
||||||
@@ -46,7 +46,8 @@ static int ACTUAL_BUF_LENGTH;
|
|||||||
static int *atan_lut = NULL;
|
static int *atan_lut = NULL;
|
||||||
static int atan_lut_size = 131072; /* 512 KB */
|
static int atan_lut_size = 131072; /* 512 KB */
|
||||||
static int atan_lut_coef = 8;
|
static int atan_lut_coef = 8;
|
||||||
//new for UDP
|
|
||||||
|
//UDP -- keep for compatibility reasons
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
static pthread_t socket_freq;
|
static pthread_t socket_freq;
|
||||||
@@ -57,7 +58,7 @@ int bandwidth_divisor = 48000; //divide bandwidth by this to get multiplier for
|
|||||||
|
|
||||||
short int volume_multiplier;
|
short int volume_multiplier;
|
||||||
short int port;
|
short int port;
|
||||||
//
|
|
||||||
struct dongle_state
|
struct dongle_state
|
||||||
{
|
{
|
||||||
int exit_flag;
|
int exit_flag;
|
||||||
@@ -462,11 +463,14 @@ void dc_block_filter(struct demod_state *fm)
|
|||||||
}
|
}
|
||||||
fm->dc_avg = avg;
|
fm->dc_avg = avg;
|
||||||
}
|
}
|
||||||
|
//TODO: Fix This so we don't get random negative values in Cygwin (or 32-bit builds?)
|
||||||
int rms(int16_t *samples, int len, int step)
|
//Fixed -- I honestly think the issue is Cygwin can't keep up and if it can't run enough samples in the other thread,
|
||||||
|
//then it reports some jank values, really unsure, I've never seen the issue in Linux side though
|
||||||
|
long int rms(int16_t *samples, int len, int step)
|
||||||
/* largely lifted from rtl_power */
|
/* largely lifted from rtl_power */
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
long int rms;
|
||||||
long p, t, s;
|
long p, t, s;
|
||||||
double dc, err;
|
double dc, err;
|
||||||
|
|
||||||
@@ -480,7 +484,10 @@ int rms(int16_t *samples, int len, int step)
|
|||||||
dc = (double)(t*step) / (double)len;
|
dc = (double)(t*step) / (double)len;
|
||||||
err = t * 2 * dc - dc * dc * len;
|
err = t * 2 * dc - dc * dc * len;
|
||||||
|
|
||||||
return (int)sqrt((p-err) / len);
|
rms = (long int)sqrt((p-err) / len);
|
||||||
|
//going with a value that's easy to figure out when its done the thing
|
||||||
|
if (rms < 0){ rms = 999; } //need to text nxdn with this setup to make sure it isn't broken in Windows Cygwin now
|
||||||
|
return rms;
|
||||||
}
|
}
|
||||||
|
|
||||||
void full_demod(struct demod_state *d)
|
void full_demod(struct demod_state *d)
|
||||||
@@ -597,7 +604,6 @@ static void *demod_thread_fn(void *arg)
|
|||||||
|
|
||||||
int nearest_gain(rtlsdr_dev_t *dev, int target_gain)
|
int nearest_gain(rtlsdr_dev_t *dev, int target_gain)
|
||||||
{
|
{
|
||||||
//target_gain = 36;
|
|
||||||
int i, r, err1, err2, count, nearest;
|
int i, r, err1, err2, count, nearest;
|
||||||
int* gains;
|
int* gains;
|
||||||
r = rtlsdr_set_tuner_gain_mode(dev, 1);
|
r = rtlsdr_set_tuner_gain_mode(dev, 1);
|
||||||
@@ -628,10 +634,9 @@ int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency)
|
|||||||
int r;
|
int r;
|
||||||
r = rtlsdr_set_center_freq(dev, frequency);
|
r = rtlsdr_set_center_freq(dev, frequency);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
fprintf (stderr, "WARNING: Failed to set center freq.\n");
|
fprintf (stderr, " (WARNING: Failed to set Center Frequency). \n");
|
||||||
} else {
|
} else {
|
||||||
//fprintf (stderr, "Tuned to %u Hz.\n", frequency);
|
fprintf (stderr, " (Center Frequency: %u Hz.) \n", frequency);
|
||||||
fprintf (stderr, "Tuned to center frequency %u Hz.\n", frequency);
|
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -679,7 +684,6 @@ int verbose_offset_tuning(rtlsdr_dev_t *dev)
|
|||||||
|
|
||||||
int verbose_auto_gain(rtlsdr_dev_t *dev)
|
int verbose_auto_gain(rtlsdr_dev_t *dev)
|
||||||
{
|
{
|
||||||
|
|
||||||
int r;
|
int r;
|
||||||
r = rtlsdr_set_tuner_gain_mode(dev, 0);
|
r = rtlsdr_set_tuner_gain_mode(dev, 0);
|
||||||
if (r != 0) {
|
if (r != 0) {
|
||||||
@@ -692,7 +696,6 @@ int verbose_auto_gain(rtlsdr_dev_t *dev)
|
|||||||
|
|
||||||
int verbose_gain_set(rtlsdr_dev_t *dev, int gain)
|
int verbose_gain_set(rtlsdr_dev_t *dev, int gain)
|
||||||
{
|
{
|
||||||
//gain = 36;
|
|
||||||
int r;
|
int r;
|
||||||
r = rtlsdr_set_tuner_gain_mode(dev, 1);
|
r = rtlsdr_set_tuner_gain_mode(dev, 1);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@@ -710,7 +713,6 @@ int verbose_gain_set(rtlsdr_dev_t *dev, int gain)
|
|||||||
|
|
||||||
int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error)
|
int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error)
|
||||||
{
|
{
|
||||||
//ppm_error = -2; //fix this opt arg to accept negative float values, i.e. float or double??
|
|
||||||
int r;
|
int r;
|
||||||
if (ppm_error == 0) {
|
if (ppm_error == 0) {
|
||||||
return 0;}
|
return 0;}
|
||||||
@@ -732,25 +734,23 @@ int verbose_reset_buffer(rtlsdr_dev_t *dev)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
//static void optimal_settings(struct fm_state *fm, int freq, int hopping)
|
static void optimal_settings(int freq, int rate)
|
||||||
static void optimal_settings(int freq, int rate) //
|
|
||||||
{
|
{
|
||||||
// giant ball of hacks
|
// giant ball of hacks
|
||||||
// seems unable to do a single pass, 2:1
|
// seems unable to do a single pass, 2:1
|
||||||
//int r, capture_freq, capture_rate;
|
|
||||||
int capture_freq, capture_rate;
|
int capture_freq, capture_rate;
|
||||||
struct dongle_state *d = &dongle;
|
struct dongle_state *d = &dongle;
|
||||||
struct demod_state *dm = &demod;
|
struct demod_state *dm = &demod;
|
||||||
struct controller_state *cs = &controller;
|
struct controller_state *cs = &controller;
|
||||||
dm->downsample = (1000000 / dm->rate_in) + 1;
|
dm->downsample = (1000000 / dm->rate_in) + 1; //dm->rate_in is the rtl_bandwidth value
|
||||||
if (dm->downsample_passes) {
|
if (dm->downsample_passes) {
|
||||||
dm->downsample_passes = (int)log2(dm->downsample) + 1;
|
dm->downsample_passes = (int)log2(dm->downsample) + 1;
|
||||||
dm->downsample = 1 << dm->downsample_passes;
|
dm->downsample = 1 << dm->downsample_passes;
|
||||||
}
|
}
|
||||||
capture_freq = freq;
|
capture_freq = freq;
|
||||||
capture_rate = dm->downsample * dm->rate_in;
|
capture_rate = dm->downsample * dm->rate_in; //
|
||||||
if (!d->offset_tuning) {
|
if (!d->offset_tuning) {
|
||||||
capture_freq = freq + capture_rate/4;}
|
capture_freq = freq + capture_rate/4;} //
|
||||||
capture_freq += cs->edge * dm->rate_in / 2;
|
capture_freq += cs->edge * dm->rate_in / 2;
|
||||||
dm->output_scale = (1<<15) / (128 * dm->downsample);
|
dm->output_scale = (1<<15) / (128 * dm->downsample);
|
||||||
if (dm->output_scale < 1) {
|
if (dm->output_scale < 1) {
|
||||||
@@ -759,6 +759,7 @@ static void optimal_settings(int freq, int rate) //
|
|||||||
dm->output_scale = 1;}
|
dm->output_scale = 1;}
|
||||||
d->freq = (uint32_t)capture_freq;
|
d->freq = (uint32_t)capture_freq;
|
||||||
d->rate = (uint32_t)capture_rate;
|
d->rate = (uint32_t)capture_rate;
|
||||||
|
// fprintf (stderr, "Capture Frequency: %i Rate: %i \n", capture_freq, capture_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *controller_thread_fn(void *arg)
|
static void *controller_thread_fn(void *arg)
|
||||||
@@ -790,7 +791,6 @@ static void *controller_thread_fn(void *arg)
|
|||||||
/* Set the sample rate */
|
/* Set the sample rate */
|
||||||
verbose_set_sample_rate(dongle.dev, dongle.rate);
|
verbose_set_sample_rate(dongle.dev, dongle.rate);
|
||||||
fprintf (stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample);
|
fprintf (stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample);
|
||||||
fprintf (stderr, "Press CTRL + C to close.\n");
|
|
||||||
|
|
||||||
while (!exitflag) {
|
while (!exitflag) {
|
||||||
safe_cond_wait(&s->hop, &s->hop_m);
|
safe_cond_wait(&s->hop, &s->hop_m);
|
||||||
@@ -807,20 +807,16 @@ static void *controller_thread_fn(void *arg)
|
|||||||
|
|
||||||
void dongle_init(struct dongle_state *s)
|
void dongle_init(struct dongle_state *s)
|
||||||
{
|
{
|
||||||
//s->rate = DEFAULT_SAMPLE_RATE;
|
|
||||||
s->rate = rtl_bandwidth;
|
s->rate = rtl_bandwidth;
|
||||||
s->gain = AUTO_GAIN; // tenths of a dB
|
s->gain = AUTO_GAIN; // tenths of a dB
|
||||||
s->mute = 0;
|
s->mute = 0;
|
||||||
s->direct_sampling = 0;
|
s->direct_sampling = 0;
|
||||||
s->offset_tuning = 0; //
|
s->offset_tuning = 0; //E4000 tuners only
|
||||||
s->demod_target = &demod;
|
s->demod_target = &demod;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void demod_init(struct demod_state *s)
|
void demod_init(struct demod_state *s)
|
||||||
{
|
{
|
||||||
//s->rate_in = DEFAULT_SAMPLE_RATE;
|
|
||||||
//s->rate_out = DEFAULT_SAMPLE_RATE;
|
|
||||||
s->rate_in = rtl_bandwidth;
|
s->rate_in = rtl_bandwidth;
|
||||||
s->rate_out = rtl_bandwidth;
|
s->rate_out = rtl_bandwidth;
|
||||||
s->squelch_level = 0;
|
s->squelch_level = 0;
|
||||||
@@ -830,16 +826,16 @@ void demod_init(struct demod_state *s)
|
|||||||
s->downsample_passes = 0;
|
s->downsample_passes = 0;
|
||||||
s->comp_fir_size = 0;
|
s->comp_fir_size = 0;
|
||||||
s->prev_index = 0;
|
s->prev_index = 0;
|
||||||
s->post_downsample = 1; // once this works, default = 4
|
s->post_downsample = 1; //1 -- once this works, default = 4 -- doesn't work on the official rtl-sdr source code either
|
||||||
s->custom_atan = 0;
|
s->custom_atan = 0;
|
||||||
s->deemph = 0;
|
s->deemph = 0;
|
||||||
s->rate_out2 = -1; // flag for disabled
|
s->rate_out2 = rtl_bandwidth; // -1 flag for disabled -- this enables low_pass_real, seems to work okay
|
||||||
s->mode_demod = &fm_demod;
|
s->mode_demod = &fm_demod;
|
||||||
s->pre_j = s->pre_r = s->now_r = s->now_j = 0;
|
s->pre_j = s->pre_r = s->now_r = s->now_j = 0;
|
||||||
s->prev_lpr_index = 0;
|
s->prev_lpr_index = 0;
|
||||||
s->deemph_a = 0;
|
s->deemph_a = 0;
|
||||||
s->now_lpr = 0;
|
s->now_lpr = 0;
|
||||||
s->dc_block = 0;
|
s->dc_block = 1; //enabling by default, but offset tuning is also enabled, so center spike shouldn't be an issue
|
||||||
s->dc_avg = 0;
|
s->dc_avg = 0;
|
||||||
pthread_rwlock_init(&s->rw, NULL);
|
pthread_rwlock_init(&s->rw, NULL);
|
||||||
pthread_cond_init(&s->ready, NULL);
|
pthread_cond_init(&s->ready, NULL);
|
||||||
@@ -856,7 +852,6 @@ void demod_cleanup(struct demod_state *s)
|
|||||||
|
|
||||||
void output_init(struct output_state *s)
|
void output_init(struct output_state *s)
|
||||||
{
|
{
|
||||||
//s->rate = DEFAULT_SAMPLE_RATE;
|
|
||||||
s->rate = rtl_bandwidth;
|
s->rate = rtl_bandwidth;
|
||||||
pthread_rwlock_init(&s->rw, NULL);
|
pthread_rwlock_init(&s->rw, NULL);
|
||||||
pthread_cond_init(&s->ready, NULL);
|
pthread_cond_init(&s->ready, NULL);
|
||||||
@@ -904,7 +899,8 @@ void sanity_checks(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
//UDP remote all below HERE
|
|
||||||
|
//UDP remote stuff
|
||||||
static unsigned int chars_to_int(unsigned char* buf) {
|
static unsigned int chars_to_int(unsigned char* buf) {
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
@@ -918,9 +914,7 @@ static unsigned int chars_to_int(unsigned char* buf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void *socket_thread_fn(void *arg) {
|
static void *socket_thread_fn(void *arg) {
|
||||||
//struct fm_state *fm = arg;
|
|
||||||
struct demod_state *d = static_cast<demod_state*>(arg);
|
struct demod_state *d = static_cast<demod_state*>(arg);
|
||||||
//int port = 6020;
|
|
||||||
int r, n;
|
int r, n;
|
||||||
int sockfd, newsockfd, portno;
|
int sockfd, newsockfd, portno;
|
||||||
socklen_t clilen;
|
socklen_t clilen;
|
||||||
@@ -952,114 +946,34 @@ static void *socket_thread_fn(void *arg) {
|
|||||||
while((n = read(sockfd,buffer,5)) != 0) {
|
while((n = read(sockfd,buffer,5)) != 0) {
|
||||||
if(buffer[0] == 0) {
|
if(buffer[0] == 0) {
|
||||||
new_freq = chars_to_int(buffer);
|
new_freq = chars_to_int(buffer);
|
||||||
dongle.freq = new_freq; //
|
dongle.freq = new_freq;
|
||||||
//opts->rtlsdr_center_freq = new_freq; //or dongle.freq, this should update the freq displayed in ncurses terminal hopefully and not do anything naughty
|
|
||||||
|
|
||||||
optimal_settings(new_freq, demod.rate_in);
|
optimal_settings(new_freq, demod.rate_in);
|
||||||
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
||||||
//verbose_set_frequency (dongle.dev, new_freq);
|
|
||||||
//fprintf (stderr, "Tuning to: %d [Hz] (central freq: %d [Hz])\n", new_freq, new_freq + freq_offset);
|
|
||||||
fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq);
|
fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq);
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
if(buffer[0] == 1) {
|
|
||||||
// change demod type
|
|
||||||
int type = chars_to_int(buffer);
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case 0:
|
|
||||||
fprintf (stderr, "Changing demod type to FM\n");
|
|
||||||
fm->mode_demod = &fm_demod;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
fprintf (stderr, "Changing demod type to AM\n");
|
|
||||||
fm->mode_demod = &am_demod;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
fprintf (stderr, "Changing demod type to USB\n");
|
|
||||||
fm->mode_demod = &usb_demod;
|
|
||||||
fm->custom_atan = 1;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
fprintf (stderr, "Changing demod type to LSB\n");
|
|
||||||
fm->mode_demod = &lsb_demod;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf (stderr, "Unknown demod type %d\n", type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//SQUELCH HERE
|
|
||||||
|
|
||||||
if (buffer[0] == 2) {
|
|
||||||
new_squelch = chars_to_int(buffer);
|
|
||||||
demod.squelch_level = new_squelch; //demod, or d*? or d->
|
|
||||||
//full_demod(d);
|
|
||||||
//new_freq = 850000000;
|
|
||||||
//dongle.freq = new_freq;
|
|
||||||
//optimal_settings(new_freq, demod.rate_in); //hacky way to 'squelch', just setting it to a dead freq instead
|
|
||||||
//rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
|
||||||
fprintf (stderr, "Changing squelch to %d \n", new_squelch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (buffer[0] == 3) {
|
|
||||||
new_gain = chars_to_int(buffer);
|
|
||||||
if (new_gain == AUTO_GAIN) {
|
|
||||||
r = rtlsdr_set_tuner_gain_mode(dev, 0);
|
|
||||||
} else {
|
|
||||||
r = rtlsdr_set_tuner_gain_mode(dev, 1);
|
|
||||||
new_gain = nearest_gain(new_gain);
|
|
||||||
r = rtlsdr_set_tuner_gain(dev, new_gain);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r != 0) {
|
|
||||||
fprintf (stderr, "WARNING: Failed to set tuner gain.\n");
|
|
||||||
} else if (new_gain == AUTO_GAIN) {
|
|
||||||
fprintf (stderr, "Tuner gain set to automatic.\n");
|
|
||||||
} else {
|
|
||||||
fprintf (stderr, "Tuner gain set to %0.2f dB.\n", new_gain/10.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
if (buffer[0] == 8) {
|
|
||||||
agc_mode = chars_to_int(buffer);
|
|
||||||
if (agc_mode == 0 || agc_mode == 1) {
|
|
||||||
fprintf (stderr, "Setting AGC to %d\n", agc_mode);
|
|
||||||
rtlsdr_set_agc_mode(dev, agc_mode);
|
|
||||||
} else {
|
|
||||||
fprintf (stderr, "Failed to set AGC to %d\n", agc_mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
//UDP Control all above HERE
|
//UDP stuff end
|
||||||
|
|
||||||
void rtlsdr_sighandler()
|
void rtlsdr_sighandler()
|
||||||
{
|
{
|
||||||
//fprintf (stderr, "Signal caught, exiting!\n");
|
fprintf (stderr, "Signal caught, exiting!\n");
|
||||||
//fprintf (stderr, "Press CTRL + C again to close. Thanks. \n"); //Kindly remind user to double tap CTRL + C
|
|
||||||
|
|
||||||
rtlsdr_cancel_async(dongle.dev);
|
rtlsdr_cancel_async(dongle.dev);
|
||||||
//cleanup_rtlsdr_stream(); //thank the wraith for this one...if it works...didn't work
|
|
||||||
//_Exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void open_rtlsdr_stream(dsd_opts *opts)
|
void open_rtlsdr_stream(dsd_opts *opts)
|
||||||
{
|
{
|
||||||
struct sigaction sigact;
|
struct sigaction sigact;
|
||||||
int r;
|
int r;
|
||||||
rtl_bandwidth = opts->rtl_bandwidth * 1000; //multiple by 1000 to get rate
|
rtl_bandwidth = opts->rtl_bandwidth * 1000; //reverted back to straight value
|
||||||
//rtl_bandwidth = 48000;
|
|
||||||
bandwidth_multiplier = (bandwidth_divisor / rtl_bandwidth);
|
bandwidth_multiplier = (bandwidth_divisor / rtl_bandwidth);
|
||||||
//bandwidth_multiplier = 1;
|
|
||||||
|
//this needs to be initted first, then we set the parameters
|
||||||
dongle_init(&dongle);
|
dongle_init(&dongle);
|
||||||
demod_init(&demod);
|
demod_init(&demod);
|
||||||
output_init(&output);
|
output_init(&output);
|
||||||
@@ -1070,29 +984,23 @@ void open_rtlsdr_stream(dsd_opts *opts)
|
|||||||
controller.freq_len++;
|
controller.freq_len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->rtlsdr_ppm_error > 0 || opts->rtlsdr_ppm_error < 0) { //this way will adjust PPM based on negative values as well
|
if (opts->rtlsdr_ppm_error != 0) {
|
||||||
dongle.ppm_error = opts->rtlsdr_ppm_error;
|
dongle.ppm_error = opts->rtlsdr_ppm_error;
|
||||||
fprintf (stderr, "Setting RTL PPM Error Set to %d\n", opts->rtlsdr_ppm_error);
|
fprintf (stderr, "Setting RTL PPM Error Set to %d\n", opts->rtlsdr_ppm_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->audio_in_type == 3) {
|
dongle.dev_index = opts->rtl_dev_index;
|
||||||
dongle.dev_index = opts->rtl_dev_index;
|
// demod.squelch_level = opts->rtl_squelch_level; //no longer used here, used in framesync vc rms value under select conditions
|
||||||
//rtl_bandwidth = opts->rtl_bandwidth * 1000; //multiple by 1000 to get rate
|
fprintf (stderr, "Setting RTL Bandwidth to %d Hz\n", rtl_bandwidth);
|
||||||
//rtl_bandwidth = 48000;
|
// fprintf (stderr, "Setting RTL Sample Multiplier to %d\n", bandwidth_multiplier);
|
||||||
//bandwidth_multiplier = rtl_bandwidth / bandwidth_divisor; //find multiple with no remainder if oddball number entered
|
fprintf (stderr, "Setting RTL RMS Squelch Level to %d\n", opts->rtl_squelch_level);
|
||||||
//bandwidth_multiplier = 1;
|
if (opts->rtl_udp_port != 0) port = opts->rtl_udp_port; //set this here, only open socket thread if set
|
||||||
demod.squelch_level = opts->rtl_squelch_level; //adding user definable squelch level to prevent false positives on account of noise in NXDN etc
|
|
||||||
fprintf (stderr, "Setting RTL VFO Bandwidth to %d Hz\n", rtl_bandwidth);
|
|
||||||
fprintf (stderr, "Setting RTL Sample Multiplier to %d\n", bandwidth_multiplier);
|
|
||||||
fprintf (stderr, "Setting RTL Squelch Level to %d\n", demod.squelch_level);
|
|
||||||
port = opts->rtl_udp_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->rtl_gain_value > 0) {
|
if (opts->rtl_gain_value > 0) {
|
||||||
dongle.gain = opts->rtl_gain_value * 10; //multiple by ten to make it consitent with the way rtl_fm really works
|
dongle.gain = opts->rtl_gain_value * 10; //multiple by ten to make it consitent with the way rtl_fm works
|
||||||
}
|
}
|
||||||
volume_multiplier = opts->rtl_volume_multiplier;
|
volume_multiplier = opts->rtl_volume_multiplier;
|
||||||
fprintf (stderr, "Setting RTL Volume Multiplier to %d\n", volume_multiplier);
|
// fprintf (stderr, "Setting RTL Volume Multiplier to %d\n", volume_multiplier);
|
||||||
|
|
||||||
/* quadruple sample_rate to limit to Δθ to ±π/2 */
|
/* quadruple sample_rate to limit to Δθ to ±π/2 */
|
||||||
demod.rate_in *= demod.post_downsample;
|
demod.rate_in *= demod.post_downsample;
|
||||||
|
|
||||||
@@ -1103,24 +1011,16 @@ void open_rtlsdr_stream(dsd_opts *opts)
|
|||||||
if (controller.freq_len > 1) demod.terminate_on_squelch = 0;
|
if (controller.freq_len > 1) demod.terminate_on_squelch = 0;
|
||||||
|
|
||||||
ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH;
|
ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH;
|
||||||
r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index);
|
|
||||||
|
|
||||||
|
r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index);
|
fprintf (stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index);
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
fprintf (stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index); //Kindly tell us which device we are using
|
fprintf (stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sigact.sa_handler = sighandler;
|
|
||||||
// sigemptyset(&sigact.sa_mask);
|
|
||||||
// sigact.sa_flags = 0;
|
|
||||||
// sigaction(SIGINT, &sigact, NULL);
|
|
||||||
// sigaction(SIGTERM, &sigact, NULL);
|
|
||||||
// sigaction(SIGQUIT, &sigact, NULL);
|
|
||||||
// sigaction(SIGPIPE, &sigact, NULL);
|
|
||||||
|
|
||||||
if (demod.deemph) {
|
if (demod.deemph) {
|
||||||
demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6)))));
|
demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6)))));
|
||||||
}
|
}
|
||||||
@@ -1128,9 +1028,11 @@ void open_rtlsdr_stream(dsd_opts *opts)
|
|||||||
/* Set the tuner gain */
|
/* Set the tuner gain */
|
||||||
if (dongle.gain == AUTO_GAIN) {
|
if (dongle.gain == AUTO_GAIN) {
|
||||||
verbose_auto_gain(dongle.dev);
|
verbose_auto_gain(dongle.dev);
|
||||||
|
fprintf (stderr, "Setting RTL Autogain. \n");
|
||||||
} else {
|
} else {
|
||||||
dongle.gain = nearest_gain(dongle.dev, dongle.gain);
|
dongle.gain = nearest_gain(dongle.dev, dongle.gain);
|
||||||
verbose_gain_set(dongle.dev, dongle.gain);
|
verbose_gain_set(dongle.dev, dongle.gain);
|
||||||
|
// fprintf (stderr, "Setting RTL Nearest Gain to %d. \n", dongle.gain); //seems to be working now
|
||||||
}
|
}
|
||||||
|
|
||||||
verbose_ppm_set(dongle.dev, dongle.ppm_error);
|
verbose_ppm_set(dongle.dev, dongle.ppm_error);
|
||||||
@@ -1142,7 +1044,8 @@ void open_rtlsdr_stream(dsd_opts *opts)
|
|||||||
usleep(100000);
|
usleep(100000);
|
||||||
pthread_create(&demod.thread, NULL, demod_thread_fn, (void*)(&demod));
|
pthread_create(&demod.thread, NULL, demod_thread_fn, (void*)(&demod));
|
||||||
pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void*)(&dongle));
|
pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void*)(&dongle));
|
||||||
pthread_create(&socket_freq, NULL, socket_thread_fn, (void *)(&controller));
|
//only create socket thread IF user specified (for legacy uses), else don't use it
|
||||||
|
if (port != 0) pthread_create(&socket_freq, NULL, socket_thread_fn, (void *)(&controller));
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup_rtlsdr_stream()
|
void cleanup_rtlsdr_stream()
|
||||||
@@ -1155,8 +1058,6 @@ void cleanup_rtlsdr_stream()
|
|||||||
safe_cond_signal(&output.ready, &output.ready_m);
|
safe_cond_signal(&output.ready, &output.ready_m);
|
||||||
safe_cond_signal(&controller.hop, &controller.hop_m);
|
safe_cond_signal(&controller.hop, &controller.hop_m);
|
||||||
pthread_join(controller.thread, NULL);
|
pthread_join(controller.thread, NULL);
|
||||||
//add socket thread
|
|
||||||
//pthread_join(socket_freq, NULL);
|
|
||||||
|
|
||||||
//dongle_cleanup(&dongle);
|
//dongle_cleanup(&dongle);
|
||||||
demod_cleanup(&demod);
|
demod_cleanup(&demod);
|
||||||
@@ -1164,32 +1065,70 @@ void cleanup_rtlsdr_stream()
|
|||||||
controller_cleanup(&controller);
|
controller_cleanup(&controller);
|
||||||
|
|
||||||
rtlsdr_close(dongle.dev);
|
rtlsdr_close(dongle.dev);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//void get_rtlsdr_sample(int16_t *sample)
|
//original for safe keeping
|
||||||
|
// void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
|
||||||
|
// {
|
||||||
|
// if (output.queue.empty())
|
||||||
|
// {
|
||||||
|
// safe_cond_wait(&output.ready, &output.ready_m);
|
||||||
|
// }
|
||||||
|
// pthread_rwlock_wrlock(&output.rw);
|
||||||
|
// *sample = output.queue.front() * volume_multiplier;
|
||||||
|
// output.queue.pop();
|
||||||
|
// pthread_rwlock_unlock(&output.rw);
|
||||||
|
// }
|
||||||
|
|
||||||
|
//find way to modify this function to allow hopping (tuning) while squelched and send 0 sample?
|
||||||
void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
|
void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
if (output.queue.empty())
|
if (output.queue.empty())
|
||||||
{
|
{
|
||||||
if (opts->use_ncurses_terminal == 1)
|
|
||||||
{
|
|
||||||
//ncursesPrinter(opts, state);
|
|
||||||
//printw ("\n\n\n HELP I'M LOCKED!\n");
|
|
||||||
}
|
|
||||||
safe_cond_wait(&output.ready, &output.ready_m);
|
safe_cond_wait(&output.ready, &output.ready_m);
|
||||||
//goto END;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_rwlock_wrlock(&output.rw);
|
pthread_rwlock_wrlock(&output.rw);
|
||||||
//*sample = output.queue.front();
|
*sample = output.queue.front() * volume_multiplier;
|
||||||
|
|
||||||
*sample = output.queue.front() * volume_multiplier; //try multiplying by numbers here for 'gain'
|
|
||||||
output.queue.pop();
|
output.queue.pop();
|
||||||
pthread_rwlock_unlock(&output.rw);
|
pthread_rwlock_unlock(&output.rw);
|
||||||
// END:
|
|
||||||
// if (1 == 2)
|
|
||||||
// {
|
|
||||||
// fprintf (stderr, "This is a dumb thing to fix");
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//function may lag since it isn't running as its own thread
|
||||||
|
void rtl_dev_tune(dsd_opts * opts, long int frequency)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "\nTuning to %lu Hz.", frequency);
|
||||||
|
dongle.freq = opts->rtlsdr_center_freq = frequency;
|
||||||
|
optimal_settings(dongle.freq, demod.rate_in);
|
||||||
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, " (Center Frequency: %u Hz.) \n", dongle.freq);
|
||||||
|
r = rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
||||||
|
if (r < 0)
|
||||||
|
fprintf (stderr, " (WARNING: Failed to set Center Frequency %u). \n", dongle.freq);
|
||||||
|
|
||||||
|
pthread_rwlock_wrlock(&output.rw); //prevent possible segfault when cleaning the queue
|
||||||
|
rtl_clean_queue();
|
||||||
|
pthread_rwlock_unlock(&output.rw); //resume
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//return RMS value (root means square) power level -- used as soft squelch inside of framesync
|
||||||
|
long int rtl_return_rms()
|
||||||
|
{
|
||||||
|
long int sr = 0;
|
||||||
|
#ifdef __arm__
|
||||||
|
sr = 100;
|
||||||
|
#else
|
||||||
|
sr = rms(demod.lowpassed, demod.lp_len, 1);
|
||||||
|
#endif
|
||||||
|
return (sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//simple function to clear the rtl sample queue when tuning and during other events (ncurses menu open/close)
|
||||||
|
void rtl_clean_queue()
|
||||||
|
{
|
||||||
|
//insert method to clear the entire queue to prevent sample 'lag'
|
||||||
|
std::queue<int16_t> empty; //create an empty queue
|
||||||
|
std::swap( output.queue, empty ); //swap in empty queue to effectively zero out current queue
|
||||||
|
}
|
||||||
539
src/trellis.cpp
539
src/trellis.cpp
@@ -1,539 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 by Jonathan Naylor, G4KLX
|
|
||||||
*
|
|
||||||
* Heavy Modificatins of Original Code to work for DSD-FME
|
|
||||||
*
|
|
||||||
* 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; version 2 of the License.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
const unsigned int INTERLEAVE_TABLE[] = {
|
|
||||||
0U, 1U, 8U, 9U, 16U, 17U, 24U, 25U, 32U, 33U, 40U, 41U, 48U, 49U, 56U, 57U, 64U, 65U, 72U, 73U, 80U, 81U, 88U, 89U, 96U, 97U,
|
|
||||||
2U, 3U, 10U, 11U, 18U, 19U, 26U, 27U, 34U, 35U, 42U, 43U, 50U, 51U, 58U, 59U, 66U, 67U, 74U, 75U, 82U, 83U, 90U, 91U,
|
|
||||||
4U, 5U, 12U, 13U, 20U, 21U, 28U, 29U, 36U, 37U, 44U, 45U, 52U, 53U, 60U, 61U, 68U, 69U, 76U, 77U, 84U, 85U, 92U, 93U,
|
|
||||||
6U, 7U, 14U, 15U, 22U, 23U, 30U, 31U, 38U, 39U, 46U, 47U, 54U, 55U, 62U, 63U, 70U, 71U, 78U, 79U, 86U, 87U, 94U, 95U};
|
|
||||||
|
|
||||||
const unsigned char ENCODE_TABLE[] = {
|
|
||||||
0U, 8U, 4U, 12U, 2U, 10U, 6U, 14U,
|
|
||||||
4U, 12U, 2U, 10U, 6U, 14U, 0U, 8U,
|
|
||||||
1U, 9U, 5U, 13U, 3U, 11U, 7U, 15U,
|
|
||||||
5U, 13U, 3U, 11U, 7U, 15U, 1U, 9U,
|
|
||||||
3U, 11U, 7U, 15U, 1U, 9U, 5U, 13U,
|
|
||||||
7U, 15U, 1U, 9U, 5U, 13U, 3U, 11U,
|
|
||||||
2U, 10U, 6U, 14U, 0U, 8U, 4U, 12U,
|
|
||||||
6U, 14U, 0U, 8U, 4U, 12U, 2U, 10U};
|
|
||||||
|
|
||||||
const unsigned char DECODE_TABLE[] = { //flip the rows and columns, right?
|
|
||||||
0,4,1,5,3,7,2,6,
|
|
||||||
8,12,9,13,11,15,10,14,
|
|
||||||
4,2,5,3,7,1,6,0,
|
|
||||||
12,10,13,11,15,9,14,8,
|
|
||||||
2,6,3,7,1,5,0,4,
|
|
||||||
10,14,11,15,9,13,8,12,
|
|
||||||
6,0,7,1,5,3,4,2,
|
|
||||||
14,8,15,9,13,11,12,10
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
|
||||||
|
|
||||||
#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
|
||||||
#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
|
||||||
|
|
||||||
unsigned char state = 0U;
|
|
||||||
//rework quickHamming to accept unsigned intergers instead and go bit by bit
|
|
||||||
int quickHamming(unsigned int butt, unsigned int farts)
|
|
||||||
{
|
|
||||||
unsigned int i = 0, count = 0, shit = 0, shart = 0;
|
|
||||||
//fprintf (stderr, "Distance Compare %X - %X \n", butt, farts );
|
|
||||||
for (i; i < 3; i++) //shift three places tops;
|
|
||||||
{
|
|
||||||
shit = (butt >> i) & 0b1;
|
|
||||||
shart = (farts >> i) & 0b1;
|
|
||||||
if (shit != shart)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload)
|
|
||||||
{
|
|
||||||
//unsigned int bestPos = 0U;
|
|
||||||
//unsigned int bestVal = 0U;
|
|
||||||
|
|
||||||
for (unsigned j = 0U; j < 20U; j++) {
|
|
||||||
unsigned int bestPos = 0U;
|
|
||||||
unsigned int bestVal = 0U;
|
|
||||||
//fprintf (stderr, " fail%d-", failPos);
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < 16U; i++) {
|
|
||||||
points[failPos] = i;
|
|
||||||
|
|
||||||
//fprintf (stderr, "i%d", i);
|
|
||||||
|
|
||||||
unsigned char tribits[49U];
|
|
||||||
unsigned int pos = CDMRTrellisCheckCode(points, tribits);
|
|
||||||
if (pos == 999U) {
|
|
||||||
CDMRTrellisTribitsToBits(tribits, payload);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos > bestPos) {
|
|
||||||
bestPos = pos;
|
|
||||||
bestVal = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
points[failPos] = bestVal;
|
|
||||||
failPos = bestPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* payload)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
unsigned char bits[144];
|
|
||||||
for (int i = 0; i < 48; i++) {
|
|
||||||
bits[(i * 3) + 0] = (tribits[i] >> 2) & 1;
|
|
||||||
bits[(i * 3) + 1] = (tribits[i] >> 1) & 1;
|
|
||||||
bits[(i * 3) + 2] = tribits[i] & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// convert bits to bytes
|
|
||||||
//fprintf (stderr, "\n Tribit Binary Sequence\n ");
|
|
||||||
for (short i = 0; i < 144; i++)
|
|
||||||
{
|
|
||||||
//fprintf (stderr, "%b", bits[i]);
|
|
||||||
}
|
|
||||||
for(short int i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
payload[i] = (bits[i * 8 + 0] << 7) | (bits[i * 8 + 1] << 6) | (bits[i * 8 + 2] << 5) | (bits[i * 8 + 3] << 4) |
|
|
||||||
(bits[i * 8 + 4] << 3) | (bits[i * 8 + 5] << 2) | (bits[i * 8 + 6] << 1) | (bits[i * 8 + 7] << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//for (int i = 0; i < 144; i++)
|
|
||||||
//payload[i/8] = (payload[i/8] << 1) | bits[i];
|
|
||||||
//DmrDataBit[j + 0] = (TrellisReturn[17-i] >> 0) & 0x01; //example
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits)
|
|
||||||
{
|
|
||||||
|
|
||||||
//unsigned char state = 0U; //move outside of function to preserve state for every iteration
|
|
||||||
//let's try reversing the points to pass through the decode table instead of flipping them here,
|
|
||||||
//it may mess up the fixcode and failpos POS
|
|
||||||
state = 0U;
|
|
||||||
for (unsigned int i = 0U; i < 49U;) //total number of points to process
|
|
||||||
{
|
|
||||||
tribits[i] = 9U;
|
|
||||||
for (unsigned int j = 0U; j < 8U; j++)
|
|
||||||
{
|
|
||||||
//if (points[i] == DECODE_TABLE[state * 8U + j])
|
|
||||||
//if (points[48-i] == DECODE_TABLE[state * 8U + j])
|
|
||||||
if (points[i] == ENCODE_TABLE[state * 8U + j])
|
|
||||||
{
|
|
||||||
tribits[i] = j;
|
|
||||||
state = tribits[i]; //carry over if found right away
|
|
||||||
i++; //increment i by one value
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//try to find shortest distance for surviving paths and decide on one
|
|
||||||
//seems to be that two paths will have distance value 1, and two will have distance value 2
|
|
||||||
//need to keep totaling up a few rounds before we can eliminate a path and resume?
|
|
||||||
if (tribits[i] == 9U)
|
|
||||||
{
|
|
||||||
int distance = 0;
|
|
||||||
int maxdistance = 99; //random metric for two position distance
|
|
||||||
int pathdistance = 0;
|
|
||||||
unsigned int match = 0;
|
|
||||||
unsigned int nextmatch = 0;
|
|
||||||
unsigned int q = 0;
|
|
||||||
//only 4 possible states for each point, but in 8 (0-7) FSM states
|
|
||||||
char path[99][99] = {0}; //[i][pathcount] = distance
|
|
||||||
int pathcount = 0;
|
|
||||||
for (unsigned int o = 0; o < 64; o++) //check entire state transition table for matches
|
|
||||||
{
|
|
||||||
if (points[i] == ENCODE_TABLE[o]) //ENCODE TABLE
|
|
||||||
{
|
|
||||||
match = o / (unsigned int)8; //get dividend no remainder/decimal I hope, get fsm state 0-7
|
|
||||||
//fprintf (stderr, "\n Point #%d [%d] Match State %d Position %d Tribit Value %d \n", i, points[i], match, o, o % (unsigned int)8);
|
|
||||||
distance = quickHamming(match, state);
|
|
||||||
//fprintf (stderr, "Raw Distance Current %d\n", distance);
|
|
||||||
path[i][pathcount] = distance ; //save which table values (paths) match as possible values and distance
|
|
||||||
for (q; q < 64; q++) //check entire state transition table for next point match
|
|
||||||
{
|
|
||||||
if (points[i+1] == ENCODE_TABLE[q]) //ENCODE TABLE
|
|
||||||
{
|
|
||||||
nextmatch = q / (unsigned int)8;
|
|
||||||
path[i+1][pathcount] = quickHamming(nextmatch, q % (unsigned int)8);
|
|
||||||
//fprintf (stderr, "Raw Distance Next %d\n", path[i+1][pathcount]);
|
|
||||||
pathcount++;
|
|
||||||
//fprintf (stderr, "State 0 Match %d Distance %d \n", o, distance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (short int z = 0; z <= pathcount; z++) //add metrics on paths to find shortest distance
|
|
||||||
{
|
|
||||||
pathdistance = path[i][0] + path[i+1][z];
|
|
||||||
//fprintf (stderr, "Total Path Distance %d\n", pathdistance);
|
|
||||||
if (pathdistance <= maxdistance) //equal too in case of tie?
|
|
||||||
{
|
|
||||||
maxdistance = pathdistance;
|
|
||||||
tribits[i] = o % (unsigned int)8; //remainder value will be tribit value on table
|
|
||||||
if (i < 49) //just in case we are on 49 already, don't want to crash or something.
|
|
||||||
{
|
|
||||||
tribits[i+1] = q % (unsigned int)8; //remainder value will be tribit value on table
|
|
||||||
}
|
|
||||||
|
|
||||||
//fprintf (stderr, "Point #%d [%d] - Tribit Candidate %d\n", i, points[i], tribits[i] );
|
|
||||||
//fprintf (stderr, "Point #%d [%d]- Tribit Candidate %d\n", i+1, points[i+1], tribits[i+1] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//fprintf (stderr, "Selected Point #%d [%d] - Tribit Selection %d\n", i, points[i], tribits[i] );
|
|
||||||
//fprintf (stderr, "Selected Point #%d [%d]- Tribit Selection %d\n", i+1, points[i+1], tribits[i+1] );
|
|
||||||
}
|
|
||||||
state = tribits[i+1]; //not quite sure if this will be needed or not, if so, set state to last found value
|
|
||||||
i += 2; //increment i by two since we already got the next tribit value as well;
|
|
||||||
//state = tribits[i]; //not quite sure if this will be needed or not, if so, set state to last found value
|
|
||||||
//i++; //increment i by two since we already got the next tribit value as well;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return 999U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
signed char dibit;
|
|
||||||
//can only assume this is giving the correct dibits out, and not inverse or something.
|
|
||||||
/*
|
|
||||||
for (int i = 0; i < 98; i++) {
|
|
||||||
n = i * 2;
|
|
||||||
if (n >= 98) n += 68;
|
|
||||||
bool b1 = data[n] != 0x0;
|
|
||||||
|
|
||||||
n = i * 2 + 1;
|
|
||||||
if (n >= 98) n += 68;
|
|
||||||
bool b2 = data[n] != 0x0;
|
|
||||||
|
|
||||||
if (!b1 && b2)
|
|
||||||
dibit = +3; // 1 - 01
|
|
||||||
else if (!b1 && !b2)
|
|
||||||
dibit = +1; // 0 - 00
|
|
||||||
else if (b1 && !b2)
|
|
||||||
dibit = -1; // 2 - 10
|
|
||||||
else
|
|
||||||
dibit = -3; // 3 - 11
|
|
||||||
|
|
||||||
*/
|
|
||||||
//Keep it simple!
|
|
||||||
for (int i = 0; i < 98; i++)
|
|
||||||
{
|
|
||||||
n = INTERLEAVE_TABLE[97-i];
|
|
||||||
//n = i;
|
|
||||||
if (data[i] == 0)
|
|
||||||
{
|
|
||||||
dibits[n] = 1; //1
|
|
||||||
}
|
|
||||||
if (data[i] == 1)
|
|
||||||
{
|
|
||||||
dibits[n] = 3; //3
|
|
||||||
}
|
|
||||||
if (data[i] == 2)
|
|
||||||
{
|
|
||||||
dibits[n] = -1; //-1
|
|
||||||
}
|
|
||||||
if (data[i] == 3)
|
|
||||||
{
|
|
||||||
dibits[n] = -3; //-3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//n = INTERLEAVE_TABLE[i]; //should this go in reverse?
|
|
||||||
//n = INTERLEAVE_TABLE[97-i]; //should this go in reverse?
|
|
||||||
//dibits[n] = dibit; //should this go in reverse too? not sure on this one, but definitely the INTERLEAVE_TABLE
|
|
||||||
//n = INTERLEAVE_TABLE[97-i]; //should this go in reverse?
|
|
||||||
//dibits[97-n] = dibit;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++) {
|
|
||||||
if (dibits[i * 2U + 0U] == +1 && dibits[i * 2U + 1U] == -1)
|
|
||||||
points[i] = 0U;
|
|
||||||
else if (dibits[i * 2U + 0U] == -1 && dibits[i * 2U + 1U] == -1)
|
|
||||||
points[i] = 1U;
|
|
||||||
else if (dibits[i * 2U + 0U] == +3 && dibits[i * 2U + 1U] == -3)
|
|
||||||
points[i] = 2U;
|
|
||||||
else if (dibits[i * 2U + 0U] == -3 && dibits[i * 2U + 1U] == -3)
|
|
||||||
points[i] = 3U;
|
|
||||||
else if (dibits[i * 2U + 0U] == -3 && dibits[i * 2U + 1U] == -1)
|
|
||||||
points[i] = 4U;
|
|
||||||
else if (dibits[i * 2U + 0U] == +3 && dibits[i * 2U + 1U] == -1)
|
|
||||||
points[i] = 5U;
|
|
||||||
else if (dibits[i * 2U + 0U] == -1 && dibits[i * 2U + 1U] == -3)
|
|
||||||
points[i] = 6U;
|
|
||||||
else if (dibits[i * 2U + 0U] == +1 && dibits[i * 2U + 1U] == -3)
|
|
||||||
points[i] = 7U;
|
|
||||||
else if (dibits[i * 2U + 0U] == -3 && dibits[i * 2U + 1U] == +3)
|
|
||||||
points[i] = 8U;
|
|
||||||
else if (dibits[i * 2U + 0U] == +3 && dibits[i * 2U + 1U] == +3)
|
|
||||||
points[i] = 9U;
|
|
||||||
else if (dibits[i * 2U + 0U] == -1 && dibits[i * 2U + 1U] == +1)
|
|
||||||
points[i] = 10U;
|
|
||||||
else if (dibits[i * 2U + 0U] == +1 && dibits[i * 2U + 1U] == +1)
|
|
||||||
points[i] = 11U;
|
|
||||||
else if (dibits[i * 2U + 0U] == +1 && dibits[i * 2U + 1U] == +3)
|
|
||||||
points[i] = 12U;
|
|
||||||
else if (dibits[i * 2U + 0U] == -1 && dibits[i * 2U + 1U] == +3)
|
|
||||||
points[i] = 13U;
|
|
||||||
else if (dibits[i * 2U + 0U] == +3 && dibits[i * 2U + 1U] == +1)
|
|
||||||
points[i] = 14U;
|
|
||||||
else if (dibits[i * 2U + 0U] == -3 && dibits[i * 2U + 1U] == +1)
|
|
||||||
points[i] = 15U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDMRTrellisPointsToDibits(const unsigned char* points, signed char* dibits)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++) {
|
|
||||||
switch (points[i]) {
|
|
||||||
case 0U:
|
|
||||||
dibits[i * 2U + 0U] = +1;
|
|
||||||
dibits[i * 2U + 1U] = -1;
|
|
||||||
break;
|
|
||||||
case 1U:
|
|
||||||
dibits[i * 2U + 0U] = -1;
|
|
||||||
dibits[i * 2U + 1U] = -1;
|
|
||||||
break;
|
|
||||||
case 2U:
|
|
||||||
dibits[i * 2U + 0U] = +3;
|
|
||||||
dibits[i * 2U + 1U] = -3;
|
|
||||||
break;
|
|
||||||
case 3U:
|
|
||||||
dibits[i * 2U + 0U] = -3;
|
|
||||||
dibits[i * 2U + 1U] = -3;
|
|
||||||
break;
|
|
||||||
case 4U:
|
|
||||||
dibits[i * 2U + 0U] = -3;
|
|
||||||
dibits[i * 2U + 1U] = -1;
|
|
||||||
break;
|
|
||||||
case 5U:
|
|
||||||
dibits[i * 2U + 0U] = +3;
|
|
||||||
dibits[i * 2U + 1U] = -1;
|
|
||||||
break;
|
|
||||||
case 6U:
|
|
||||||
dibits[i * 2U + 0U] = -1;
|
|
||||||
dibits[i * 2U + 1U] = -3;
|
|
||||||
break;
|
|
||||||
case 7U:
|
|
||||||
dibits[i * 2U + 0U] = +1;
|
|
||||||
dibits[i * 2U + 1U] = -3;
|
|
||||||
break;
|
|
||||||
case 8U:
|
|
||||||
dibits[i * 2U + 0U] = -3;
|
|
||||||
dibits[i * 2U + 1U] = +3;
|
|
||||||
break;
|
|
||||||
case 9U:
|
|
||||||
dibits[i * 2U + 0U] = +3;
|
|
||||||
dibits[i * 2U + 1U] = +3;
|
|
||||||
break;
|
|
||||||
case 10U:
|
|
||||||
dibits[i * 2U + 0U] = -1;
|
|
||||||
dibits[i * 2U + 1U] = +1;
|
|
||||||
break;
|
|
||||||
case 11U:
|
|
||||||
dibits[i * 2U + 0U] = +1;
|
|
||||||
dibits[i * 2U + 1U] = +1;
|
|
||||||
break;
|
|
||||||
case 12U:
|
|
||||||
dibits[i * 2U + 0U] = +1;
|
|
||||||
dibits[i * 2U + 1U] = +3;
|
|
||||||
break;
|
|
||||||
case 13U:
|
|
||||||
dibits[i * 2U + 0U] = -1;
|
|
||||||
dibits[i * 2U + 1U] = +3;
|
|
||||||
break;
|
|
||||||
case 14U:
|
|
||||||
dibits[i * 2U + 0U] = +3;
|
|
||||||
dibits[i * 2U + 1U] = +1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dibits[i * 2U + 0U] = -3;
|
|
||||||
dibits[i * 2U + 1U] = +1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDMRTrellisBitsToTribits(const unsigned char* payload, unsigned char* tribits)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0U; i < 48U; i++) {
|
|
||||||
unsigned int n = 143U - i * 3U;
|
|
||||||
|
|
||||||
bool b1 = READ_BIT(payload, n) != 0x00U;
|
|
||||||
n--;
|
|
||||||
bool b2 = READ_BIT(payload, n) != 0x00U;
|
|
||||||
n--;
|
|
||||||
bool b3 = READ_BIT(payload, n) != 0x00U;
|
|
||||||
|
|
||||||
unsigned char tribit = 0U;
|
|
||||||
tribit |= b1 ? 4U : 0U;
|
|
||||||
tribit |= b2 ? 2U : 0U;
|
|
||||||
tribit |= b3 ? 1U : 0U;
|
|
||||||
|
|
||||||
//tribits[i] = tribit;
|
|
||||||
}
|
|
||||||
|
|
||||||
tribits[48U] = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload)
|
|
||||||
{
|
|
||||||
assert(data != NULL);
|
|
||||||
assert(payload != NULL);
|
|
||||||
unsigned char tribits[49U];
|
|
||||||
for (short i = 0; i < 49; i++)
|
|
||||||
{
|
|
||||||
tribits[i] = 0; //initialize with 0 values so we know later on if these values are actually being assigned or not
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n Raw Trellis Dibits inside Decoder\n ");
|
|
||||||
for (short i = 0; i < 98; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "#%d [%0d] ", i, data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
signed char dibits[98U];
|
|
||||||
CDMRTrellisDeinterleave(data, dibits);
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n Trellis Deinterleaved Dibits inside Decoder (converted to signed)\n ");
|
|
||||||
for (short i = 0; i < 98; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "#%d [%d] ", i, dibits[i]); //signed char, values should be -3, -1, 1, 3 only
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unsigned char points[49U];
|
|
||||||
unsigned char reverse_points[49U];
|
|
||||||
CDMRTrellisDibitsToPoints(dibits, points);
|
|
||||||
//maybe keep point 49 at 49, since its the flush?
|
|
||||||
//fprintf (stderr, "\n Trellis Reverse Points inside Decoder\n ");
|
|
||||||
for (short i = 0; i < 49; i++)
|
|
||||||
{
|
|
||||||
reverse_points[i] = points[48-i];
|
|
||||||
//fprintf (stderr, "#%d [%02u] ", i, reverse_points[i]);
|
|
||||||
}
|
|
||||||
//reverse_points[48] = points[48];
|
|
||||||
//fprintf (stderr, "#%d [%02u] ", 48, reverse_points[48]);
|
|
||||||
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n Trellis Points inside Decoder\n ");
|
|
||||||
for (short i = 0; i < 49; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "#%d [%02u] ", i, points[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check the original code
|
|
||||||
|
|
||||||
unsigned int failPos = CDMRTrellisCheckCode(points, tribits);
|
|
||||||
//unsigned int failPos = CDMRTrellisCheckCode(reverse_points, tribits); //stack smashing detected terminated
|
|
||||||
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n Check Code Trellis Tribits inside Decoder\n ");
|
|
||||||
for (short i = 0; i < 49; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "#%d [%0u] ", i, tribits[i]); //shouldn't have any values greater that 0x7?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failPos == 999U)
|
|
||||||
{
|
|
||||||
CDMRTrellisTribitsToBits(tribits, payload);
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\nTrellis Payload Tribits\n %s ", KCYN);
|
|
||||||
for (short i = 0; i < 49; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "#%d [%0u] ", i, tribits[i]); //shouldn't have any values greater that 0x7?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\nTrellis Payload Tribits to Bytes inside Decoder\n %s ", KCYN);
|
|
||||||
for (short i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", payload[i]);
|
|
||||||
}
|
|
||||||
fprintf (stderr,"%s", KNRM); //change back to normal
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char savePoints[49U];
|
|
||||||
for (unsigned int i = 0U; i < 49U; i++)
|
|
||||||
savePoints[i] = points[i];
|
|
||||||
//savePoints[i] = reverse_points[i];
|
|
||||||
|
|
||||||
bool ret = CDMRTrellisFixCode(points, failPos, payload);
|
|
||||||
//bool ret = CDMRTrellisFixCode(reverse_points, failPos, payload);
|
|
||||||
if (ret){
|
|
||||||
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n if return Trellis Tribits inside Decoder\n ");
|
|
||||||
for (short i = 0; i < 49; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "#%d [%0u] ", i, tribits[i]); //shouldn't have any values greater that 0x7?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\nTrellis Payload Tribits to Bits fix return true\n "); //this one seems to be happening
|
|
||||||
for (short i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", payload[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true; }
|
|
||||||
|
|
||||||
if (failPos == 0U)
|
|
||||||
{
|
|
||||||
if (1 == 2)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\nTrellis Payload Tribits to Bytes failPos == 0U\n ");
|
|
||||||
for (short i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", payload[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false; }
|
|
||||||
|
|
||||||
// Backtrack one place for a last go
|
|
||||||
return CDMRTrellisFixCode(savePoints, failPos - 1U, payload);
|
|
||||||
}
|
|
||||||
@@ -630,7 +630,7 @@ processX2TDMAvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
if ((eeei == 0) && (aiei == 0))
|
if ((eeei == 0) && (aiei == 0))
|
||||||
{
|
{
|
||||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
// processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
||||||
}
|
}
|
||||||
if (opts->p25enc == 1)
|
if (opts->p25enc == 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
pacmd load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
pactl load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
||||||
pacmd load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
|
pactl load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user