mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 09:17:20 -05:00
Compare commits
174 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
acb5c16c30 | ||
|
|
d16e775e46 | ||
|
|
11d4c8c49c | ||
|
|
1477880720 | ||
|
|
9290098081 | ||
|
|
eeb6924369 | ||
|
|
c02d7c0ba0 | ||
|
|
324191d79f | ||
|
|
e613b85829 | ||
|
|
49f81da4df | ||
|
|
826c99d563 | ||
|
|
2441664b19 | ||
|
|
5c733c0660 | ||
|
|
73824084ad | ||
|
|
209d4e039e | ||
|
|
fa4e84bc77 | ||
|
|
4946897914 | ||
|
|
57e3f518dd | ||
|
|
08a548339c | ||
|
|
36c337ede8 | ||
|
|
f6bb325dac | ||
|
|
626869992e | ||
|
|
5422b337bb | ||
|
|
b6f270be05 | ||
|
|
3ea813ebbc | ||
|
|
53980c98ed | ||
|
|
536372d877 | ||
|
|
896f3a8533 | ||
|
|
68bbea64d5 | ||
|
|
711a4cb871 | ||
|
|
8fd1d8fe35 | ||
|
|
23c396cbe8 | ||
|
|
e91c21fadb | ||
|
|
15a8f80344 | ||
|
|
5130899a67 | ||
|
|
f5ac9ff0b2 | ||
|
|
14dd045312 | ||
|
|
fbb00f78fe | ||
|
|
a5d23d9e12 | ||
|
|
a2b3da5b5a | ||
|
|
a9417afd67 | ||
|
|
438f89bf4c | ||
|
|
2f2eecddfc | ||
|
|
2fceee5a2f | ||
|
|
62c70cbf92 | ||
|
|
b97014ff0b | ||
|
|
68edb9463c | ||
|
|
178f88a28f | ||
|
|
f7d103c6a2 | ||
|
|
10d10063f0 | ||
|
|
dba3b79266 | ||
|
|
0441b8fdbd | ||
|
|
ea399e50a8 | ||
|
|
66f710e877 | ||
|
|
0f227e40c3 | ||
|
|
136ed75342 | ||
|
|
61cefa6a87 | ||
|
|
cc7504c280 | ||
|
|
07139b3d7d | ||
|
|
638f212525 | ||
|
|
9f624e0b62 | ||
|
|
eefe1bac5e | ||
|
|
532c1cc3b6 | ||
|
|
b1c40ed6e7 | ||
|
|
94dbd1512d | ||
|
|
c73d45d553 | ||
|
|
d1efa05164 | ||
|
|
7af5ffe9fa | ||
|
|
8141ea2e78 | ||
|
|
ec4ace1514 | ||
|
|
eb5cbaad93 | ||
|
|
f5d0575978 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -2,3 +2,8 @@
|
|||||||
*.so*
|
*.so*
|
||||||
*.dylib
|
*.dylib
|
||||||
build
|
build
|
||||||
|
*.sh
|
||||||
|
*.csv
|
||||||
|
examples
|
||||||
|
*.bin
|
||||||
|
*.ans
|
||||||
|
|||||||
@@ -1,11 +1,50 @@
|
|||||||
project(dsd-fme)
|
#Ubuntu 20.04 uses 3.16.3
|
||||||
cmake_minimum_required(VERSION 2.8.11)
|
#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-zdev)
|
||||||
|
|
||||||
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 -DNXDN=ON to enable new NXDN Sync Pattern Testing
|
||||||
|
option(NXDN
|
||||||
|
"Build with new NXDN Sync Pattern Testing Enabled" OFF)
|
||||||
|
if (NXDN)
|
||||||
|
add_definitions(-DNXDNTESTSYNC)
|
||||||
|
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 ()
|
||||||
|
|
||||||
include(git_revision)
|
include(git_revision)
|
||||||
git_describe(GIT_TAG)
|
git_describe(GIT_TAG)
|
||||||
|
|
||||||
@@ -14,7 +53,7 @@ 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)
|
||||||
|
|
||||||
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})
|
||||||
@@ -38,24 +77,24 @@ list(APPEND SRCS "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c")
|
|||||||
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/include")
|
include_directories("${PROJECT_SOURCE_DIR}/include")
|
||||||
|
|
||||||
ADD_EXECUTABLE(dsd-fme ${SRCS} ${HEADERS})
|
ADD_EXECUTABLE(dsd-fme-zdev ${SRCS} ${HEADERS})
|
||||||
TARGET_LINK_LIBRARIES(dsd-fme ${LIBS})
|
TARGET_LINK_LIBRARIES(dsd-fme-zdev ${LIBS})
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
install(TARGETS dsd-fme DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS dsd-fme-zdev DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
# man page
|
# man page
|
||||||
find_program(HELP2MAN_FOUND help2man)
|
find_program(HELP2MAN_FOUND help2man)
|
||||||
if (HELP2MAN_FOUND)
|
if (HELP2MAN_FOUND)
|
||||||
add_custom_command(TARGET dsd-fme POST_BUILD
|
add_custom_command(TARGET dsd-fme-zdev POST_BUILD
|
||||||
COMMAND help2man
|
COMMAND help2man
|
||||||
ARGS -n "Digital Speech Decoder"
|
ARGS -n "Digital Speech Decoder"
|
||||||
--version-string=${GIT_TAG}
|
--version-string=${GIT_TAG}
|
||||||
-o ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme.1
|
-o ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme-zdev.1
|
||||||
--no-info
|
--no-info
|
||||||
$<TARGET_FILE:dsd-fme>
|
$<TARGET_FILE:dsd-fme-zdev>
|
||||||
)
|
)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme-zdev.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# uninstall target
|
# uninstall target
|
||||||
|
|||||||
@@ -27,3 +27,7 @@ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|||||||
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.
|
||||||
|
|
||||||
|
# Digital Speech Decoder - Florida Man Edition
|
||||||
|
|
||||||
|
This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [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"). 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 hrh17 for the many hours of wav samples submitted by them.
|
||||||
|
|||||||
249
README.md
249
README.md
@@ -1,248 +1,8 @@
|
|||||||
# Notice - This branch is now considered 'legacy' and the master branch will be considered the 'main' branch going forward.
|
## Notice
|
||||||
|
|
||||||
# Digital Speech Decoder - Florida Man Edition
|
This branch is for development and experimentation. Results are not gauranteed to be stable.
|
||||||
|
This branch will also attempt to consolidate the code bases for 'Main' and 'Aero' into one codeset
|
||||||
This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [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"). 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 hrh17 for the many hours of wav samples submitted by them.
|
with the differences needed for Aero (Cygwin) handled by cmake option `cmake -DAERO=ON ..` when building.
|
||||||
|
|
||||||
## 2022.10.01 Update ##
|
|
||||||
|
|
||||||
Very Experimental (Mostly Just Sampling Speed and using built in QPSK demodulator) for Phase 2 CQPSK 6000 sps input over disc tap inputs (SDR++, GQRX, WAV files). Promising results, but some errors in decoding as expected. I've had some pretty good luck using SDR++ to listen to and decode Duke Energy TDMA Control Channel through the virtual sink. Experimental CQPSK can be toggled in Ncurses Menu and also via command line switch `-f2 -m2` but keep in mind this will only work with Phase 2 RF channels using CQPSK, and not a combined decoding.
|
|
||||||
|
|
||||||
Also, added some new iden_up PDUs and other odd and end P25 PDU's (still far from complete). Ncurses Terminal and Console output will now show relevant frequencies for CC and Voice channels if iden_up commands are present.
|
|
||||||
|
|
||||||
DSD-FME, and The Florida Man Labs will be taking a hiatus period from straigth development of this project. Currently, I feel we are at a bit of a stand still as far as what can be done with the project without new demodulation, dibit buffer, audio, and input methods. DSD-FME will continue to receive maintenance updates for simple bug fixes and also any major bug issues that cause crashing, etc., but I am reluctant to continue developing with the current underlying issues mentioned above. I am going to go into a period of hiatus with this project and do lots of experimentation, learning, and testing to see what viable options are out there or need to be created from scratch. I feel like I've learned a lot, achieved a lot, made tons of mistakes, learned from a few of them, but ultimately improved on the DSD project. DSD-FME will continue to receive replies and support in any opened issues but will currently only extend to help in setting up and using the software, on top of regular minor maintenance and any crash related issues.
|
|
||||||
|
|
||||||
## 2022.09.28 Mini Update ##
|
|
||||||
Back by popular demand, I've re-implemented DMR Stereo as a standalone decoder. DMR Stereo is also still available in XDMA decoding for P25 1, 2, and DMR BS/MS Simplex as well.
|
|
||||||
|
|
||||||
DMR Stereo `dsd-fme -fs`
|
|
||||||
XDMA `dsd-fme -ft`
|
|
||||||
|
|
||||||
It can also be selected in the NCurses Terminal Menu. Sadly I had to remove the X2-TDMA decoder option in the menu, I'm sure nobody will miss it.
|
|
||||||
|
|
||||||
## 2022.09.17 Update ##
|
|
||||||
P25 Phase 2 Audio decoding has been implemented into DSD-FME. Currently, Phase 2 will only work from OP25 symbol capture bin files, or from wav files/SDR applications/RTL input that are sourced from FSK4 sources. CQPSK (LSM/H-D8PSK) will not work from audio sources. With the addition of Phases 2 audio, a new default decoder class has been implemented, XDMA, which is P25 Phase 1, Phase 2, DMR BS and MS (DMR Stereo). Furthermore, very limited Phase 1 TSBK support and Phase 2 FACCH/SACCH/LCCH has been worked in just to get Network Status Broadcasts, which can fill in the required P2 parameters for WACN, SYSID, and CC for Phase 2 frame de-scrambling, and active voice call information.
|
|
||||||
|
|
||||||
With the implementation of the XDMA decoder class (default decoder), the CLI switches for DMR Stereo has been repurposed. -T option will now open a per call wav file saving when used with NCurses Terminal, otherwise it will write wav files to slot 1 and slot 2 wav files.
|
|
||||||
|
|
||||||
Anybody using `dsd-fme -fr -T` ...... should now just use `dsd-fme -ft` instead!
|
|
||||||
|
|
||||||
To see the up to date CLI options, please look at the help options with the -h CLI option.
|
|
||||||
|
|
||||||
## 2022.08.12 Update ##
|
|
||||||
A new menu system has been introduced in the NCURSES Terminal. Also includes support for LRRP to text file type of choice and reading OP25 symbol capture bin files.
|
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=TqTfAfaPJ4s "DSD-FME Update 2022.08.12")
|
|
||||||
|
|
||||||
To get started with the new menu system, simply launch with:
|
|
||||||
|
|
||||||
`dsd-fme -N 2> log.txt`
|
|
||||||
|
|
||||||
## 2022.06.20 Update - Current Users Please Read!! ##
|
|
||||||
The executable for this project has been changed from `dsd` to `dsd-fme` so after pulling or cloning the lastest version, make sure to call the software with `dsd-fme`.
|
|
||||||
|
|
||||||
## 2022.05.05 Update ##
|
|
||||||
DMR Stereo method added for listening to voice audio in both TDMA channels/slots simultaneously.
|
|
||||||
|
|
||||||
### Example Usage and Note!
|
|
||||||
`dsd-fme` or `./dsd-fme` may need to be used in your usage, depending on if you are running it from the build folder, or if you have run make install. You will need to tweak the examples to your particular usage. Users can always check `dsd-fme -h` for help and all command line switches to use.
|
|
||||||
|
|
||||||
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, P25 (1 and 2) and X2-TDMA decoding. 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, DMR BS/MS, X2-TDMA)
|
|
||||||
-fs DMR Stereo BS/MS Simplex
|
|
||||||
-fa Legacy Auto
|
|
||||||
-fi NXDN48
|
|
||||||
-fn NXDN96
|
|
||||||
-fp ProVoice
|
|
||||||
-fm dPMR, also may need to use -xd if inverted dPMR.
|
|
||||||
-fr DMR, also may need to use -xr if inverted DMR.
|
|
||||||
-f1 P25P1
|
|
||||||
-fx X2-TDMA
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example Usage - New XDMA Decoder, Ncurses Terminal, Pulse Input/Output, and Log Console to file
|
|
||||||
`dsd-fme -ft -N 2> voice.log`
|
|
||||||
|
|
||||||
and in a second terminal tab, same folder, run
|
|
||||||
|
|
||||||
`tail -n 40 -f voice.log`
|
|
||||||
|
|
||||||
Then you can open up your pavucontrol "Pulse Audio Volume Control" or "Volume Control" application and route input into DSD-FME from any application and DSD-FME output to the left and right speakers respectively. (unlock the channel in the application stream and adjust left and right independently)
|
|
||||||
|
|
||||||
## Example Usage - RTL2832 Dongle Input
|
|
||||||
`dsd-fme -fi -i rtl -c 154.9875M -P -2 -D 1 -G 36 -L 70 -U 6021 -Y 12`
|
|
||||||
|
|
||||||
```
|
|
||||||
-i rtl to use rtl_fm (default is -i pulse for pulse audio)
|
|
||||||
|
|
||||||
-o pulse to set output to pulse audio (excluded, is default anyways)
|
|
||||||
|
|
||||||
-c Set frequency
|
|
||||||
|
|
||||||
-P set PPM error (default = 0)
|
|
||||||
|
|
||||||
-D set device index number (default = 0; first device detected)
|
|
||||||
|
|
||||||
-G set device gain (0-49) (default = 0 Auto Gain)
|
|
||||||
|
|
||||||
-L set rtl squelch to 70 (default = 0; off)
|
|
||||||
|
|
||||||
-U set UDP port for rtl_fm remote control (default = 6020)
|
|
||||||
|
|
||||||
-Y 12 set rtl VFO bandwidth in kHz, (default = 12)(6, 8, 12, 16, 24, 48)
|
|
||||||
|
|
||||||
```
|
|
||||||
## Example STDIN UDP from GQRX or SDR++, output to Pulse Audio, and append decoded audio to wav file
|
|
||||||
Be sure to first start UDP output sink in GQRX or SDR++ and set VFO appropriately
|
|
||||||
|
|
||||||
`socat stdio udp-listen:7355 | dsd-fme -fi -i - -w nxdn.wav`
|
|
||||||
|
|
||||||
## Example Usage - Extra Information for Academic Study and Logging
|
|
||||||
|
|
||||||
`dsd-fme -Z -N 2>> voice.log`
|
|
||||||
|
|
||||||
and in a second terminal tab, same folder, run
|
|
||||||
|
|
||||||
`tail -n 40 -f voice.log`
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Roadmap
|
|
||||||
The Current list of objectives include:
|
|
||||||
|
|
||||||
1. New demodulators and input method(s) (considering liquid-dsp).
|
|
||||||
|
|
||||||
2. Graphical User Interface (may need to migrate to new project).
|
|
||||||
|
|
||||||
3. Lots of experimentation.
|
|
||||||
|
|
||||||
## How to clone, check out, and build this branch
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
If you need all dependencies build and installed first (only on Debian/Ubuntu/Mint/Pi), run:
|
|
||||||
|
|
||||||
```
|
|
||||||
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/pulseaudio/download-and-install.sh
|
|
||||||
chmod +x 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:
|
|
||||||
|
|
||||||
```
|
|
||||||
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/pulseaudio/download-and-install-nodeps.sh
|
|
||||||
chmod +x 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
|
|
||||||
|
|
||||||
First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different.
|
|
||||||
|
|
||||||
```
|
|
||||||
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
|
|
||||||
```
|
|
||||||
## Headless
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build and Install ITPP - ONLY IF NOT IN REPO!!
|
|
||||||
|
|
||||||
```
|
|
||||||
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
|
|
||||||
tar xjf itpp*
|
|
||||||
#if you can't cd into this folder, double check folder name first
|
|
||||||
cd itpp-4.3.1
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
sudo make install
|
|
||||||
sudo ldconfig
|
|
||||||
cd ..
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice") This version of MBELib is 1.3.1 and prints to STDERR, using the stock 1.3.0 MBElib may cause problems with print alignments if paired with this version of DSD-FME, or cause issues with printing over the screen in future builds.
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone https://github.com/lwvmobile/mbelib
|
|
||||||
cd mbelib
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
sudo make install
|
|
||||||
sudo ldconfig
|
|
||||||
cd ..
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
Finish by running these steps to clone and build DSD-FME w/ pulseaudio support.
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone https://github.com/lwvmobile/dsd-fme
|
|
||||||
cd dsd-fme
|
|
||||||
git branch -a
|
|
||||||
git checkout remotes/origin/pulseaudio
|
|
||||||
git checkout -b pulseaudio
|
|
||||||
git branch -a #double check to see if you are on pulseaudio branch
|
|
||||||
sudo cp tone8.wav /usr/share/
|
|
||||||
sudo cp tone24.wav /usr/share/
|
|
||||||
sudo chmod 777 /usr/share/tone8.wav
|
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
##only run make install if you don't have another version already installed##
|
|
||||||
sudo make install
|
|
||||||
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.
|
|
||||||
|
|
||||||
```
|
|
||||||
pacmd 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
|
|
||||||
```
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
```
|
|
||||||
##Open your clone folder##
|
|
||||||
git pull https://github.com/lwvmobile/dsd-fme pulseaudio
|
|
||||||
##cd into your build folder##
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
sudo make install
|
|
||||||
sudo ldconfig
|
|
||||||
```
|
|
||||||
|
|
||||||
If the call alert wav files aren't playing, then make sure to run the following in the dsd-fme folder to copy the wav files to the /usr/share/ folder and give them adequate permission to be accessed.
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo cp tone8.wav /usr/share/
|
|
||||||
sudo cp tone24.wav /usr/share/
|
|
||||||
sudo chmod 777 /usr/share/tone8.wav
|
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Copyright (C) 2010 DSD Author
|
Copyright (C) 2010 DSD Author
|
||||||
@@ -259,3 +19,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.
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
#! /bin/bash
|
|
||||||
clear
|
|
||||||
echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer
|
|
||||||
echo This will clone, build, and install DSD-FME only.
|
|
||||||
echo If you need dependencies installed, please run download-and-install.sh instead.
|
|
||||||
echo
|
|
||||||
|
|
||||||
read -p "Press enter to continue"
|
|
||||||
|
|
||||||
git clone https://github.com/lwvmobile/dsd-fme
|
|
||||||
cd dsd-fme
|
|
||||||
git branch -a
|
|
||||||
git checkout remotes/origin/pulseaudio
|
|
||||||
git checkout -b pulseaudio
|
|
||||||
git branch -a #double check to see if you are on pulseaudio branch
|
|
||||||
sudo cp tone8.wav /usr/share/
|
|
||||||
sudo cp tone24.wav /usr/share/
|
|
||||||
sudo chmod 777 /usr/share/tone8.wav
|
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
##only run make install if you don't have another version already installed##
|
|
||||||
sudo make install
|
|
||||||
sudo ldconfig
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#! /bin/bash
|
|
||||||
clear
|
|
||||||
echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer
|
|
||||||
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 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
|
|
||||||
|
|
||||||
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.
|
|
||||||
read -p "Press enter to continue"
|
|
||||||
|
|
||||||
#wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
|
|
||||||
#tar xjf itpp*
|
|
||||||
#if you can't cd into this folder, double check folder name first
|
|
||||||
#cd itpp-4.3.1
|
|
||||||
#mkdir build
|
|
||||||
#cd build
|
|
||||||
#cmake ..
|
|
||||||
#make -j `nproc`
|
|
||||||
#sudo make install
|
|
||||||
#sudo ldconfig
|
|
||||||
#cd ..
|
|
||||||
#cd ..
|
|
||||||
|
|
||||||
git clone https://github.com/lwvmobile/mbelib
|
|
||||||
cd mbelib
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
sudo make install
|
|
||||||
sudo ldconfig
|
|
||||||
cd ..
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
git clone https://github.com/lwvmobile/dsd-fme
|
|
||||||
cd dsd-fme
|
|
||||||
git branch -a
|
|
||||||
git checkout remotes/origin/pulseaudio
|
|
||||||
git checkout -b pulseaudio
|
|
||||||
git branch -a #double check to see if you are on pulseaudio branch
|
|
||||||
sudo cp tone8.wav /usr/share/
|
|
||||||
sudo cp tone24.wav /usr/share/
|
|
||||||
sudo chmod 777 /usr/share/tone8.wav
|
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
##only run make install if you don't have another version already installed##
|
|
||||||
sudo make install
|
|
||||||
sudo ldconfig
|
|
||||||
|
|
||||||
else
|
|
||||||
echo
|
|
||||||
echo Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
BIN
dsd-fme.png
BIN
dsd-fme.png
Binary file not shown.
|
Before Width: | Height: | Size: 160 KiB |
BIN
dsd-fme2.png
BIN
dsd-fme2.png
Binary file not shown.
|
Before Width: | Height: | Size: 153 KiB |
566
include/dsd.h
566
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,13 +27,23 @@
|
|||||||
#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>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
//#define __USE_XOPEN //compiler warning on this, need to rewrite dsd_file so it doesn't use strptime
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.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>
|
||||||
@@ -62,38 +76,13 @@
|
|||||||
|
|
||||||
static volatile int exitflag;
|
static volatile int exitflag;
|
||||||
|
|
||||||
|
//group csv import struct
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t RFChannelType;
|
unsigned long int groupNumber;
|
||||||
uint8_t FunctionnalChannelType;
|
char groupMode[8]; //char *?
|
||||||
uint8_t Option;
|
char groupName[50];
|
||||||
uint8_t Direction;
|
} groupinfo;
|
||||||
uint8_t CompleteLich; /* 7 bits of LICH without parity */
|
|
||||||
uint8_t PreviousCompleteLich; /* 7 bits of previous LICH without parity */
|
|
||||||
} NxdnLich_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint8_t StructureField;
|
|
||||||
uint8_t RAN;
|
|
||||||
uint8_t Data[18];
|
|
||||||
uint8_t CrcIsGood;
|
|
||||||
} NxdnSacchRawPart_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint8_t Data[80];
|
|
||||||
uint8_t CrcIsGood;
|
|
||||||
} NxdnFacch1RawPart_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint8_t Data[184];
|
|
||||||
uint8_t CrcIsGood;
|
|
||||||
} NxdnFacch2RawPart_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -135,11 +124,11 @@ typedef struct
|
|||||||
uint8_t NextIVComputed[8];
|
uint8_t NextIVComputed[8];
|
||||||
} NxdnElementsContent_t;
|
} NxdnElementsContent_t;
|
||||||
|
|
||||||
//Read Solomon (12,9) constant
|
//Reed Solomon (12,9) constant
|
||||||
#define RS_12_9_DATASIZE 9
|
#define RS_12_9_DATASIZE 9
|
||||||
#define RS_12_9_CHECKSUMSIZE 3
|
#define RS_12_9_CHECKSUMSIZE 3
|
||||||
|
|
||||||
//Read Solomon (12,9) struct
|
//Reed Solomon (12,9) struct
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t data[RS_12_9_DATASIZE+RS_12_9_CHECKSUMSIZE];
|
uint8_t data[RS_12_9_DATASIZE+RS_12_9_CHECKSUMSIZE];
|
||||||
@@ -163,48 +152,6 @@ typedef struct
|
|||||||
uint8_t bytes[3];
|
uint8_t bytes[3];
|
||||||
} rs_12_9_checksum_t;
|
} rs_12_9_checksum_t;
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
char VoiceFrame[3][72]; /* 3 x 72 bit of voice per time slot frame */
|
|
||||||
char Sync[48]; /* 48 bit of data or SYNC per time slot frame */
|
|
||||||
}TimeSlotRawVoiceFrame_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
char DeInterleavedVoiceSample[3][4][24]; /* 3 x (4 x 24) deinterleaved bit of voice per time slot frame */
|
|
||||||
}TimeSlotDeinterleavedVoiceFrame_t;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int errs1[3]; /* 3 x errors #1 computed when demodulate the AMBE voice bit of the frame */
|
|
||||||
int errs2[3]; /* 3 x errors #2 computed when demodulate the AMBE voice bit of the frame */
|
|
||||||
char AmbeBit[3][49]; /* 3 x 49 bit of AMBE voice of the frame */
|
|
||||||
}TimeSlotAmbeVoiceFrame_t;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
unsigned int ProtectFlag;
|
|
||||||
unsigned int Reserved;
|
|
||||||
unsigned int FullLinkControlOpcode;
|
|
||||||
unsigned int FeatureSetID;
|
|
||||||
unsigned int ServiceOptions;
|
|
||||||
unsigned int GroupAddress; /* Destination ID or TG (in Air Interface format) */
|
|
||||||
unsigned int SourceAddress; /* Source ID (in Air Interface format) */
|
|
||||||
unsigned int DataValidity; /* 0 = All Full LC data are incorrect ; 1 = Full LC data are correct (CRC checked OK) */
|
|
||||||
unsigned int LeftOvers; //what is the 8 bits for that isn't used
|
|
||||||
}FullLinkControlPDU_t;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
TimeSlotRawVoiceFrame_t TimeSlotRawVoiceFrame[6]; /* A voice superframe contains 6 timeslot voice frame */
|
|
||||||
TimeSlotDeinterleavedVoiceFrame_t TimeSlotDeinterleavedVoiceFrame[6];
|
|
||||||
TimeSlotAmbeVoiceFrame_t TimeSlotAmbeVoiceFrame[6];
|
|
||||||
FullLinkControlPDU_t FullLC;
|
|
||||||
} TimeSlotVoiceSuperFrame_t;
|
|
||||||
|
|
||||||
|
|
||||||
//dPMR
|
//dPMR
|
||||||
/* Could only be 2 or 4 */
|
/* Could only be 2 or 4 */
|
||||||
#define NB_OF_DPMR_VOICE_FRAME_TO_DECODE 2
|
#define NB_OF_DPMR_VOICE_FRAME_TO_DECODE 2
|
||||||
@@ -247,16 +194,17 @@ typedef struct
|
|||||||
int p25status;
|
int p25status;
|
||||||
int p25tg;
|
int p25tg;
|
||||||
int scoperate;
|
int scoperate;
|
||||||
char audio_in_dev[1024];
|
char audio_in_dev[2048]; //increase size for super long directory/file names
|
||||||
int audio_in_fd;
|
int audio_in_fd;
|
||||||
SNDFILE *audio_in_file;
|
SNDFILE *audio_in_file;
|
||||||
SF_INFO *audio_in_file_info;
|
SF_INFO *audio_in_file_info;
|
||||||
|
|
||||||
uint32_t rtlsdr_center_freq;
|
uint32_t rtlsdr_center_freq;
|
||||||
int rtlsdr_ppm_error; //was int, changed to float
|
int rtlsdr_ppm_error;
|
||||||
int audio_in_type; // 0 for device, 1 for file, 3 for rtlsdr
|
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;
|
||||||
|
|
||||||
@@ -266,8 +214,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;
|
||||||
@@ -281,6 +231,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;
|
||||||
@@ -346,6 +297,7 @@ typedef struct
|
|||||||
int inverted_dpmr;
|
int inverted_dpmr;
|
||||||
int frame_dpmr;
|
int frame_dpmr;
|
||||||
|
|
||||||
|
short int dmr_mono;
|
||||||
short int dmr_stereo;
|
short int dmr_stereo;
|
||||||
short int lrrp_file_output;
|
short int lrrp_file_output;
|
||||||
|
|
||||||
@@ -359,6 +311,74 @@ typedef struct
|
|||||||
FILE *symbolfile;
|
FILE *symbolfile;
|
||||||
int call_alert;
|
int call_alert;
|
||||||
|
|
||||||
|
//rigctl opt
|
||||||
|
int rigctl_sockfd;
|
||||||
|
int use_rigctl;
|
||||||
|
int rigctlportno;
|
||||||
|
char rigctlhostname[1024];
|
||||||
|
|
||||||
|
//udp socket for GQRX, SDR++, etc
|
||||||
|
int udp_sockfd;
|
||||||
|
int udp_portno;
|
||||||
|
char * udp_hostname;
|
||||||
|
SNDFILE *udp_file_in;
|
||||||
|
|
||||||
|
//tcp socket for SDR++, etc
|
||||||
|
int tcp_sockfd;
|
||||||
|
int tcp_portno;
|
||||||
|
char tcp_hostname[1024];
|
||||||
|
SNDFILE *tcp_file_in;
|
||||||
|
|
||||||
|
//wav file sample rate, interpolator and decimator
|
||||||
|
int wav_sample_rate;
|
||||||
|
int wav_interpolator;
|
||||||
|
int wav_decimator;
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
char group_in_file[1024];
|
||||||
|
char lcn_in_file[1024];
|
||||||
|
char chan_in_file[1024];
|
||||||
|
char key_in_file[1024];
|
||||||
|
//end import filenames
|
||||||
|
|
||||||
|
//reverse mute
|
||||||
|
uint8_t reverse_mute;
|
||||||
|
|
||||||
|
//setmod bandwidth
|
||||||
|
int setmod_bw;
|
||||||
|
|
||||||
|
//DMR Location Area - DMRLA B***S***
|
||||||
|
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
|
||||||
|
|
||||||
|
//Trunking - Use Group List as Allow List
|
||||||
|
uint8_t trunk_use_allow_list;
|
||||||
|
|
||||||
|
//Trunking - Tune Group Calls
|
||||||
|
uint8_t trunk_tune_group_calls;
|
||||||
|
|
||||||
|
//Trunking - Tune Private Calls
|
||||||
|
uint8_t trunk_tune_private_calls;
|
||||||
|
|
||||||
|
//Trunking - 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
|
||||||
|
uint8_t slot_preference;
|
||||||
|
|
||||||
|
//'DSP' Format Output
|
||||||
|
uint8_t use_dsp_output;
|
||||||
|
char dsp_out_file[2048];
|
||||||
|
|
||||||
} dsd_opts;
|
} dsd_opts;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -384,7 +404,6 @@ typedef struct
|
|||||||
int audio_out_idx2;
|
int audio_out_idx2;
|
||||||
int audio_out_idxR;
|
int audio_out_idxR;
|
||||||
int audio_out_idx2R;
|
int audio_out_idx2R;
|
||||||
//int wav_out_bytes;
|
|
||||||
int center;
|
int center;
|
||||||
int jitter;
|
int jitter;
|
||||||
int synctype;
|
int synctype;
|
||||||
@@ -431,7 +450,6 @@ typedef struct
|
|||||||
int numtdulc;
|
int numtdulc;
|
||||||
int firstframe;
|
int firstframe;
|
||||||
char slot0light[8];
|
char slot0light[8];
|
||||||
//char slot1light[8];
|
|
||||||
float aout_gain;
|
float aout_gain;
|
||||||
float aout_gainR;
|
float aout_gainR;
|
||||||
float aout_max_buf[200];
|
float aout_max_buf[200];
|
||||||
@@ -480,10 +498,10 @@ typedef struct
|
|||||||
// Last dibit read
|
// Last dibit read
|
||||||
int last_dibit;
|
int last_dibit;
|
||||||
|
|
||||||
// Heuristics state data for +P5 signals
|
// Heuristics state data for +P25 signals
|
||||||
P25Heuristics p25_heuristics;
|
P25Heuristics p25_heuristics;
|
||||||
|
|
||||||
// Heuristics state data for -P5 signals
|
// Heuristics state data for -P25 signals
|
||||||
P25Heuristics inv_p25_heuristics;
|
P25Heuristics inv_p25_heuristics;
|
||||||
|
|
||||||
//input sample buffer for monitoring Input
|
//input sample buffer for monitoring Input
|
||||||
@@ -497,17 +515,10 @@ typedef struct
|
|||||||
unsigned int nxdn_cipher_type;
|
unsigned int nxdn_cipher_type;
|
||||||
unsigned int nxdn_key;
|
unsigned int nxdn_key;
|
||||||
char nxdn_call_type[1024];
|
char nxdn_call_type[1024];
|
||||||
char dmr_callsign[2][6][99]; //plenty of room in case of overflow;
|
|
||||||
char dmr_lrrp[2][6][9999]; //memory hog
|
|
||||||
unsigned long long int dmr_lrrp_source[2];
|
unsigned long long int dmr_lrrp_source[2];
|
||||||
|
|
||||||
NxdnSacchRawPart_t NxdnSacchRawPart[4];
|
|
||||||
NxdnFacch1RawPart_t NxdnFacch1RawPart[2];
|
|
||||||
NxdnFacch2RawPart_t NxdnFacch2RawPart;
|
|
||||||
NxdnElementsContent_t NxdnElementsContent;
|
NxdnElementsContent_t NxdnElementsContent;
|
||||||
NxdnLich_t NxdnLich;
|
|
||||||
|
|
||||||
int printNXDNAmbeVoiceSampleHex;
|
|
||||||
|
|
||||||
char ambe_ciphered[49];
|
char ambe_ciphered[49];
|
||||||
char ambe_deciphered[49];
|
char ambe_deciphered[49];
|
||||||
@@ -521,50 +532,52 @@ 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;
|
||||||
TimeSlotVoiceSuperFrame_t TS1SuperFrame;
|
|
||||||
TimeSlotVoiceSuperFrame_t TS2SuperFrame;
|
|
||||||
|
|
||||||
char dmr_branding[25];
|
|
||||||
uint8_t dmr_12_rate_sf[2][288]; //was 84, expanded to 288 to prevent possible crash if more comes in than expected
|
|
||||||
uint8_t dmr_34_rate_sf[2][288]; //was 96, expanded to 288 to prevent possible crash if more comes in than expected
|
|
||||||
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
|
||||||
|
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 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
|
||||||
|
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
|
||||||
|
int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet.
|
||||||
|
|
||||||
|
//dmr talker alias new/fixed stuff
|
||||||
|
uint8_t dmr_alias_format[2]; //per slot
|
||||||
|
uint8_t dmr_alias_len[2]; //per slot
|
||||||
|
char dmr_alias_block_segment[2][4][7][16]; //2 slots, by 4 blocks, by up to 7 alias bytes that are up to 16-bit chars
|
||||||
|
char dmr_embedded_gps[2][200]; //2 slots by 99 char string for string embedded 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 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;
|
||||||
|
|
||||||
//These flags are used to known the DMR frame
|
char dpmr_caller_id[20];
|
||||||
//printing format (hex or binary)
|
char dpmr_target_id[20];
|
||||||
int printDMRRawVoiceFrameHex;
|
|
||||||
int printDMRRawVoiceFrameBin;
|
|
||||||
int printDMRRawDataFrameHex;
|
|
||||||
int printDMRRawDataFrameBin;
|
|
||||||
int printDMRAmbeVoiceSampleHex;
|
|
||||||
int printDMRAmbeVoiceSampleBin;
|
|
||||||
|
|
||||||
//These flags are used to known the dPMR frame
|
|
||||||
//orinting format (hex or binary)
|
|
||||||
int printdPMRRawVoiceFrameHex;
|
|
||||||
int printdPMRRawVoiceFrameBin;
|
|
||||||
int printdPMRRawDataFrameHex;
|
|
||||||
int printdPMRRawDataFrameBin;
|
|
||||||
int printdPMRAmbeVoiceSampleHex;
|
|
||||||
int printdPMRAmbeVoiceSampleBin;
|
|
||||||
|
|
||||||
unsigned char * dpmr_caller_id;
|
|
||||||
unsigned char * dpmr_target_id;
|
|
||||||
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
|
||||||
@@ -572,6 +585,8 @@ typedef struct
|
|||||||
short int dmr_ms_mode;
|
short int dmr_ms_mode;
|
||||||
unsigned int dmrburstL;
|
unsigned int dmrburstL;
|
||||||
unsigned int dmrburstR;
|
unsigned int dmrburstR;
|
||||||
|
int dropL;
|
||||||
|
int dropR;
|
||||||
unsigned long long int R;
|
unsigned long long int R;
|
||||||
unsigned long long int RR;
|
unsigned long long int RR;
|
||||||
unsigned long long int H;
|
unsigned long long int H;
|
||||||
@@ -587,6 +602,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)
|
||||||
@@ -595,15 +612,18 @@ 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];
|
||||||
int p25_chan_spac[16];
|
int p25_chan_spac[16];
|
||||||
long int p25_base_freq[16];
|
long int p25_base_freq[16];
|
||||||
|
|
||||||
//p25 frequency storage for display in ncurses
|
//p25 frequency storage for trunking and display in ncurses
|
||||||
long int p25_cc_freq; //cc freq from net_stat
|
long int p25_cc_freq; //cc freq from net_stat
|
||||||
long int p25_vc_freq[2]; //vc freq from voice grant updates, etc
|
long int p25_vc_freq[2]; //vc freq from voice grant updates, etc
|
||||||
|
int p25_cc_is_tdma; //flag to tell us that the P25 control channel is TDMA so we can change symbol rate when required
|
||||||
|
|
||||||
|
|
||||||
//experimental symbol file capture read throttle
|
//experimental symbol file capture read throttle
|
||||||
int symbol_throttle; //throttle speed
|
int symbol_throttle; //throttle speed
|
||||||
@@ -612,16 +632,72 @@ typedef struct
|
|||||||
//dstar header for ncurses
|
//dstar header for ncurses
|
||||||
unsigned char dstarradioheader[41];
|
unsigned char dstarradioheader[41];
|
||||||
|
|
||||||
#ifdef TRACE_DSD
|
//dmr trunking stuff
|
||||||
char debug_prefix;
|
int dmr_rest_channel;
|
||||||
char debug_prefix_2;
|
int dmr_mfid; //just when 'fid' is used as a manufacturer ID and not a feature set id
|
||||||
unsigned int debug_sample_index;
|
int dmr_vc_lcn;
|
||||||
unsigned int debug_sample_left_edge;
|
int dmr_vc_lsn;
|
||||||
unsigned int debug_sample_right_edge;
|
int dmr_tuned_lcn;
|
||||||
FILE* debug_label_file;
|
|
||||||
FILE* debug_label_dibit_file;
|
//edacs
|
||||||
FILE* debug_label_imbe_file;
|
int ea_mode;
|
||||||
#endif
|
int esk_mode;
|
||||||
|
unsigned short esk_mask;
|
||||||
|
unsigned long long int edacs_site_id;
|
||||||
|
int edacs_lcn_count; //running tally of lcn's observed on edacs system
|
||||||
|
int edacs_cc_lcn; //current lcn for the edacs control channel
|
||||||
|
int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to)
|
||||||
|
int edacs_tuned_lcn; //the vc we are currently tuned to...above variable is for updating all in the matrix
|
||||||
|
|
||||||
|
//trunking group and lcn freq list
|
||||||
|
long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully
|
||||||
|
long int trunk_chan_map[0xFFFF]; //NXDN - 10 bit; P25 - 16 bit; DMR up to 12 bit (standard TIII)
|
||||||
|
groupinfo group_array[0x3FF]; //max supported by Cygwin is 3FFF, I hope nobody actually tries to import this many groups
|
||||||
|
unsigned int group_tally; //tally number of groups imported from CSV file for referencing later
|
||||||
|
int lcn_freq_count;
|
||||||
|
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
|
||||||
|
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_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
|
||||||
|
|
||||||
|
//new nxdn stuff
|
||||||
|
int nxdn_part_of_frame;
|
||||||
|
int nxdn_ran;
|
||||||
|
int nxdn_sf;
|
||||||
|
bool nxdn_sacch_non_superframe; //flag to indicate whether or not a sacch is a part of a superframe, or an individual piece
|
||||||
|
uint8_t nxdn_sacch_frame_segment[4][18]; //part of frame by 18 bits
|
||||||
|
uint8_t nxdn_sacch_frame_segcrc[4];
|
||||||
|
uint8_t nxdn_alias_block_number;
|
||||||
|
char nxdn_alias_block_segment[4][4][8];
|
||||||
|
|
||||||
|
//site/srv/cch info
|
||||||
|
char nxdn_location_category[14];
|
||||||
|
uint32_t nxdn_location_sys_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
|
||||||
|
uint64_t late_entry_mi_fragment[2][7][3];
|
||||||
|
|
||||||
|
//dmr manufacturer branding and sub_branding (i.e., Motorola and Con+)
|
||||||
|
char dmr_branding[20];
|
||||||
|
char dmr_branding_sub[80];
|
||||||
|
|
||||||
|
//Remus DMR End Call Alert Beep
|
||||||
|
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
|
||||||
|
|
||||||
|
//Upsampled Audio Smoothing
|
||||||
|
uint8_t audio_smoothing;
|
||||||
|
|
||||||
} dsd_state;
|
} dsd_state;
|
||||||
|
|
||||||
@@ -662,8 +738,12 @@ typedef struct
|
|||||||
#define DMR_MS_DATA_SYNC "311131133313133331131113"
|
#define DMR_MS_DATA_SYNC "311131133313133331131113"
|
||||||
#define DMR_MS_VOICE_SYNC "133313311131311113313331"
|
#define DMR_MS_VOICE_SYNC "133313311131311113313331"
|
||||||
|
|
||||||
//Testing NXDN FSW again
|
//Part 1-A CAI 4.4.4 (FSW only - Late Entry - Marginal Signal)
|
||||||
#define NXDN_FSW "3131331131"
|
#define NXDN_FSW "3131331131"
|
||||||
|
#define INV_NXDN_FSW "1313113313"
|
||||||
|
//Part 1-A CAI 4.4.3 Preamble Last 9 plus FSW (start of RDCH)
|
||||||
|
#define NXDN_PANDFSW "3131133313131331131" //19 symbols
|
||||||
|
#define INV_NXDN_PANDFSW "1313311131313113313" //19 symbols
|
||||||
|
|
||||||
#define DMR_RC_DATA_SYNC "131331111133133133311313"
|
#define DMR_RC_DATA_SYNC "131331111133133133311313"
|
||||||
|
|
||||||
@@ -677,6 +757,29 @@ 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 INV_EDACS_SYNC "131313131313131313131333111311311313131313131313"
|
||||||
|
|
||||||
#define DPMR_FRAME_SYNC_1 "111333331133131131111313"
|
#define DPMR_FRAME_SYNC_1 "111333331133131131111313"
|
||||||
#define DPMR_FRAME_SYNC_2 "113333131331"
|
#define DPMR_FRAME_SYNC_2 "113333131331"
|
||||||
#define DPMR_FRAME_SYNC_3 "133131333311"
|
#define DPMR_FRAME_SYNC_3 "133131333311"
|
||||||
@@ -691,13 +794,7 @@ typedef struct
|
|||||||
/*
|
/*
|
||||||
* function prototypes
|
* function prototypes
|
||||||
*/
|
*/
|
||||||
void processDMRdata (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processDMRvoice (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrBS (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrMS (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrMSData (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processdPMRvoice (dsd_opts * opts, dsd_state * state);
|
void processdPMRvoice (dsd_opts * opts, dsd_state * state);
|
||||||
void processAudio (dsd_opts * opts, dsd_state * state);
|
void processAudio (dsd_opts * opts, dsd_state * state);
|
||||||
void processAudioR (dsd_opts * opts, dsd_state * state);
|
void processAudioR (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -718,13 +815,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);
|
||||||
@@ -748,7 +848,7 @@ void initState (dsd_state * state);
|
|||||||
void usage ();
|
void usage ();
|
||||||
void liveScanner (dsd_opts * opts, dsd_state * state);
|
void liveScanner (dsd_opts * opts, dsd_state * state);
|
||||||
void cleanupAndExit (dsd_opts * opts, dsd_state * state);
|
void cleanupAndExit (dsd_opts * opts, dsd_state * state);
|
||||||
void sigfun (int sig);
|
// void sigfun (int sig); //not necesary
|
||||||
int main (int argc, char **argv);
|
int main (int argc, char **argv);
|
||||||
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv);
|
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv);
|
||||||
void processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
|
void processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
|
||||||
@@ -757,8 +857,10 @@ void resumeScan (dsd_opts * opts, dsd_state * state);
|
|||||||
int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
|
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);
|
||||||
void processNXDNVoice (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processNXDNData (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);
|
||||||
@@ -771,48 +873,50 @@ 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 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);
|
||||||
|
|
||||||
int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr);
|
int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr);
|
||||||
/* Global functions */ //also borrowed
|
uint64_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
|
||||||
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
|
|
||||||
uint32_t ConvertAsciiToByte(uint8_t AsciiMsbByte, uint8_t AsciiLsbByte, uint8_t * OutputByte);
|
|
||||||
void Convert49BitSampleInto7Bytes(char * InputBit, char * OutputByte);
|
|
||||||
void Convert7BytesInto49BitSample(char * InputByte, char * OutputBit);
|
|
||||||
|
|
||||||
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);
|
||||||
void ncursesClose ();
|
void ncursesClose ();
|
||||||
|
|
||||||
/* NXDN frame decoding functions */
|
//new NXDN Functions start here!
|
||||||
void ProcessNXDNFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
||||||
void ProcessNxdnRCCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
void nxdn_descramble (uint8_t dibits[], int len);
|
||||||
void ProcessNxdnRTCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
//nxdn deinterleaving/depuncturing functions
|
||||||
void ProcessNxdnRDCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
|
||||||
void ProcessNxdnRTCH_C_Frame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
|
||||||
void ProcessNXDNIdleData (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
|
||||||
void ProcessNXDNFacch1Data (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
|
||||||
void ProcessNXDNUdchData (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
//type-d 'idas' deinterleaving/depuncturing functions
|
||||||
//end borrow
|
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_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
|
||||||
|
|
||||||
/* NXDN functions */
|
//OP25 NXDN CRC functions
|
||||||
|
static inline int load_i(const uint8_t val[], int len);
|
||||||
|
static uint8_t crc6(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 crc16cac(const uint8_t buf[], int len);
|
||||||
|
static uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
|
||||||
|
|
||||||
|
/* NXDN Convolution functions */
|
||||||
void CNXDNConvolution_start(void);
|
void CNXDNConvolution_start(void);
|
||||||
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
|
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
|
||||||
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
|
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
|
||||||
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
|
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
|
||||||
uint8_t NXDN_decode_LICH(uint8_t InputLich[8],
|
|
||||||
uint8_t * RFChannelType,
|
//keeping these
|
||||||
uint8_t * FunctionnalChannelType,
|
|
||||||
uint8_t * Option,
|
|
||||||
uint8_t * Direction,
|
|
||||||
uint8_t * CompleteLichBinaryFormat,
|
|
||||||
uint8_t Inverted);
|
|
||||||
uint8_t NXDN_SACCH_raw_part_decode(uint8_t * Input, uint8_t * Output);
|
|
||||||
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
|
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
|
||||||
uint8_t NXDN_FACCH1_decode(uint8_t * Input, uint8_t * Output);
|
|
||||||
uint8_t NXDN_UDCH_decode(uint8_t * Input, uint8_t * Output);
|
|
||||||
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||||
uint8_t CrcCorrect, uint8_t * ElementsContent);
|
uint8_t CrcCorrect, uint8_t * ElementsContent);
|
||||||
void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
@@ -820,13 +924,17 @@ void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|||||||
char * NXDN_Call_Type_To_Str(uint8_t CallType);
|
char * NXDN_Call_Type_To_Str(uint8_t CallType);
|
||||||
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
|
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
|
||||||
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
|
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
|
||||||
uint16_t CRC15BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
|
//added these
|
||||||
uint16_t CRC12BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
|
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
uint8_t CRC6BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
|
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
|
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
|
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
|
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
|
void NXDN_decode_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);
|
||||||
void printdPMRAmbeVoiceSample(dsd_opts * opts, dsd_state * state);
|
|
||||||
void printdPMRRawVoiceFrame (dsd_opts * opts, dsd_state * state);
|
|
||||||
|
|
||||||
//dPMR functions
|
//dPMR functions
|
||||||
void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble);
|
void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble);
|
||||||
@@ -836,45 +944,74 @@ uint8_t CRC8BitdPMR(uint8_t * BufferIn, uint32_t BitLength);
|
|||||||
void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8]);
|
void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8]);
|
||||||
int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
|
int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
|
||||||
|
|
||||||
void ProcessDMR (dsd_opts * opts, dsd_state * state);
|
|
||||||
void DMRDataFrameProcess(dsd_opts * opts, dsd_state * state);
|
|
||||||
void DMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
|
||||||
//BPTC (Block Product Turbo Code) functions
|
//BPTC (Block Product Turbo Code) functions
|
||||||
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);
|
||||||
|
|
||||||
//Read 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);
|
||||||
uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
|
uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
|
||||||
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
|
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
|
||||||
rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword);
|
rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword);
|
||||||
|
|
||||||
//SYNC DMR data extraction functions
|
//DMR CRC Functions
|
||||||
void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
|
||||||
void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
|
||||||
void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state);
|
|
||||||
uint16_t ComputeCrcCCITT(uint8_t * DMRData);
|
uint16_t ComputeCrcCCITT(uint8_t * DMRData);
|
||||||
|
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);
|
||||||
uint8_t * DmrAlgIdToStr(uint8_t AlgID);
|
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData);
|
||||||
uint8_t * DmrAlgPrivacyModeToStr(uint32_t PrivacyMode);
|
uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData);
|
||||||
|
|
||||||
void ProcessDmrPIHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
//new simplified dmr functions
|
||||||
void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
|
||||||
void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
void dmr_data_sync (dsd_opts * opts, dsd_state * state);
|
||||||
void Process1Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
||||||
void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors, uint8_t type);
|
||||||
void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98], uint8_t syncdata[48], uint8_t SlotType[20]);
|
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 ProcessMBCData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
|
||||||
void ProcessMBChData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
|
||||||
void ProcessReservedData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
|
||||||
void ProcessUnifiedData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
//Embedded Alias and GPS
|
||||||
|
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||||
|
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||||
|
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||||
|
|
||||||
|
//"DMR STEREO"
|
||||||
|
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmrBS (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmrMS (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmrMSData (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)
|
||||||
|
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_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_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
|
||||||
|
void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
|
||||||
|
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 (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
|
||||||
|
bool Hamming17123(uint8_t* d);
|
||||||
|
uint8_t crc8(uint8_t bits[], unsigned int len);
|
||||||
|
bool crc8_ok(uint8_t bits[], unsigned int len);
|
||||||
|
uint8_t crc7(uint8_t bits[], unsigned int len);
|
||||||
|
|
||||||
//LFSR code courtesy of https://github.com/mattames/LFSR/
|
//LFSR code courtesy of https://github.com/mattames/LFSR/
|
||||||
void LFSR(dsd_state * state);
|
void LFSR(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);
|
||||||
@@ -926,6 +1063,41 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
//P25 Channel to Frequency
|
//P25 Channel to Frequency
|
||||||
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel);
|
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
//rigctl functions and TCP/UDP functions
|
||||||
|
void error(char *msg);
|
||||||
|
int Connect (char *hostname, int portno);
|
||||||
|
bool Send(int sockfd, char *buf);
|
||||||
|
bool Recv(int sockfd, char *buf);
|
||||||
|
|
||||||
|
//rtl_fm udp tuning function
|
||||||
|
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency);
|
||||||
|
|
||||||
|
long int GetCurrentFreq(int sockfd);
|
||||||
|
bool SetFreq(int sockfd, long int freq);
|
||||||
|
bool SetModulation(int sockfd, int bandwidth);
|
||||||
|
//commands below unique to GQRX only, not usable on SDR++
|
||||||
|
bool GetSignalLevel(int sockfd, double *dBFS);
|
||||||
|
bool GetSquelchLevel(int sockfd, double *dBFS);
|
||||||
|
bool SetSquelchLevel(int sockfd, double dBFS);
|
||||||
|
bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp);
|
||||||
|
//end gqrx-scanner
|
||||||
|
|
||||||
|
//UDP socket connection
|
||||||
|
int UDPBind (char *hostname, int portno);
|
||||||
|
|
||||||
|
//EDACS
|
||||||
|
void edacs(dsd_opts * opts, dsd_state * state);
|
||||||
|
unsigned long long int edacs_bch (unsigned long long int message);
|
||||||
|
|
||||||
|
//csv imports
|
||||||
|
int csvGroupImport(dsd_opts * opts, dsd_state * state);
|
||||||
|
int csvLCNImport(dsd_opts * opts, dsd_state * state);
|
||||||
|
int csvChanImport(dsd_opts * opts, dsd_state * state);
|
||||||
|
int csvKeyImport(dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -936,10 +1108,9 @@ 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();
|
||||||
#endif
|
#endif
|
||||||
//TRELLIS
|
//DMR TRELLIS
|
||||||
void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* payload);
|
void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* payload);
|
||||||
unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits);
|
unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits);
|
||||||
//unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* state, unsigned char* tribits);
|
|
||||||
bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload);
|
bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload);
|
||||||
void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits);
|
void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits);
|
||||||
void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points);
|
void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points);
|
||||||
@@ -951,9 +1122,8 @@ bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload);
|
|||||||
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 (unsigned long long int 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
|
||||||
// uint16_t crc16(uint8_t buf[], int len);
|
|
||||||
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);
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* 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
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NXDN AMBE interleave schedule
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _MAIN
|
|
||||||
extern const int nW[36];
|
|
||||||
extern const int nX[36];
|
|
||||||
extern const int nY[36];
|
|
||||||
extern const int nZ[36];
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 2,
|
|
||||||
0, 2, 0, 2, 0, 2,
|
|
||||||
0, 2, 0, 2, 0, 2
|
|
||||||
};
|
|
||||||
|
|
||||||
const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
|
||||||
20, 7, 19, 6, 18, 5,
|
|
||||||
17, 4, 16, 3, 15, 2,
|
|
||||||
14, 1, 13, 0, 12, 10,
|
|
||||||
11, 9, 10, 8, 9, 7,
|
|
||||||
8, 6, 7, 5, 6, 4
|
|
||||||
};
|
|
||||||
|
|
||||||
const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
|
||||||
0, 2, 0, 3, 0, 3,
|
|
||||||
1, 3, 1, 3, 1, 3,
|
|
||||||
1, 3, 1, 3, 1, 3,
|
|
||||||
1, 3, 1, 3, 1, 3,
|
|
||||||
1, 3, 1, 3, 1, 3
|
|
||||||
};
|
|
||||||
|
|
||||||
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
|
||||||
2, 0, 1, 13, 0, 12,
|
|
||||||
22, 11, 21, 10, 20, 9,
|
|
||||||
19, 8, 18, 7, 17, 6,
|
|
||||||
16, 5, 15, 4, 14, 3,
|
|
||||||
13, 2, 12, 1, 11, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,257 +1,82 @@
|
|||||||
/*
|
// Portions Below from Osmocom OP25
|
||||||
* Copyright (C) 2010 DSD Author
|
// NXDN Encoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
// thx gr-ysf fr_vch_decoder_bb_impl.cc * Copyright 2015 Mathias Weyland *
|
||||||
*
|
// OP25 is free software; you can redistribute it and/or modify it
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
// under the terms of the GNU General Public License as published by
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
// the Free Software Foundation; either version 3, or (at your option)
|
||||||
* copyright notice and this permission notice appear in all copies.
|
// any later version.
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* 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
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _MAIN
|
|
||||||
extern const int nW[36];
|
|
||||||
extern const int nX[36];
|
|
||||||
extern const int nY[36];
|
|
||||||
extern const int nZ[36];
|
|
||||||
extern const char nxdnpr[145];
|
|
||||||
extern const char nxdnpr2[145]; //why is this 145 but referenced later as 182 or 384
|
|
||||||
//borrow from LEH
|
|
||||||
extern const unsigned char nsW[72];
|
|
||||||
extern const int DSDNXDN_SACCH_m_Interleave[60];
|
|
||||||
extern const int DSDNXDN_SACCH_m_PunctureList[12];
|
|
||||||
extern const int DSDNXDN_FACCH1_m_Interleave[144];
|
|
||||||
extern const int DSDNXDN_FACCH1_m_PunctureList[48];
|
|
||||||
extern const int DSDNXDN_UDCH_m_Interleave[348];
|
|
||||||
extern const int DSDNXDN_UDCH_m_PunctureList[58];
|
|
||||||
//
|
//
|
||||||
//enum
|
// OP25 is distributed in the hope that it will be useful, but WITHOUT
|
||||||
//{
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
#define NXDN_VCALL 0b000001 /* VCALL = VCALL_REQ = VCALL_RESP */
|
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||||
#define NXDN_VCALL_IV 0b000011
|
// License for more details.
|
||||||
#define NXDN_DCALL_HDR 0b001001 /* Header Format - NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
|
//
|
||||||
#define NXDN_DCALL_USR 0b001011 /* User Data Format */
|
// You should have received a copy of the GNU General Public License
|
||||||
#define NXDN_DCALL_ACK 0b001100
|
// along with OP25; see the file COPYING. If not, write to the Free
|
||||||
#define NXDN_REL_EX 0b000111
|
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
|
||||||
#define NXDN_HEAD_DLY 0b001111
|
// 02110-1301, USA.
|
||||||
#define NXDN_SDCALL_REQ_HDR 0b111000 /* Header Format */
|
|
||||||
#define NXDN_SDCALL_REQ_USR 0b111001 /* User Data Format */
|
|
||||||
#define NXDN_SDCALL_RESP 0b111011
|
|
||||||
#define NXDN_SDCALL_IV 0b111010
|
|
||||||
#define NXDN_STAT_INQ_REQ 0b110000
|
|
||||||
#define NXDN_STAT_INQ_RESP 0b110001
|
|
||||||
#define NXDN_STAT_REQ 0b110010
|
|
||||||
#define NXDN_STAT_RESP 0b110011
|
|
||||||
#define NXDN_REM_CON_REQ 0b110100
|
|
||||||
#define NXDN_REM_CON_RESP 0b110101
|
|
||||||
#define NXDN_REM_CON_E_REQ 0b110110
|
|
||||||
#define NXDN_REM_CON_E_RESP 0b110111
|
|
||||||
//NXDN_VCALL_REQ = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
|
|
||||||
//NXDN_VCALL_RESP = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
|
|
||||||
#define NXDN_VCALL_REC_REQ = 0b000010 /* NXDN_VCALL_REC_REQ = NXDN_VCALL_REC_RESP */
|
|
||||||
//NXDN_VCALL_REC_RESP = 0b000010,
|
|
||||||
#define NXDN_VCALL_CONN_REQ = 0b000011 /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
|
|
||||||
//NXDN_VCALL_CONN_RESP = 0b000011, /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
|
|
||||||
#define NXDN_VCALL_ASSGN_DUP = 0b000101
|
|
||||||
//NXDN_DCALL_REQ = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
|
|
||||||
//NXDN_DCALL_RESP = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
|
|
||||||
#define NXDN_DCALL_REC_REQ = 0b001010 /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
|
|
||||||
//NXDN_DCALL_REC_RESP = 0b001010, /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
|
|
||||||
#define NXDN_DCALL_ASSGN = 0b001110
|
|
||||||
#define NXDN_DCALL_ASSGN_DUP = 0b001101
|
|
||||||
#define NXDN_IDLE 0b010000
|
|
||||||
#define NXDN_DISC_REQ = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
|
|
||||||
#define NXDN_DISC = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
|
|
||||||
|
|
||||||
|
static const uint8_t PERM_12_5[] = {
|
||||||
#else
|
0,12,24,36,48,
|
||||||
/*
|
1,13,25,37,49,
|
||||||
* pseudorandom bit sequence
|
2,14,26,38,50,
|
||||||
*/
|
3,15,27,39,51,
|
||||||
const char nxdnpr[145] = { 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1 };
|
4,16,28,40,52,
|
||||||
//const unsigned char nxdnpr2[384] =
|
5,17,29,41,53,
|
||||||
const unsigned char nxdnpr2[182] = //generated from default in manual with the x^9+x^4+1 poly?
|
6,18,30,42,54,
|
||||||
{
|
7,19,31,43,55,
|
||||||
0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0,
|
8,20,32,44,56,
|
||||||
1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0,
|
9,21,33,45,57,
|
||||||
0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1,
|
10,22,34,46,58,
|
||||||
0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1,
|
11,23,35,47,59
|
||||||
1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
|
|
||||||
0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
|
|
||||||
1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1,
|
|
||||||
0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
|
|
||||||
1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1,
|
|
||||||
1, 1, 0, 0, 0, 1//, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
|
|
||||||
// 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0,
|
|
||||||
// 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
|
|
||||||
// 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
|
||||||
// 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1,
|
|
||||||
// 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0,
|
|
||||||
// 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1,
|
|
||||||
// 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1,
|
|
||||||
// 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1,
|
|
||||||
// 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0,
|
|
||||||
// 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
|
|
||||||
// 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
// 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
* NXDN AMBE interleave schedule
|
|
||||||
*/
|
|
||||||
const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 2,
|
|
||||||
0, 2, 0, 2, 0, 2,
|
|
||||||
0, 2, 0, 2, 0, 2
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
static const uint8_t PERM_16_9[] = {
|
||||||
20, 7, 19, 6, 18, 5,
|
0, 16, 32, 48, 64, 80, 96, 112, 128,
|
||||||
17, 4, 16, 3, 15, 2,
|
1, 17, 33, 49, 65, 81, 97, 113, 129,
|
||||||
14, 1, 13, 0, 12, 10,
|
2, 18, 34, 50, 66, 82, 98, 114, 130,
|
||||||
11, 9, 10, 8, 9, 7,
|
3, 19, 35, 51, 67, 83, 99, 115, 131,
|
||||||
8, 6, 7, 5, 6, 4
|
4, 20, 36, 52, 68, 84, 100, 116, 132,
|
||||||
|
5, 21, 37, 53, 69, 85, 101, 117, 133,
|
||||||
|
6, 22, 38, 54, 70, 86, 102, 118, 134,
|
||||||
|
7, 23, 39, 55, 71, 87, 103, 119, 135,
|
||||||
|
8, 24, 40, 56, 72, 88, 104, 120, 136,
|
||||||
|
9, 25, 41, 57, 73, 89, 105, 121, 137,
|
||||||
|
10, 26, 42, 58, 74, 90, 106, 122, 138,
|
||||||
|
11, 27, 43, 59, 75, 91, 107, 123, 139,
|
||||||
|
12, 28, 44, 60, 76, 92, 108, 124, 140,
|
||||||
|
13, 29, 45, 61, 77, 93, 109, 125, 141,
|
||||||
|
14, 30, 46, 62, 78, 94, 110, 126, 142,
|
||||||
|
15, 31, 47, 63, 79, 95, 111, 127, 143
|
||||||
};
|
};
|
||||||
|
|
||||||
const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
static const uint16_t PERM_12_25[] = {
|
||||||
0, 2, 0, 3, 0, 3,
|
0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228,240,252,264,276,288,
|
||||||
1, 3, 1, 3, 1, 3,
|
1,13,25,37,49,61,73,85,97,109,121,133,145,157,169,181,193,205,217,229,241,253,265,277,289,
|
||||||
1, 3, 1, 3, 1, 3,
|
2,14,26,38,50,62,74,86,98,110,122,134,146,158,170,182,194,206,218,230,242,254,266,278,290,
|
||||||
1, 3, 1, 3, 1, 3,
|
3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183,195,207,219,231,243,255,267,279,291,
|
||||||
1, 3, 1, 3, 1, 3
|
4,16,28,40,52,64,76,88,100,112,124,136,148,160,172,184,196,208,220,232,244,256,268,280,292,
|
||||||
|
5,17,29,41,53,65,77,89,101,113,125,137,149,161,173,185,197,209,221,233,245,257,269,281,293,
|
||||||
|
6,18,30,42,54,66,78,90,102,114,126,138,150,162,174,186,198,210,222,234,246,258,270,282,294,
|
||||||
|
7,19,31,43,55,67,79,91,103,115,127,139,151,163,175,187,199,211,223,235,247,259,271,283,295,
|
||||||
|
8,20,32,44,56,68,80,92,104,116,128,140,152,164,176,188,200,212,224,236,248,260,272,284,296,
|
||||||
|
9,21,33,45,57,69,81,93,105,117,129,141,153,165,177,189,201,213,225,237,249,261,273,285,297,
|
||||||
|
10,22,34,46,58,70,82,94,106,118,130,142,154,166,178,190,202,214,226,238,250,262,274,286,298,
|
||||||
|
11,23,35,47,59,71,83,95,107,119,131,143,155,167,179,191,203,215,227,239,251,263,275,287,299
|
||||||
};
|
};
|
||||||
|
|
||||||
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
static const uint16_t PERM_12_29[] = {
|
||||||
2, 0, 1, 13, 0, 12,
|
0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228,240,252,264,276,288,300,312,324,336,
|
||||||
22, 11, 21, 10, 20, 9,
|
1,13,25,37,49,61,73,85,97,109,121,133,145,157,169,181,193,205,217,229,241,253,265,277,289,301,313,325,337,
|
||||||
19, 8, 18, 7, 17, 6,
|
2,14,26,38,50,62,74,86,98,110,122,134,146,158,170,182,194,206,218,230,242,254,266,278,290,302,314,326,338,
|
||||||
16, 5, 15, 4, 14, 3,
|
3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183,195,207,219,231,243,255,267,279,291,303,315,327,339,
|
||||||
13, 2, 12, 1, 11, 0
|
4,16,28,40,52,64,76,88,100,112,124,136,148,160,172,184,196,208,220,232,244,256,268,280,292,304,316,328,340,
|
||||||
|
5,17,29,41,53,65,77,89,101,113,125,137,149,161,173,185,197,209,221,233,245,257,269,281,293,305,317,329,341,
|
||||||
|
6,18,30,42,54,66,78,90,102,114,126,138,150,162,174,186,198,210,222,234,246,258,270,282,294,306,318,330,342,
|
||||||
|
7,19,31,43,55,67,79,91,103,115,127,139,151,163,175,187,199,211,223,235,247,259,271,283,295,307,319,331,343,
|
||||||
|
8,20,32,44,56,68,80,92,104,116,128,140,152,164,176,188,200,212,224,236,248,260,272,284,296,308,320,332,344,
|
||||||
|
9,21,33,45,57,69,81,93,105,117,129,141,153,165,177,189,201,213,225,237,249,261,273,285,297,309,321,333,345,
|
||||||
|
10,22,34,46,58,70,82,94,106,118,130,142,154,166,178,190,202,214,226,238,250,262,274,286,298,310,322,334,346,
|
||||||
|
11,23,35,47,59,71,83,95,107,119,131,143,155,167,179,191,203,215,227,239,251,263,275,287,299,311,323,335,347
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* NXDN SACCH interleave
|
|
||||||
*/
|
|
||||||
const unsigned char nsW[72] = {
|
|
||||||
0, 12, 24, 36, 48, 60,
|
|
||||||
1, 13, 25, 37, 49, 61,
|
|
||||||
2, 14, 26, 38, 50, 62,
|
|
||||||
3, 15, 27, 39, 51, 63,
|
|
||||||
4, 16, 28, 40, 52, 64,
|
|
||||||
5, 17, 29, 41, 53, 65,
|
|
||||||
6, 18, 30, 42, 54, 66,
|
|
||||||
7, 19, 31, 43, 55, 67,
|
|
||||||
8, 20, 32, 44, 56, 68,
|
|
||||||
9, 21, 33, 45, 57, 69,
|
|
||||||
10,22, 34, 46, 58, 70,
|
|
||||||
11,23, 35, 47, 59, 71
|
|
||||||
};
|
|
||||||
|
|
||||||
const int DSDNXDN_SACCH_m_Interleave[60] =
|
|
||||||
{
|
|
||||||
0, 12, 24, 36, 48,
|
|
||||||
1, 13, 25, 37, 49,
|
|
||||||
2, 14, 26, 38, 50,
|
|
||||||
3, 15, 27, 39, 51,
|
|
||||||
4, 16, 28, 40, 52,
|
|
||||||
5, 17, 29, 41, 53,
|
|
||||||
6, 18, 30, 42, 54,
|
|
||||||
7, 19, 31, 43, 55,
|
|
||||||
8, 20, 32, 44, 56,
|
|
||||||
9, 21, 33, 45, 57,
|
|
||||||
10, 22, 34, 46, 58,
|
|
||||||
11, 23, 35, 47, 59,
|
|
||||||
};
|
|
||||||
|
|
||||||
const int DSDNXDN_SACCH_m_PunctureList[12] = { 5, 11, 17, 23, 29, 35, 41, 47, 53, 59, 65, 71 };
|
|
||||||
|
|
||||||
const unsigned int DSDNXDN_FACCH1_m_Interleave[144] =
|
|
||||||
{
|
|
||||||
0, 16, 32, 48, 64, 80, 96, 112, 128,
|
|
||||||
1, 17, 33, 49, 65, 81, 97, 113, 129,
|
|
||||||
2, 18, 34, 50, 66, 82, 98, 114, 130,
|
|
||||||
3, 19, 35, 51, 67, 83, 99, 115, 131,
|
|
||||||
4, 20, 36, 52, 68, 84, 100, 116, 132,
|
|
||||||
5, 21, 37, 53, 69, 85, 101, 117, 133,
|
|
||||||
6, 22, 38, 54, 70, 86, 102, 118, 134,
|
|
||||||
7, 23, 39, 55, 71, 87, 103, 119, 135,
|
|
||||||
8, 24, 40, 56, 72, 88, 104, 120, 136,
|
|
||||||
9, 25, 41, 57, 73, 89, 105, 121, 137,
|
|
||||||
10, 26, 42, 58, 74, 90, 106, 122, 138,
|
|
||||||
11, 27, 43, 59, 75, 91, 107, 123, 139,
|
|
||||||
12, 28, 44, 60, 76, 92, 108, 124, 140,
|
|
||||||
13, 29, 45, 61, 77, 93, 109, 125, 141,
|
|
||||||
14, 30, 46, 62, 78, 94, 110, 126, 142,
|
|
||||||
15, 31, 47, 63, 79, 95, 111, 127, 143,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned int DSDNXDN_FACCH1_m_PunctureList[48] =
|
|
||||||
{
|
|
||||||
1U, 5U, 9U, 13U, 17U, 21U, 25U, 29U, 33U, 37U,
|
|
||||||
41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, 73U, 77U,
|
|
||||||
81U, 85U, 89U, 93U, 97U, 101U, 105U, 109U, 113U, 117U,
|
|
||||||
121U, 125U, 129U, 133U, 137U, 141U, 145U, 149U, 153U, 157U,
|
|
||||||
161U, 165U, 169U, 173U, 177U, 181U, 185U, 189U
|
|
||||||
};
|
|
||||||
|
|
||||||
const int DSDNXDN_UDCH_m_Interleave[348] = {
|
|
||||||
0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180, 192, 204, 216, 228, 240, 252, 264, 276, 288, 300, 312, 324, 336,
|
|
||||||
1, 13, 25, 37, 49, 61, 73, 85, 97, 109, 121, 133, 145, 157, 169, 181, 193, 205, 217, 229, 241, 253, 265, 277, 289, 301, 313, 325, 337,
|
|
||||||
2, 14, 26, 38, 50, 62, 74, 86, 98, 110, 122, 134, 146, 158, 170, 182, 194, 206, 218, 230, 242, 254, 266, 278, 290, 302, 314, 326, 338,
|
|
||||||
3, 15, 27, 39, 51, 63, 75, 87, 99, 111, 123, 135, 147, 159, 171, 183, 195, 207, 219, 231, 243, 255, 267, 279, 291, 303, 315, 327, 339,
|
|
||||||
4, 16, 28, 40, 52, 64, 76, 88, 100, 112, 124, 136, 148, 160, 172, 184, 196, 208, 220, 232, 244, 256, 268, 280, 292, 304, 316, 328, 340,
|
|
||||||
5, 17, 29, 41, 53, 65, 77, 89, 101, 113, 125, 137, 149, 161, 173, 185, 197, 209, 221, 233, 245, 257, 269, 281, 293, 305, 317, 329, 341,
|
|
||||||
6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 126, 138, 150, 162, 174, 186, 198, 210, 222, 234, 246, 258, 270, 282, 294, 306, 318, 330, 342,
|
|
||||||
7, 19, 31, 43, 55, 67, 79, 91, 103, 115, 127, 139, 151, 163, 175, 187, 199, 211, 223, 235, 247, 259, 271, 283, 295, 307, 319, 331, 343,
|
|
||||||
8, 20, 32, 44, 56, 68, 80, 92, 104, 116, 128, 140, 152, 164, 176, 188, 200, 212, 224, 236, 248, 260, 272, 284, 296, 308, 320, 332, 344,
|
|
||||||
9, 21, 33, 45, 57, 69, 81, 93, 105, 117, 129, 141, 153, 165, 177, 189, 201, 213, 225, 237, 249, 261, 273, 285, 297, 309, 321, 333, 345,
|
|
||||||
10, 22, 34, 46, 58, 70, 82, 94, 106, 118, 130, 142, 154, 166, 178, 190, 202, 214, 226, 238, 250, 262, 274, 286, 298, 310, 322, 334, 346,
|
|
||||||
11, 23, 35, 47, 59, 71, 83, 95, 107, 119, 131, 143, 155, 167, 179, 191, 203, 215, 227, 239, 251, 263, 275, 287, 299, 311, 323, 335, 347,
|
|
||||||
};
|
|
||||||
|
|
||||||
const int DSDNXDN_UDCH_m_PunctureList[58] = {
|
|
||||||
3, 11,
|
|
||||||
17, 25,
|
|
||||||
31, 39,
|
|
||||||
45, 53,
|
|
||||||
59, 67,
|
|
||||||
73, 81,
|
|
||||||
87, 95,
|
|
||||||
101, 109,
|
|
||||||
115, 123,
|
|
||||||
129, 137,
|
|
||||||
143, 151,
|
|
||||||
157, 165,
|
|
||||||
171, 179,
|
|
||||||
185, 193,
|
|
||||||
199, 207,
|
|
||||||
213, 221,
|
|
||||||
227, 235,
|
|
||||||
241, 249,
|
|
||||||
255, 263,
|
|
||||||
269, 277,
|
|
||||||
283, 291,
|
|
||||||
297, 305,
|
|
||||||
311, 319,
|
|
||||||
325, 333,
|
|
||||||
339, 347,
|
|
||||||
353, 361,
|
|
||||||
367, 375,
|
|
||||||
381, 389,
|
|
||||||
395, 403,
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endif /* _MAIN */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
10
install.sh
10
install.sh
@@ -1,10 +0,0 @@
|
|||||||
sudo cp tone8.wav /usr/share/
|
|
||||||
sudo cp tone24.wav /usr/share/
|
|
||||||
sudo chmod 777 /usr/share/tone8.wav
|
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
sudo make install
|
|
||||||
sudo ldconfig
|
|
||||||
21
rebuild.sh
21
rebuild.sh
@@ -1,21 +0,0 @@
|
|||||||
#! /bin/bash
|
|
||||||
clear
|
|
||||||
echo DSD-FME Digital Speech Decoder - Florida Man Edition
|
|
||||||
echo Automatic Git Pull and Rebuild
|
|
||||||
echo
|
|
||||||
sleep 1
|
|
||||||
##Open your clone folder##
|
|
||||||
git pull https://github.com/lwvmobile/dsd-fme pulseaudio
|
|
||||||
sleep 2
|
|
||||||
sudo cp tone8.wav /usr/share/
|
|
||||||
sudo cp tone24.wav /usr/share/
|
|
||||||
sudo chmod 777 /usr/share/tone8.wav
|
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
|
||||||
##cd into your build folder##
|
|
||||||
cd build
|
|
||||||
##cmake usually isn't necesary, but could be if I update the cmakelist.txt
|
|
||||||
cmake ..
|
|
||||||
make -j `nproc`
|
|
||||||
sudo make install
|
|
||||||
sudo ldconfig
|
|
||||||
|
|
||||||
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() */
|
||||||
|
|
||||||
|
|||||||
784
src/dmr_block.c
Normal file
784
src/dmr_block.c
Normal file
@@ -0,0 +1,784 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_block.c
|
||||||
|
* DMR Data Header and Data Block Assembly/Handling
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//hopefully a more simplified (or logical) version...once you get past all the variables
|
||||||
|
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t i, j, k;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
|
||||||
|
//clear out unified pdu 'superframe' slot
|
||||||
|
for (short int i = 0; i < 288; i++) state->dmr_pdu_sf[slot][i] = 0;
|
||||||
|
|
||||||
|
//reset block counter to 1
|
||||||
|
state->data_block_counter[slot] = 1;
|
||||||
|
|
||||||
|
if (IrrecoverableErrors == 0 && CRCCorrect == 1) //&&CRCCorrect == 1
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t gi = dheader_bits[0]; //group or individual data
|
||||||
|
uint8_t a = dheader_bits[1]; //response requested flag
|
||||||
|
uint8_t ab = (uint8_t)ConvertBitIntoBytes(&dheader_bits[2], 2); //appended blocks
|
||||||
|
uint8_t dpf = (uint8_t)ConvertBitIntoBytes(&dheader_bits[4], 4); //data packet format
|
||||||
|
uint8_t sap = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 4); //service access point
|
||||||
|
uint8_t poc = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 4); //padding octets
|
||||||
|
|
||||||
|
uint32_t target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[16], 24); //destination llid
|
||||||
|
uint32_t source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[40], 24); //source llid
|
||||||
|
|
||||||
|
//extra tgt/src handling for XPT
|
||||||
|
uint8_t target_hash[24];
|
||||||
|
uint8_t tg_hash = 0;
|
||||||
|
uint8_t is_xpt = 0;
|
||||||
|
//set flag if XPT
|
||||||
|
if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1;
|
||||||
|
|
||||||
|
//truncate tgt/src to 16-bit values if XPT
|
||||||
|
if (is_xpt == 1)
|
||||||
|
{
|
||||||
|
target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16); //destination llid
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16); //source llid
|
||||||
|
if (gi == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 16; i++) target_hash[i] = dheader_bits[24+i];
|
||||||
|
tg_hash = crc8 (target_hash, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t is_cap = 0;
|
||||||
|
if (strcmp (state->dmr_branding_sub, "Cap+ ") == 0) is_cap = 1;
|
||||||
|
|
||||||
|
if (is_cap)
|
||||||
|
{
|
||||||
|
//truncate tg on group? or just on private/individual data?
|
||||||
|
if (gi == 0) target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16);
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
//store source for dmr pdu packet handling (lrrp) when not available in completed message
|
||||||
|
if (dpf != 15) state->dmr_lrrp_source[slot] = source;
|
||||||
|
|
||||||
|
//set dpf to storage for later use (UDT, SD, etc)
|
||||||
|
state->data_header_format[slot] = dpf;
|
||||||
|
|
||||||
|
//Type Strings
|
||||||
|
char sap_string[20];
|
||||||
|
char mfid_string[20];
|
||||||
|
char udtf_string[20];
|
||||||
|
char sddd_string[20];
|
||||||
|
sprintf (sap_string, "%s", "");
|
||||||
|
sprintf (mfid_string, "%s", "");
|
||||||
|
sprintf (udtf_string, "%s", "");
|
||||||
|
sprintf (sddd_string, "%s", "");
|
||||||
|
|
||||||
|
//see 9.3 - ETSI TS 102 361-1 V2.5.1 (2017-10) for more info
|
||||||
|
uint8_t f = dheader_bits[64]; //F -- Full message flag (F)
|
||||||
|
uint8_t bf = (uint8_t)ConvertBitIntoBytes(&dheader_bits[65], 7); //Blocks to Follow (BF)
|
||||||
|
|
||||||
|
//confirmed data header
|
||||||
|
uint8_t s = dheader_bits[72]; //S -- Re-Synchronize Flag
|
||||||
|
uint8_t ns = (uint8_t)ConvertBitIntoBytes(&dheader_bits[73], 3); //N(S) -- send sequence number
|
||||||
|
uint8_t fsn = (uint8_t)ConvertBitIntoBytes(&dheader_bits[76], 4); //Fragment Sequence Number (FSN)
|
||||||
|
|
||||||
|
//response header
|
||||||
|
uint8_t r_class = (uint8_t)ConvertBitIntoBytes(&dheader_bits[72], 2);
|
||||||
|
uint8_t r_type = (uint8_t)ConvertBitIntoBytes(&dheader_bits[74], 3);
|
||||||
|
uint8_t r_status = (uint8_t)ConvertBitIntoBytes(&dheader_bits[77], 3);
|
||||||
|
|
||||||
|
//short data - status/precoded
|
||||||
|
uint8_t s_ab_msb = (uint8_t)ConvertBitIntoBytes(&dheader_bits[2], 2); //appended block, msb
|
||||||
|
uint8_t s_ab_lsb = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4);//appended block, lsb
|
||||||
|
uint8_t s_ab_fin = (s_ab_msb << 2) | s_ab_lsb; //appended blocks, final value
|
||||||
|
uint8_t s_source_port = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 3);
|
||||||
|
uint8_t s_dest_port = (uint8_t)ConvertBitIntoBytes(&dheader_bits[67], 3);
|
||||||
|
uint8_t s_status_precoded = (uint8_t)ConvertBitIntoBytes(&dheader_bits[70], 10);
|
||||||
|
|
||||||
|
//short data - raw
|
||||||
|
uint8_t sd_sarq = dheader_bits[70]; //Selective Automatic Repeat reQuest
|
||||||
|
uint8_t sd_f = dheader_bits[71]; //full message flag
|
||||||
|
uint8_t sd_bp = (uint8_t)ConvertBitIntoBytes(&dheader_bits[72], 8); //bit padding
|
||||||
|
|
||||||
|
//short data - defined
|
||||||
|
uint8_t dd_format = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 6);
|
||||||
|
|
||||||
|
//Unified Data Transport (UDT)
|
||||||
|
uint8_t udt_format = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4);
|
||||||
|
uint8_t udt_padnib = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 5);
|
||||||
|
uint8_t udt_uab = (uint8_t)ConvertBitIntoBytes(&dheader_bits[70], 2); //udt appended blocks
|
||||||
|
uint8_t udt_sf = dheader_bits[72];
|
||||||
|
uint8_t udt_pf = dheader_bits[73];
|
||||||
|
uint8_t udt_op = (uint8_t)ConvertBitIntoBytes(&dheader_bits[74], 6);
|
||||||
|
|
||||||
|
//p_head
|
||||||
|
uint8_t p_sap = (uint8_t)ConvertBitIntoBytes(&dheader_bits[0], 4);
|
||||||
|
uint8_t p_mfid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8);
|
||||||
|
uint8_t p_pot = (uint8_t)ConvertBitIntoBytes(&dheader_bits[3], 5); //looking for LRRP pot_report
|
||||||
|
|
||||||
|
fprintf (stderr, "%s ", KGRN);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Slot %d Data Header - ", slot+1);
|
||||||
|
|
||||||
|
if (gi == 1 && dpf != 15) fprintf(stderr, "Group - ");
|
||||||
|
if (gi == 0 && dpf != 15) fprintf(stderr, "Indiv - ");
|
||||||
|
|
||||||
|
if (dpf == 0) fprintf (stderr, "Unified Data Transport (UDT) ");
|
||||||
|
else if (dpf == 1) fprintf (stderr, "Response Packet ");
|
||||||
|
else if (dpf == 2) fprintf (stderr, "Unconfirmed Delivery ");
|
||||||
|
else if (dpf == 3) fprintf (stderr, "Confirmed Delivery ");
|
||||||
|
else if (dpf == 13) fprintf (stderr, "Short Data: Defined ");
|
||||||
|
else if (dpf == 14) fprintf (stderr, "Short Data: Raw or S/P ");
|
||||||
|
else if (dpf == 15) fprintf (stderr, "Proprietary Packet Data");
|
||||||
|
else fprintf (stderr, "Reserved/Unknown DPF %X ", dpf);
|
||||||
|
|
||||||
|
if (a == 1 && dpf != 15) fprintf(stderr, "- Response Requested ");
|
||||||
|
if (dpf != 15) fprintf (stderr, "- Source: %d Target: %d ", source, target);
|
||||||
|
|
||||||
|
//include the hash value if this is an XPT and if its IND data
|
||||||
|
if (dpf != 15 && is_xpt == 1 && gi == 0) fprintf (stderr, "Hash: %d ", tg_hash);
|
||||||
|
|
||||||
|
//sap string handling
|
||||||
|
if (dpf == 15) sap = p_sap;
|
||||||
|
|
||||||
|
if (sap == 0) sprintf (sap_string, "%s", "UDT Data"); //apparently, both dpf and sap for UDT is 0
|
||||||
|
else if (sap == 2) sprintf (sap_string, "%s", "TCP Comp"); //TCP/IP header compression
|
||||||
|
else if (sap == 3) sprintf (sap_string, "%s", "UDP Comp"); //UDP/IP header compression
|
||||||
|
else if (sap == 4) sprintf (sap_string, "%s", "IP Based"); //IP based Packet Data
|
||||||
|
else if (sap == 5) sprintf (sap_string, "%s", "ARP Prot"); //Address Resoution Protocol (ARP)
|
||||||
|
else if (sap == 9) sprintf (sap_string, "%s", "Prop PDU"); //Proprietary Packet Data
|
||||||
|
else if (sap == 10) sprintf (sap_string, "%s", "Short DT"); //Short Data
|
||||||
|
else sprintf (sap_string, "%s", "Reserved"); //reserved, or err/unk
|
||||||
|
|
||||||
|
//mfid string handling
|
||||||
|
if (dpf == 15)
|
||||||
|
{
|
||||||
|
if (p_mfid == 0x10) sprintf (mfid_string, "%s", "Motorola");
|
||||||
|
else if (p_mfid == 0x58) sprintf (mfid_string, "%s", "Tait");
|
||||||
|
else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera");
|
||||||
|
else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera");
|
||||||
|
else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot");
|
||||||
|
else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard");
|
||||||
|
else sprintf (mfid_string, "%s", "Other");
|
||||||
|
}
|
||||||
|
|
||||||
|
//udt format string handling
|
||||||
|
if (dpf == 0)
|
||||||
|
{
|
||||||
|
if (udt_format == 0x00) sprintf (udtf_string, "%s", "Binary");
|
||||||
|
else if (udt_format == 0x01) sprintf (udtf_string, "%s", "MS/TG Adr");
|
||||||
|
else if (udt_format == 0x02) sprintf (udtf_string, "%s", "4-bit BCD");
|
||||||
|
else if (udt_format == 0x03) sprintf (udtf_string, "%s", "ISO7 Char");
|
||||||
|
else if (udt_format == 0x04) sprintf (udtf_string, "%s", "ISO8 Char");
|
||||||
|
else if (udt_format == 0x05) sprintf (udtf_string, "%s", "NMEA LOCN");
|
||||||
|
else if (udt_format == 0x06) sprintf (udtf_string, "%s", "IP Addr");
|
||||||
|
else if (udt_format == 0x07) sprintf (udtf_string, "%s", "UTF-16"); //16-bit Unicode Chars
|
||||||
|
else if (udt_format == 0x08) sprintf (udtf_string, "%s", "Manu Spec"); //Manufacturer Specific
|
||||||
|
else if (udt_format == 0x09) sprintf (udtf_string, "%s", "Manu Spec"); //Manufacturer Specific
|
||||||
|
else if (udt_format == 0x0A) sprintf (udtf_string, "%s", "Mixed UTF"); //Appended block contains addr and 16-bit UTF-16BE
|
||||||
|
else sprintf (udtf_string, "%s", "Reserved");
|
||||||
|
}
|
||||||
|
|
||||||
|
//short data dd_head format string
|
||||||
|
if (dpf == 13)
|
||||||
|
{
|
||||||
|
if (dd_format == 0x00) sprintf (sddd_string, "%s", "Binary");
|
||||||
|
else if (dd_format == 0x01) sprintf (sddd_string, "%s", "BCD ");
|
||||||
|
else if (dd_format == 0x02) sprintf (sddd_string, "%s", "7-bit Char");
|
||||||
|
else if (dd_format == 0x03) sprintf (sddd_string, "%s", "IEC 8859-1");
|
||||||
|
else if (dd_format == 0x04) sprintf (sddd_string, "%s", "IEC 8859-2");
|
||||||
|
else if (dd_format == 0x05) sprintf (sddd_string, "%s", "IEC 8859-3");
|
||||||
|
else if (dd_format == 0x06) sprintf (sddd_string, "%s", "IEC 8859-4");
|
||||||
|
else if (dd_format == 0x07) sprintf (sddd_string, "%s", "IEC 8859-5");
|
||||||
|
else if (dd_format == 0x08) sprintf (sddd_string, "%s", "IEC 8859-6");
|
||||||
|
else if (dd_format == 0x09) sprintf (sddd_string, "%s", "IEC 8859-7");
|
||||||
|
else if (dd_format == 0x0A) sprintf (sddd_string, "%s", "IEC 8859-8");
|
||||||
|
else if (dd_format == 0x0B) sprintf (sddd_string, "%s", "IEC 8859-9");
|
||||||
|
else if (dd_format == 0x0C) sprintf (sddd_string, "%s", "IEC 8859-10");
|
||||||
|
else if (dd_format == 0x0D) sprintf (sddd_string, "%s", "IEC 8859-11");
|
||||||
|
else if (dd_format == 0x0E) sprintf (sddd_string, "%s", "IEC 8859-13"); //there is no 8059-12
|
||||||
|
else if (dd_format == 0x0F) sprintf (sddd_string, "%s", "IEC 8859-14");
|
||||||
|
else if (dd_format == 0x10) sprintf (sddd_string, "%s", "IEC 8859-15");
|
||||||
|
else if (dd_format == 0x11) sprintf (sddd_string, "%s", "IEC 8859-16");
|
||||||
|
else if (dd_format == 0x12) sprintf (sddd_string, "%s", "UTF-8 ");
|
||||||
|
else if (dd_format == 0x13) sprintf (sddd_string, "%s", "UTF-16 ");
|
||||||
|
else if (dd_format == 0x14) sprintf (sddd_string, "%s", "UTF-16BE");
|
||||||
|
else if (dd_format == 0x15) sprintf (sddd_string, "%s", "UTF-16LE");
|
||||||
|
else if (dd_format == 0x16) sprintf (sddd_string, "%s", "UTF-32 ");
|
||||||
|
else if (dd_format == 0x17) sprintf (sddd_string, "%s", "UTF-32BE");
|
||||||
|
else if (dd_format == 0x18) sprintf (sddd_string, "%s", "UTF-32LE");
|
||||||
|
else sprintf (sddd_string, "%s", "Reserved");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dpf == 0) //UDT
|
||||||
|
{
|
||||||
|
//UDT packet info -- samples needed for testing
|
||||||
|
//NOTE: This format's completed message has a CRC16 - like MBC - but has number of blocks (appended blocks) like R 1/2, etc.
|
||||||
|
fprintf (stderr, "\n SAP %02X [%s] - FMT %d [%s] - PDn %d - BLOCKS %d SF %d - PF %d OP %02X", sap, sap_string, udt_format, udtf_string, udt_padnib, udt_uab, udt_sf, udt_pf, udt_op);
|
||||||
|
|
||||||
|
//set number of blocks to follow (appended blocks) for block assembler
|
||||||
|
state->data_header_blocks[slot] = udt_uab;
|
||||||
|
|
||||||
|
//set data header to valid
|
||||||
|
state->data_header_valid[slot] = 1;
|
||||||
|
|
||||||
|
//send to assembler as type 3, rearrange into CSBK type PDU (to verify), and send to dmr_cspdu
|
||||||
|
dmr_block_assembler (opts, state, dheader, 12, 0x0B, 3);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dpf == 1) //response data packet header
|
||||||
|
{
|
||||||
|
//mostly fleshed out response packet info
|
||||||
|
fprintf (stderr, "\n SAP %02X [%s] - Class %d - Type %0d - ", sap, sap_string, r_class, r_type);
|
||||||
|
if (r_class == 0 && r_type == 1) fprintf (stderr, "ACK - Success");
|
||||||
|
if (r_class == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "NACK - ");
|
||||||
|
if (r_type == 0) fprintf (stderr, "Illegal Format");
|
||||||
|
if (r_type == 1) fprintf (stderr, "Illegal Format");
|
||||||
|
if (r_type == 2) fprintf (stderr, "Packet CRC ERR");
|
||||||
|
if (r_type == 3) fprintf (stderr, "Memory Full");
|
||||||
|
if (r_type == 4) fprintf (stderr, "FSN Out of Seq");
|
||||||
|
if (r_type == 5) fprintf (stderr, "Undeliverable");
|
||||||
|
if (r_type == 6) fprintf (stderr, "PKT Out of Seq");
|
||||||
|
if (r_type == 7) fprintf (stderr, "Invalid User");
|
||||||
|
}
|
||||||
|
if (r_class == 2) fprintf (stderr, "SACK - Retry");
|
||||||
|
if (r_status) fprintf (stderr, " - %d", r_status); //the object/value of the ack/nack/sack
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Confirmed or Unconfirmed Data Packets Header
|
||||||
|
if (dpf == 2 || dpf == 3)
|
||||||
|
{
|
||||||
|
if (dpf == 2) fprintf (stderr, "\n SAP %02X [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN %d", sap, sap_string, f, bf, poc, fsn);
|
||||||
|
if (dpf == 3) fprintf (stderr, "\n SAP %02X [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN %d", sap, sap_string, f, bf, poc, s, ns, fsn);
|
||||||
|
state->data_header_blocks[slot] = bf;
|
||||||
|
if (dpf == 3) state->data_conf_data[slot] = 1; //set confirmed data delivery flag for additional CRC checks, block assembly, etc.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Short Data DD_Head (13), and R_Head or SP_Head (14)
|
||||||
|
if (dpf == 13 || dpf == 14)
|
||||||
|
{
|
||||||
|
//only set if not all zeroes
|
||||||
|
if (s_ab_fin) state->data_header_blocks[slot] = s_ab_fin;
|
||||||
|
|
||||||
|
//Short Data: Defined
|
||||||
|
if (dpf == 13) fprintf (stderr, "\n SD:D [DD_HEAD] - SAP %02d [%s] - BLOCKS %02d - DD %02X - PADb %d - FMT %02X [%s]", sap, sap_string, s_ab_fin, dd_format, sd_bp, dd_format, sddd_string);
|
||||||
|
//Short Data: Raw or S/P
|
||||||
|
if (dpf == 14)
|
||||||
|
{
|
||||||
|
//S/P has all appended block bits set to zero -- any other way to tell difference?
|
||||||
|
if (s_ab_fin == 0) fprintf (stderr, "\n SD:S/P [SP_HEAD] - SAP %02d [%s] - SP %02d - DP %02d - S/P %02X", sap, sap_string, s_source_port, s_dest_port, s_status_precoded);
|
||||||
|
|
||||||
|
//Raw
|
||||||
|
else fprintf (stderr, "\n SD:RAW [R_HEAD] - SAP %02d [%s] - BLOCKS %02d - SP %02d - DP %02d - SARQ %d - FMF %d - PDb %d", sap, sap_string, s_ab_fin, s_source_port, s_dest_port, sd_sarq, sd_f, sd_bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
//6.2.2 The Response Requested (A) information element of the header shall be set to 0
|
||||||
|
//for unconfirmed data and shall be set to 1 for confirmed data. DD_HEAD, R_HEAD, or SP_HEAD (double check)
|
||||||
|
if (a == 1)
|
||||||
|
{
|
||||||
|
state->data_conf_data[slot] = 1;
|
||||||
|
fprintf (stderr, " - Confirmed Data");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Proprietary Data Header
|
||||||
|
if (dpf == 15)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " - P_SAP %02d [%s] - MFID %02X [%s]", p_sap, sap_string, p_mfid, mfid_string);
|
||||||
|
|
||||||
|
//look for LRRP potential report 0x1F (may be others as well)
|
||||||
|
//this could just be a sap == 1, which is 'reserved', unsure (or maybe no actual correlation?)
|
||||||
|
if (p_sap != 1) state->data_header_blocks[slot]--; //decrement the blocks to follow count in order to pass CRC32 and properly assemble blocks
|
||||||
|
else //add block to start of the dmr_pdu_sf since it probably contains vital LRRP data (0x1F report) -- won't pass CRC32 either way
|
||||||
|
{
|
||||||
|
int blocks = state->data_header_blocks[slot]-1;
|
||||||
|
if (blocks < 1) blocks = 4; //safety value
|
||||||
|
if (blocks > 9) blocks = 9;
|
||||||
|
for(i = 0; i < 12; i++) state->dmr_pdu_sf[slot][i+(blocks*12)] = dheader[i];
|
||||||
|
state->data_block_counter[slot]++;
|
||||||
|
//no matter how you assemble this, with or without the p_head block(s), there is ALWAYS a CRC32 Failure
|
||||||
|
//even when it continues to pass on other SAP values, SAP 1 must have a different checksum?
|
||||||
|
//Users trying to get this will want to enable -F to bypass the CRC32 failure
|
||||||
|
|
||||||
|
//a recent discovery on a few systems and samples show the existence of multiple Prop Head Packets (how does that work)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//block storage sanity
|
||||||
|
if (state->data_header_blocks[slot] > 9) state->data_header_blocks[slot] = 9;
|
||||||
|
//assuming we didn't receive the initial data header block on a p_head and then decremented it
|
||||||
|
//3 or 4 seems to be the average safe value
|
||||||
|
if (state->data_header_blocks[slot] < 1) state->data_header_blocks[slot] = 4;
|
||||||
|
//set data header validity unless its a p_head (should be set prior, if received)
|
||||||
|
if (dpf != 15) state->data_header_valid[slot] = 1;
|
||||||
|
|
||||||
|
//REMUS! Uncomment these lines if desired
|
||||||
|
if (dpf != 15 && dpf != 1)
|
||||||
|
{
|
||||||
|
sprintf (state->dmr_lrrp_gps[slot], "Data Call - %s SRC [%d] DST [%d] ", sap_string, source, target);
|
||||||
|
if (a == 1) strcat (state->dmr_lrrp_gps[slot], "- RSP REQ ");
|
||||||
|
}
|
||||||
|
|
||||||
|
//REMUS! Uncomment these lines if desired -- if the data request data header is missing, though, this may keep accumulating
|
||||||
|
// if (dpf == 1)
|
||||||
|
// {
|
||||||
|
// strcat (state->dmr_lrrp_gps[slot], "- RSP ");
|
||||||
|
// if (r_class == 0) strcat (state->dmr_lrrp_gps[slot], "ACK - RX OK");
|
||||||
|
// if (r_class == 1) strcat (state->dmr_lrrp_gps[slot], "NACK - RX ERR");
|
||||||
|
// if (r_class == 2) strcat (state->dmr_lrrp_gps[slot], "SACK - RETRY");
|
||||||
|
|
||||||
|
// }
|
||||||
|
//End REMUS Data string
|
||||||
|
|
||||||
|
//store SAP value
|
||||||
|
state->data_header_sap[slot] = sap;
|
||||||
|
|
||||||
|
} //End Irrecoverable Errors
|
||||||
|
|
||||||
|
if (IrrecoverableErrors != 0)
|
||||||
|
{
|
||||||
|
state->data_header_valid[slot] = 0;
|
||||||
|
sprintf (state->dmr_lrrp_gps[slot], "%s", "");
|
||||||
|
state->data_p_head[slot] = 0;
|
||||||
|
state->data_conf_data[slot] = 0;
|
||||||
|
state->data_block_counter[slot] = 1;
|
||||||
|
state->data_header_blocks[slot] = 1;
|
||||||
|
state->data_header_format[slot] = 7;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert UDT to a CSBK friendly PDU format
|
||||||
|
void dmr_udt_cspdu_converter (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
//rearrange UDT data to a CSBK message format
|
||||||
|
uint8_t cs_byte[48]; //12 x 4, 2 bit AB should mean only 3 total appended blocks, so 4 all together (minus rearrangement)
|
||||||
|
memset (cs_byte, 0, sizeof (cs_byte));
|
||||||
|
uint8_t cs_bits[48*8];
|
||||||
|
memset (cs_bits, 0, sizeof (cs_bits));
|
||||||
|
|
||||||
|
cs_byte[0] = (uint8_t)ConvertBitIntoBytes(&block_bits[73], 7); //lb, pf and opcode byte
|
||||||
|
uint8_t udt_opcode = (uint8_t)ConvertBitIntoBytes(&block_bits[74], 6); //for observation testing
|
||||||
|
cs_byte[1] = 0; //set mfid to zero for now (may use custom value if needed later)
|
||||||
|
uint8_t udt_mfid = 0; //setting to zero
|
||||||
|
|
||||||
|
//should be 34 total (maximum) after removing tailing CRC16 (initial and last block)
|
||||||
|
for (i = 0; i < 34; i++) cs_byte[2+i] = block_bytes[12+i];
|
||||||
|
|
||||||
|
//bytes to bits
|
||||||
|
for(i = 0, j = 0; i < 36; i++, j+=8) //4 blocks max?
|
||||||
|
{
|
||||||
|
cs_bits[j + 0] = (cs_byte[i] >> 7) & 0x01;
|
||||||
|
cs_bits[j + 1] = (cs_byte[i] >> 6) & 0x01;
|
||||||
|
cs_bits[j + 2] = (cs_byte[i] >> 5) & 0x01;
|
||||||
|
cs_bits[j + 3] = (cs_byte[i] >> 4) & 0x01;
|
||||||
|
cs_bits[j + 4] = (cs_byte[i] >> 3) & 0x01;
|
||||||
|
cs_bits[j + 5] = (cs_byte[i] >> 2) & 0x01;
|
||||||
|
cs_bits[j + 6] = (cs_byte[i] >> 1) & 0x01;
|
||||||
|
cs_bits[j + 7] = (cs_byte[i] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
dmr_cspdu (opts, state, cs_bits, cs_byte, CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//assemble the blocks as they come in, shuffle them into the unified dmr_pdu_sf
|
||||||
|
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
uint8_t lb = 0; //mbc last block
|
||||||
|
uint8_t pf = 0; //mbc protect flag
|
||||||
|
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
int blocks;
|
||||||
|
uint8_t blockcounter = state->data_block_counter[slot];
|
||||||
|
uint8_t block_num = state->data_header_blocks[slot];
|
||||||
|
|
||||||
|
uint32_t CRCCorrect = 0;
|
||||||
|
uint32_t CRCComputed = 0;
|
||||||
|
uint32_t CRCExtracted = 0;
|
||||||
|
uint32_t IrrecoverableErrors = 0;
|
||||||
|
|
||||||
|
uint8_t dmr_pdu_sf_bits[8*24*50]; //give plenty of space so we don't go oob
|
||||||
|
|
||||||
|
//MBC Header and Block CRC
|
||||||
|
uint8_t mbc_crc_good[2]; //header and blocks crc pass/fail local storage
|
||||||
|
memset (mbc_crc_good, 0, sizeof (mbc_crc_good)); //init on 0 - bad crc
|
||||||
|
|
||||||
|
if (type == 1) blocks = state->data_header_blocks[slot] - 1;
|
||||||
|
if (type == 2) blocks = state->data_block_counter[slot];
|
||||||
|
|
||||||
|
//UDT Header and Block Check
|
||||||
|
uint8_t is_udt = 0;
|
||||||
|
if (type == 3)
|
||||||
|
{
|
||||||
|
blocks = state->data_block_counter[slot];
|
||||||
|
is_udt = 1;
|
||||||
|
type = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sanity check, setting block_len and block values to sane numbers in case of missing header, else could overload array (crash) or print out
|
||||||
|
if (blocks < 1) blocks = 1; //changed blocks from uint8_t to int
|
||||||
|
if (blocks > 9) blocks = 9;
|
||||||
|
if (block_len == 0) block_len = 18;
|
||||||
|
if (block_len > 24) block_len = 24;
|
||||||
|
|
||||||
|
if (type == 1)
|
||||||
|
{
|
||||||
|
//type 1 data block, shuffle method
|
||||||
|
for (j = 0; j < blocks; j++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < block_len; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i+block_len*j] = state->dmr_pdu_sf[slot][i+block_len*(j+1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < block_len; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i+(blocks*block_len)] = block_bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//time to send the completed 'superframe' to the DMR PDU message handler
|
||||||
|
if (state->data_block_counter[slot] == state->data_header_blocks[slot] && state->data_header_valid[slot] == 1)
|
||||||
|
{
|
||||||
|
//CRC32 on completed messages
|
||||||
|
for(i = 0, j = 0; i < (block_num * block_len); i++, j+=8)
|
||||||
|
{
|
||||||
|
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//sanity check to prevent a negative or zero block_num or sending a negative value into the bit array
|
||||||
|
if (block_num < 1 || block_num == 0) block_num = 1;
|
||||||
|
|
||||||
|
CRCExtracted = 0;
|
||||||
|
//extract crc from last block, apply to completed 'superframe' minus header
|
||||||
|
for(i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
CRCExtracted = CRCExtracted << 1;
|
||||||
|
CRCExtracted = CRCExtracted | (uint32_t)(dmr_pdu_sf_bits[i + (block_len * 8) * block_num - 32] & 1); //96
|
||||||
|
}
|
||||||
|
|
||||||
|
//rearrage for ridiculously stupid CRC32 LSO/MSO ordering
|
||||||
|
for(i = 0, j = 0; i < (block_num * block_len); i+=2, j+=16)
|
||||||
|
{
|
||||||
|
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i+1] >> 7) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i+1] >> 6) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i+1] >> 5) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i+1] >> 4) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i+1] >> 3) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i+1] >> 2) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i+1] >> 1) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i+1] >> 0) & 0x01;
|
||||||
|
|
||||||
|
dmr_pdu_sf_bits[j + 8] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 9] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 10] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 11] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 12] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 13] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 14] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 15] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||||
|
|
||||||
|
//increment extra 2 bytes on each nth byte (depends on data type 1/2, 3/4, 1) if conf data
|
||||||
|
if ( i == (block_len - 1) )
|
||||||
|
{
|
||||||
|
if (state->data_conf_data[slot] == 0) i+=2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//confirmed working now!
|
||||||
|
CRCComputed = (uint32_t)ComputeCrc32Bit(dmr_pdu_sf_bits, ( block_num * block_len * 8) - 32);
|
||||||
|
|
||||||
|
//if the CRC32 is correct, I think its fair to assume we don't need to worry about if the
|
||||||
|
//individual CRC9s are correct on confirmed data blocks (but we can confirm now that they are all good)
|
||||||
|
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||||
|
|
||||||
|
//only run on good CRC or CRC/RAS bypass (aggressive_framesync == 0)
|
||||||
|
if (CRCCorrect || opts->aggressive_framesync == 0)
|
||||||
|
dmr_pdu (opts, state, block_len, state->dmr_pdu_sf[slot]);
|
||||||
|
|
||||||
|
if (CRCCorrect) ; //print nothing
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "\n Slot %d - Multi Block PDU Message CRC32 ERR", slot+1);
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
// fprintf (stderr, " %X - %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Full Super Frame Type 1 - Payload Output
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, "\n Slot %d - Multi Block PDU Message\n ", slot+1);
|
||||||
|
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||||
|
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
|
if (state->data_header_format[slot] == 13 || state->data_header_format[slot] == 14 || state->data_header_sap[slot] == 10)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, "\n Short Data: ASCII \n" );
|
||||||
|
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||||
|
{
|
||||||
|
if (state->dmr_pdu_sf[slot][i] <= 0x7E && state->dmr_pdu_sf[slot][i] >=0x20)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%c", state->dmr_pdu_sf[slot][i]);
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " ");
|
||||||
|
|
||||||
|
if (i == 47 || i == 95) fprintf (stderr, "\n ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
} //end completed sf
|
||||||
|
|
||||||
|
} //end type 1 blocks
|
||||||
|
|
||||||
|
|
||||||
|
//type 2 - MBC and UDT header, MBC and UDT continuation blocks
|
||||||
|
if (type == 2)
|
||||||
|
{
|
||||||
|
//sanity check (marginal signal, bad decodes, etc) -- may go a little lower (find out max number of MBC blocks supported)
|
||||||
|
if (state->data_block_counter[slot] > 4) state->data_block_counter[slot] = 4;
|
||||||
|
|
||||||
|
//Type 2 data block, additive method
|
||||||
|
for (i = 0; i < block_len; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i+(blockcounter*block_len)] = block_bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (dmr_pdu_sf_bits, 0, sizeof(dmr_pdu_sf_bits));
|
||||||
|
|
||||||
|
lb = block_bytes[0] >> 7; //last block flag
|
||||||
|
pf = (block_bytes[0] >> 6) & 1;
|
||||||
|
|
||||||
|
if (is_udt)
|
||||||
|
{
|
||||||
|
blocks = state->data_header_blocks[slot];
|
||||||
|
if (blocks == blockcounter) lb = 1; //double check
|
||||||
|
pf = 0; //flag off is set from bit in error
|
||||||
|
}
|
||||||
|
|
||||||
|
//last block arrived and we have a valid data header, time to send to cspdu decoder
|
||||||
|
if (lb == 1 && state->data_header_valid[slot] == 1)
|
||||||
|
{
|
||||||
|
for(i = 0, j = 0; i < 12*4; i++, j+=8) //4 blocks max?
|
||||||
|
{
|
||||||
|
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check UDT PF
|
||||||
|
if (is_udt) pf = dmr_pdu_sf_bits[73];
|
||||||
|
|
||||||
|
//CRC check on Header and full frames as appropriate (header crc already stored)
|
||||||
|
//The 16 bit CRC in the header shall include the data carried by the header.
|
||||||
|
//The 16 bit CRC in the last block shall be performed on
|
||||||
|
//all MBC blocks (conmbined) except the header block.
|
||||||
|
|
||||||
|
mbc_crc_good[0] = state->data_block_crc_valid[slot][0];
|
||||||
|
|
||||||
|
CRCExtracted = 0;
|
||||||
|
//extract crc from last block, apply to completed 'superframe' minus header
|
||||||
|
for(i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
CRCExtracted = CRCExtracted << 1;
|
||||||
|
CRCExtracted = CRCExtracted | (uint32_t)(dmr_pdu_sf_bits[i + 96*(1+blocks) - 16] & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mbc_block_bits[12*8*3];
|
||||||
|
memset (mbc_block_bits, 0, sizeof(mbc_block_bits));
|
||||||
|
//shift continuation blocks and last block into seperate array for crc check
|
||||||
|
for (i = 0; i < 12*8*3; i++) //only doing 3 blocks (4 minus the header)
|
||||||
|
{
|
||||||
|
mbc_block_bits[i] = dmr_pdu_sf_bits[i+96]; //skip mbc header
|
||||||
|
}
|
||||||
|
|
||||||
|
CRCComputed = ComputeCrcCCITT16d (mbc_block_bits, ((blocks+0)*96)-16 );
|
||||||
|
|
||||||
|
if (CRCComputed == CRCExtracted) mbc_crc_good[1] = 1;
|
||||||
|
|
||||||
|
CRCCorrect = 0;
|
||||||
|
IrrecoverableErrors = 1;
|
||||||
|
|
||||||
|
//set good on good header and good blocks
|
||||||
|
if (mbc_crc_good[0] == 1 && mbc_crc_good[1] == 1)
|
||||||
|
{
|
||||||
|
CRCCorrect = 1;
|
||||||
|
IrrecoverableErrors = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "\n Slot %d - Multi Block Control Message CRC16 ERR", slot+1);
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
fprintf (stderr, " %X - %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
//cspdu will only act on any fid/opcodes if good CRC to prevent falsing on control signalling
|
||||||
|
if (!is_udt && !pf) dmr_cspdu (opts, state, dmr_pdu_sf_bits, state->dmr_pdu_sf[slot], CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
//will need to reassemble partially before sending to dmr_pdu or dmr_cspdu for handling -- needs testing
|
||||||
|
if (is_udt && !pf) dmr_udt_cspdu_converter (opts, state, state->dmr_pdu_sf[slot], dmr_pdu_sf_bits, CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
//Full Super Frame MBC/UDT - Debug Output
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, "\n Slot %d - Multi Block Control Message\n ", slot+1);
|
||||||
|
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||||
|
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
if (mbc_crc_good[0] == 0) fprintf (stderr, "MBC/UDT Header CRC ERR ");
|
||||||
|
if (mbc_crc_good[1] == 0) fprintf (stderr, "MBC/UDT Blocks CRC ERR ");
|
||||||
|
if (pf) fprintf (stderr, "MBC/UDT Header/Blocks Protected ");
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //end last block flag on MBC
|
||||||
|
|
||||||
|
} //end type 2 (MBC Header and Continuation)
|
||||||
|
|
||||||
|
//leave this seperate so we can reset/zero stuff in case the data header isn't valid, etc
|
||||||
|
//switched to an if-elseif-else so we could get the block counter increment on the end
|
||||||
|
//without having it in an akward position
|
||||||
|
|
||||||
|
//if the end of normal data header and blocks
|
||||||
|
if (type == 1 && state->data_block_counter[slot] == state->data_header_blocks[slot])
|
||||||
|
{
|
||||||
|
|
||||||
|
//clear out unified pdu 'superframe' slot
|
||||||
|
for (short int i = 0; i < 288; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Zero Out MBC Header Block CRC Valid
|
||||||
|
state->data_block_crc_valid[slot][0] = 0;
|
||||||
|
//reset the block counter (data blocks)
|
||||||
|
state->data_block_counter[slot] = 1;
|
||||||
|
//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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//else if the end of MBC Header and Blocks
|
||||||
|
else if (type == 2 && lb == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
//clear out unified pdu 'superframe' slot
|
||||||
|
for (short int i = 0; i < 288; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Zero Out MBC Header Block CRC Valid
|
||||||
|
state->data_block_crc_valid[slot][0] = 0;
|
||||||
|
//reset the block counter (data blocks)
|
||||||
|
state->data_block_counter[slot] = 1;
|
||||||
|
//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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//else increment block counter after sorting/shuffling blocks
|
||||||
|
else state->data_block_counter[slot]++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//failsafe to clear old data header, block info, cach, in case of tact/emb/slottype failures
|
||||||
|
//or tuning away and we can no longer verify accurate data block reporting
|
||||||
|
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
memset (state->data_p_head, 0, sizeof(state->data_p_head));
|
||||||
|
memset (state->data_conf_data, 0, sizeof(state->data_conf_data));
|
||||||
|
memset (state->dmr_pdu_sf, 0, sizeof(state->dmr_pdu_sf));
|
||||||
|
memset (state->data_block_counter, 1, sizeof(state->data_block_counter));
|
||||||
|
memset (state->data_header_blocks, 1, sizeof(state->data_header_blocks));
|
||||||
|
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
|
||||||
|
memset (state->dmr_lrrp_source, 0, sizeof(state->dmr_lrrp_source));
|
||||||
|
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
||||||
|
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||||
|
memset (state->cap_plus_block_num, 0, sizeof(state->cap_plus_block_num));
|
||||||
|
memset (state->data_header_valid, 0, sizeof(state->data_header_valid));
|
||||||
|
memset (state->data_header_format, 7, sizeof(state->data_header_format));
|
||||||
|
//reset some strings
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->dmr_lrrp_gps[0], "%s", "");
|
||||||
|
sprintf (state->dmr_lrrp_gps[1], "%s", "");
|
||||||
|
}
|
||||||
803
src/dmr_bs.c
803
src/dmr_bs.c
File diff suppressed because it is too large
Load Diff
1474
src/dmr_csbk.c
Normal file
1474
src/dmr_csbk.c
Normal file
File diff suppressed because it is too large
Load Diff
447
src/dmr_data.c
447
src/dmr_data.c
@@ -18,35 +18,32 @@
|
|||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
processDMRdata (dsd_opts * opts, dsd_state * state)
|
dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i, dibit;
|
int i, dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
char sync[25];
|
char sync[25];
|
||||||
char syncdata[48];
|
char syncdata[48];
|
||||||
char cachdata[25] = {0};
|
char cachdata[25];
|
||||||
char cc[5] = {0};
|
|
||||||
char ccAscii[5] = {0};
|
|
||||||
char bursttype[5];
|
|
||||||
unsigned int burst;
|
|
||||||
char info[196];
|
|
||||||
|
|
||||||
|
char bursttype[5];
|
||||||
|
uint8_t burst;
|
||||||
|
char info[196];
|
||||||
unsigned char SlotType[20];
|
unsigned char SlotType[20];
|
||||||
unsigned int SlotTypeOk;
|
unsigned int SlotTypeOk;
|
||||||
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
|
uint8_t cach_err = 0;
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
int cachInterleave[24] =
|
||||||
char syncbits[49];
|
{0, 7, 8, 9, 1, 10,
|
||||||
char cachbits[25];
|
11, 12, 2, 13, 14,
|
||||||
int k;
|
15, 3, 16, 4, 17, 18,
|
||||||
#endif
|
19, 5, 20, 21, 22, 6, 23
|
||||||
|
};
|
||||||
ccAscii[4] = 0;
|
|
||||||
bursttype[4] = 0;
|
|
||||||
|
|
||||||
dibit_p = state->dmr_payload_p - 90;
|
dibit_p = state->dmr_payload_p - 90;
|
||||||
// CACH
|
|
||||||
|
//collect cach and de-interleave
|
||||||
for (i = 0; i < 12; i++)
|
for (i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
@@ -59,73 +56,38 @@ processDMRdata (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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//decode and correct cach and compare
|
//seperate tact bits from cach
|
||||||
int cach_okay = 69;
|
uint8_t tact_bits[7];
|
||||||
if ( Hamming_7_4_decode (cachdata) ) //is now de-interleaved appropriately
|
for (i = 0; i < 7; i++)
|
||||||
{
|
{
|
||||||
cach_okay = 1;
|
tact_bits[i] = cachdata[i];
|
||||||
}
|
|
||||||
if (cach_okay == 1)
|
|
||||||
{
|
|
||||||
//fprintf (stderr, "CACH Okay ");
|
|
||||||
}
|
|
||||||
if (cach_okay != 1)
|
|
||||||
{
|
|
||||||
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
|
|
||||||
if (opts->aggressive_framesync == 1) //may not worry about it on data
|
|
||||||
{
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->currentslot = cachdata[1];
|
//run hamming on tact bits
|
||||||
|
int cach_okay = -1;
|
||||||
|
if ( Hamming_7_4_decode (tact_bits) ) cach_okay = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cach_okay = -1;
|
||||||
|
SlotTypeOk = 0;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
//minor bugfix for MSData when MSData has accumulated data blocks
|
state->currentslot = tact_bits[1];
|
||||||
//(i.e. data header and 1/2 rate data), force all data into slot 0
|
|
||||||
|
//in the future, maybe we will remove the hard set on this
|
||||||
if (state->dmr_ms_mode == 1)
|
if (state->dmr_ms_mode == 1)
|
||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->currentslot == 0 && state->dmr_ms_mode == 0)
|
|
||||||
{
|
|
||||||
state->slot1light[0] = '[';
|
|
||||||
state->slot1light[6] = ']';
|
|
||||||
state->slot2light[0] = ' ';
|
|
||||||
state->slot2light[6] = ' ';
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
if (state->currentslot == 1 && state->dmr_ms_mode == 0)
|
|
||||||
{
|
|
||||||
state->slot2light[0] = '[';
|
|
||||||
state->slot2light[6] = ']';
|
|
||||||
state->slot1light[0] = ' ';
|
|
||||||
state->slot1light[6] = ' ';
|
|
||||||
}
|
|
||||||
//end correct cach and set slot
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = cachdata[i];
|
|
||||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
cachbits[24] = 0;
|
|
||||||
fprintf(stderr, "%s ", cachbits);
|
|
||||||
#endif
|
|
||||||
//trellis bits
|
|
||||||
unsigned char trellisdibits[98];
|
|
||||||
// Current slot - First half - Data Payload - 1st part
|
// Current slot - First half - Data Payload - 1st part
|
||||||
for (i = 0; i < 49; i++)
|
for (i = 0; i < 49; i++)
|
||||||
{
|
{
|
||||||
@@ -139,7 +101,7 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+12];
|
dibit = (int)state->dmr_stereo_payload[i+12];
|
||||||
}
|
}
|
||||||
trellisdibits[i] = dibit;
|
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
|
||||||
}
|
}
|
||||||
@@ -153,10 +115,10 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[61]; //61, not i+61
|
dibit = (int)state->dmr_stereo_payload[61];
|
||||||
}
|
}
|
||||||
cc[0] = (1 & (dibit >> 1)); // bit 1
|
else state->dmr_stereo_payload[61] = dibit;
|
||||||
cc[1] = (1 & dibit); // bit 0
|
|
||||||
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
|
||||||
|
|
||||||
@@ -170,17 +132,11 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[62];
|
dibit = (int)state->dmr_stereo_payload[62];
|
||||||
}
|
}
|
||||||
cc[2] = (1 & (dibit >> 1)); // bit 1
|
else state->dmr_stereo_payload[62] = dibit;
|
||||||
cc[3] = (1 & dibit); // bit 0
|
|
||||||
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
|
||||||
|
|
||||||
ccAscii[0] = cc[0] + '0';
|
|
||||||
ccAscii[1] = cc[1] + '0';
|
|
||||||
ccAscii[2] = cc[2] + '0';
|
|
||||||
ccAscii[3] = cc[3] + '0';
|
|
||||||
ccAscii[4] = cc[4] + '\0';
|
|
||||||
|
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
dibit_p++;
|
dibit_p++;
|
||||||
if (opts->inverted_dmr == 1)
|
if (opts->inverted_dmr == 1)
|
||||||
@@ -189,8 +145,9 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1) //state
|
if (state->dmr_stereo == 1) //state
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[63]; //(int)
|
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
|
||||||
@@ -203,8 +160,9 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1) //state
|
if (state->dmr_stereo == 1) //state
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[64]; //(int)
|
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
|
||||||
@@ -220,10 +178,11 @@ processDMRdata (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
|
||||||
|
|
||||||
// signalling data or sync
|
// signaling data or sync
|
||||||
for (i = 0; i < 24; i++)
|
for (i = 0; i < 24; i++)
|
||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
@@ -236,35 +195,24 @@ processDMRdata (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;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
sync[24] = 0;
|
sync[24] = 0;
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
syncbits[k] = syncdata[(k*2)] + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
syncbits[k] = syncdata[(k*2)+1] + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
syncbits[48] = 0;
|
|
||||||
fprintf(stderr, "%s ", syncbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )//|| (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
|
||||||
{
|
{
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
sprintf(state->slot1light, "[slot1]");
|
sprintf(state->slot1light, "[slot1]");
|
||||||
|
sprintf(state->slot2light, " slot2 ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
sprintf(state->slot1light, " slot1 ");
|
||||||
sprintf(state->slot2light, "[slot2]");
|
sprintf(state->slot2light, "[slot2]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,15 +221,17 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
sprintf(state->slot1light, "[sLoT1]");
|
sprintf(state->slot1light, "[sLoT1]");
|
||||||
|
sprintf(state->slot2light, "[DMODE]");
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
||||||
{
|
{
|
||||||
state->currentslot = 1;
|
state->currentslot = 1;
|
||||||
|
sprintf(state->slot1light, "[DMODE]");
|
||||||
sprintf(state->slot2light, "[sLoT2]");
|
sprintf(state->slot2light, "[sLoT2]");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->errorbars == 1 && state->dmr_ms_mode == 0)
|
if (state->dmr_ms_mode == 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
||||||
}
|
}
|
||||||
@@ -289,17 +239,14 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
|
|||||||
// Slot type - Second part - Parity bit
|
// Slot type - Second part - Parity bit
|
||||||
for (i = 0; i < 5; i++)
|
for (i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method
|
if (state->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
}
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
//dibit = (dibit ^ 2);
|
|
||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+90]; //double checked these i+ values make sure we are on the right ones
|
dibit = (int)state->dmr_stereo_payload[i+90];
|
||||||
}
|
}
|
||||||
SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1
|
SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[(i*2) + 11] = (1 & dibit); // bit 0
|
SlotType[(i*2) + 11] = (1 & dibit); // bit 0
|
||||||
@@ -308,264 +255,98 @@ processDMRdata (dsd_opts * opts, dsd_state * state)
|
|||||||
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
|
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
|
||||||
|
|
||||||
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
|
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
|
||||||
if( Golay_20_8_decode(SlotType) )
|
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
|
||||||
{
|
|
||||||
SlotTypeOk = 1;
|
|
||||||
//fprintf (stderr, "Slot Okay ");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
SlotTypeOk = -1;
|
||||||
if (opts->aggressive_framesync == 1)
|
SlotTypeOk = 0;
|
||||||
{
|
goto END;
|
||||||
SlotTypeOk = 0;
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Slot Type parity checked => Fill the color code */
|
|
||||||
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);
|
||||||
state->color_code_ok = SlotTypeOk;
|
state->color_code_ok = SlotTypeOk;
|
||||||
|
|
||||||
/* Reconstitute the burst type */
|
//not sure why I still have two variables for this, need to look and see what state->color_code still ties into
|
||||||
//consider assigning this only when slottypeok or similar check passes, eliminate bad burst types from decoding randomly
|
if (SlotTypeOk == 1) state->dmr_color_code = state->color_code;
|
||||||
burst = (unsigned int)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->dmrburstL = burst;
|
|
||||||
}
|
|
||||||
if (state->currentslot == 1)
|
|
||||||
{
|
|
||||||
state->dmrburstR = burst;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reconstitute the burst type */
|
/* Reconstitute the burst type */
|
||||||
bursttype[0] = SlotType[4] + '0';
|
burst = (uint8_t)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
|
||||||
bursttype[1] = SlotType[5] + '0';
|
if (state->currentslot == 0) state->dmrburstL = burst;
|
||||||
bursttype[2] = SlotType[6] + '0';
|
if (state->currentslot == 1) state->dmrburstR = burst;
|
||||||
bursttype[3] = SlotType[7] + '0';
|
|
||||||
bursttype[4] = '\0';
|
|
||||||
|
|
||||||
if (strcmp (bursttype, "0000") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " PI Header ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0001") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " VOICE LC ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0010") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " TLC ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0011") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " CSBK ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0100") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " MBC Header ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0101") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " MBC ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0110") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " DATA Header ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0111") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " RATE 1/2 DATA");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "1000") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " RATE 3/4 DATA");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "1001") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " Slot idle ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "1010") == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " Rate 1 DATA ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf(state->fsubtype, " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current slot - First half - Data Payload - 1st part
|
// Current slot - Second Half - Data Payload - 2nd part
|
||||||
//trellis
|
|
||||||
for (i = 0; i < 49; i++)
|
for (i = 0; i < 49; i++)
|
||||||
{
|
{
|
||||||
if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method
|
if (state->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+95] = dibit;
|
||||||
}
|
}
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
//dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+95]; //double checked these i+ values make sure we are on the right ones
|
dibit = (int)state->dmr_stereo_payload[i+95];
|
||||||
}
|
}
|
||||||
trellisdibits[i+49] = dibit;
|
|
||||||
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
|
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
|
||||||
info[(2*i) + 99] = (1 & dibit); // bit 0
|
info[(2*i) + 99] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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
|
||||||
|
if (state->dmr_ms_mode == 0 && opts->dmr_mono == 0)
|
||||||
|
{
|
||||||
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
//ending line break
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
END:
|
||||||
|
if (SlotTypeOk == 0 || cach_okay != 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "| CACH/Burst FEC ERR");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
dmr_reset_blocks (opts, state); //failsafe to reset all data header and blocks when bad tact or slottype
|
||||||
|
}
|
||||||
|
|
||||||
// Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit)
|
// Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit)
|
||||||
if (state->dmr_stereo == 0)
|
if (state->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
skipDibit (opts, state, 12 + 49 + 5);
|
skipDibit (opts, state, 12 + 49 + 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
//if using con+ trunking and last voice observed more than x seconds ago, snap back to con+ cc
|
||||||
|
//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)
|
||||||
{
|
{
|
||||||
/* Print the color code */
|
//bug fixed that caused hopping from CC to VC; decreased to 2 seconds.
|
||||||
//if (burst == 0b1001 && state->color_code_ok) //only print on idle for data types, otherwise prints on voice frames.
|
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||||
if (SlotTypeOk == 1)
|
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KCYN);
|
if (opts->use_rigctl == 1) //rigctl tuning
|
||||||
fprintf(stderr, "| Color Code=%02d ", (int)state->color_code);
|
{
|
||||||
fprintf (stderr, "%s", KNRM);
|
if (opts->setmod_bw != 0) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
//state->dmr_color_code = state->color_code;
|
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||||||
}
|
}
|
||||||
else fprintf(stderr, "| ");
|
|
||||||
|
|
||||||
//if(state->color_code_ok) fprintf(stderr, "(OK) |");
|
else if (opts->audio_in_type == 3) //rtl_fm tuning
|
||||||
if(state->color_code_ok) fprintf(stderr, "| (CRC OK ) |"); //add line break
|
{
|
||||||
else
|
//UDP command to tune the RTL dongle
|
||||||
{
|
rtl_udp_tune(opts, state, state->p25_cc_freq);
|
||||||
fprintf (stderr, "%s", KRED);
|
}
|
||||||
fprintf(stderr, "| (CRC ERR) |");
|
opts->p25_is_tuned = 0;
|
||||||
fprintf (stderr, "%s", KNRM);
|
//zero out vc frequencies
|
||||||
}
|
state->p25_vc_freq[0] = 0;
|
||||||
//fprintf (stderr, "\n"); //print line break
|
state->p25_vc_freq[1] = 0;
|
||||||
if (strcmp (state->fsubtype, " ") == 0)
|
memset (state->active_channel, 0, sizeof(state->active_channel));
|
||||||
{
|
state->last_cc_sync_time = time(NULL);
|
||||||
//fprintf(stderr, " Unknown burst type: %s", bursttype);
|
state->is_con_plus = 0; //con+ flag off
|
||||||
fprintf(stderr, " Unknown");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s", state->fsubtype);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(burst)
|
}
|
||||||
{
|
|
||||||
/* Burst = PI header */
|
|
||||||
case 0b0000:
|
|
||||||
{
|
|
||||||
ProcessDmrPIHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = Voice LC header */
|
|
||||||
case 0b0001:
|
|
||||||
{
|
|
||||||
/* Extract data from Voice LC Header */
|
|
||||||
ProcessDmrVoiceLcHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = TLC */
|
|
||||||
case 0b0010:
|
|
||||||
{
|
|
||||||
//Not sure if I really care about this in the context of what it shows
|
|
||||||
ProcessDmrTerminaisonLC(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = CSBK */
|
|
||||||
case 0b0011:
|
|
||||||
{
|
|
||||||
//going to disable until I can find more useful info to present
|
|
||||||
ProcessCSBK(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = MBC Header */
|
|
||||||
case 0b0100:
|
|
||||||
{
|
|
||||||
ProcessMBChData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = MBC Continuation*/
|
|
||||||
case 0b0101:
|
|
||||||
{
|
|
||||||
ProcessMBCData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = DATA Header */
|
|
||||||
case 0b0110:
|
|
||||||
{
|
|
||||||
ProcessDataData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = RATE 1/2 DATA */
|
|
||||||
case 0b0111:
|
|
||||||
{
|
|
||||||
Process12Data(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = RATE 3/4 DATA */
|
|
||||||
case 0b1000:
|
|
||||||
{
|
|
||||||
//need to keep working on improving the trellis decode, but is partially viable now
|
|
||||||
Process34Data(opts, state, trellisdibits, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = Slot idle */
|
|
||||||
case 0b1001:
|
|
||||||
{
|
|
||||||
if(state->color_code_ok && state->dmr_stereo == 0) state->dmr_color_code = state->color_code; //try setting this on idle if crc ok
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = Rate 1 DATA */
|
|
||||||
case 0b1010:
|
|
||||||
{
|
|
||||||
Process1Data(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Burst = Unified Single Block DATA */
|
|
||||||
case 0b1101:
|
|
||||||
{
|
|
||||||
ProcessUnifiedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default */
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
/* Nothing to do */
|
|
||||||
ProcessReservedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} /* End switch(burst) */
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
END:
|
|
||||||
if (SlotTypeOk == 0 || cach_okay != 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, "| ** CACH or Burst Type FEC ERR ** ");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* End processDMRdata() */
|
|
||||||
|
|||||||
645
src/dmr_dburst.c
Normal file
645
src/dmr_dburst.c
Normal file
@@ -0,0 +1,645 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_dburst.c
|
||||||
|
* DMR Data Burst Handling and related BPTC/FEC/CRC Functions
|
||||||
|
*
|
||||||
|
* Portions of BPTC/FEC/CRC code from LouisErigHerve
|
||||||
|
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
//WIP: Unified Single Block Data - USBD -- TODO: USBD LIP Protocol Handling
|
||||||
|
//WIP: UDT Header and Blocks (coded - need samples)
|
||||||
|
//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"
|
||||||
|
|
||||||
|
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t i, j, k;
|
||||||
|
uint32_t CRCExtracted = 0;
|
||||||
|
uint32_t CRCComputed = 0;
|
||||||
|
uint32_t CRCCorrect = 0;
|
||||||
|
uint32_t IrrecoverableErrors = 0;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
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
|
||||||
|
uint8_t BPTCDeInteleavedData[196];
|
||||||
|
uint8_t BPTCDmrDataBit[96];
|
||||||
|
uint8_t BPTCDmrDataByte[12];
|
||||||
|
|
||||||
|
//Embedded Signalling Specific
|
||||||
|
uint8_t BptcDataMatrix[8][16];
|
||||||
|
uint8_t LC_DataBit[77];
|
||||||
|
uint8_t LC_DataBytes[10];
|
||||||
|
int Burst = -1;
|
||||||
|
|
||||||
|
//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_bits, 0, sizeof (DMR_PDU_bits));
|
||||||
|
|
||||||
|
uint8_t R[3];
|
||||||
|
uint8_t BPTCReservedBits = 0;
|
||||||
|
uint8_t is_ras = 0;
|
||||||
|
uint8_t crc_original_validity = 0;
|
||||||
|
|
||||||
|
uint32_t crcmask = 0;
|
||||||
|
uint8_t crclen = 0;
|
||||||
|
|
||||||
|
uint8_t is_bptc = 0;
|
||||||
|
uint8_t is_trellis = 0;
|
||||||
|
uint8_t is_emb = 0;
|
||||||
|
uint8_t is_lc = 0;
|
||||||
|
uint8_t is_full = 0;
|
||||||
|
uint8_t is_udt = 0;
|
||||||
|
uint8_t pdu_len = 0;
|
||||||
|
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
||||||
|
|
||||||
|
uint8_t usbd_st = 0; //usbd service type
|
||||||
|
|
||||||
|
switch(databurst){
|
||||||
|
case 0x00: //PI
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0x6969; //insert pi and 69 jokes here
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " PI ");
|
||||||
|
break;
|
||||||
|
case 0x01: //VLC
|
||||||
|
is_bptc = 1;
|
||||||
|
is_lc = 1;
|
||||||
|
crclen = 24;
|
||||||
|
crcmask = 0x969696;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " VLC ");
|
||||||
|
break;
|
||||||
|
case 0x02: //TLC
|
||||||
|
is_bptc = 1;
|
||||||
|
is_lc = 1;
|
||||||
|
crcmask = 0x999999;
|
||||||
|
crclen = 24;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " TLC ");
|
||||||
|
break;
|
||||||
|
case 0x03: //CSBK
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0xA5A5;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " CSBK");
|
||||||
|
break;
|
||||||
|
case 0x04: //MBC Header
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0xAAAA;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " MBCH");
|
||||||
|
break;
|
||||||
|
case 0x05: //MBC Continuation
|
||||||
|
is_bptc = 1;
|
||||||
|
//let block assembler carry out crc on the completed message
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " MBCC");
|
||||||
|
break;
|
||||||
|
case 0x06: //Data Header
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0xCCCC;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " DATA");
|
||||||
|
break;
|
||||||
|
case 0x07: //1/2 Rate Data
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 9; //confirmed data only
|
||||||
|
crcmask = 0x0F0;
|
||||||
|
pdu_len = 12; //12 bytes unconfirmed
|
||||||
|
sprintf(state->fsubtype, " R12U ");
|
||||||
|
if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
pdu_len = 10;
|
||||||
|
pdu_start = 2; //start at plus two when assembling
|
||||||
|
sprintf(state->fsubtype, " R12C ");
|
||||||
|
}
|
||||||
|
if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks
|
||||||
|
{
|
||||||
|
is_udt = 1;
|
||||||
|
sprintf(state->fsubtype, " UDTC ");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x08: //3/4 Rate Data
|
||||||
|
is_trellis = 1;
|
||||||
|
crclen = 9; //confirmed data only
|
||||||
|
crcmask = 0x1FF;
|
||||||
|
pdu_len = 18; //18 bytes unconfirmed
|
||||||
|
sprintf(state->fsubtype, " R34U ");
|
||||||
|
if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
pdu_len = 16;
|
||||||
|
pdu_start = 2; //start at plus two when assembling
|
||||||
|
sprintf(state->fsubtype, " R34C ");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x09: //Idle
|
||||||
|
//pseudo-random data fill, no need to do anything with this
|
||||||
|
sprintf(state->fsubtype, " IDLE ");
|
||||||
|
break;
|
||||||
|
case 0x0A: //1 Rate Data
|
||||||
|
crclen = 9; //confirmed data only
|
||||||
|
crcmask = 0x10F;
|
||||||
|
is_full = 1;
|
||||||
|
pdu_len = 25; //196 bits - 24.5 bytes
|
||||||
|
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;
|
||||||
|
case 0x0B: //Unified Single Block Data USBD
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0x3333;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " USBD ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
//special types (not real data 'sync' bursts)
|
||||||
|
case 0xEB: //Embedded Signalling
|
||||||
|
crclen = 5;
|
||||||
|
is_emb = 1;
|
||||||
|
pdu_len = 9;
|
||||||
|
break;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//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 (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
||||||
|
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
|
||||||
|
if (is_bptc)
|
||||||
|
{
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCComputed = 0;
|
||||||
|
IrrecoverableErrors = 0;
|
||||||
|
|
||||||
|
/* Deinterleave DMR data */
|
||||||
|
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
|
||||||
|
|
||||||
|
/* Extract the BPTC 196,96 DMR data */
|
||||||
|
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
||||||
|
|
||||||
|
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
|
||||||
|
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x04);
|
||||||
|
|
||||||
|
//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 */
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
BPTCDmrDataByte[i] = 0;
|
||||||
|
for(j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1;
|
||||||
|
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01);
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
|
||||||
|
CRCExtracted = 0;
|
||||||
|
for(i = 0; i < crclen; i++)
|
||||||
|
{
|
||||||
|
CRCExtracted = CRCExtracted << 1;
|
||||||
|
CRCExtracted = CRCExtracted | (uint32_t)(BPTCDmrDataBit[i + 96 - crclen] & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */
|
||||||
|
CRCExtracted = CRCExtracted ^ crcmask;
|
||||||
|
|
||||||
|
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
||||||
|
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
||||||
|
|
||||||
|
//set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these)
|
||||||
|
else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
||||||
|
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||||
|
else CRCCorrect = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the 'RAS Flag', if no irrecoverable errors but bad crc, only when enabled by user (to prevent a lot of bad data CSBKs)
|
||||||
|
if (opts->aggressive_framesync == 0 && CRCCorrect == 0 && IrrecoverableErrors == 0 && BPTCReservedBits == 4) is_ras = 1;
|
||||||
|
|
||||||
|
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
|
||||||
|
if (BPTCDmrDataByte[1] == 0x68) is_ras = 0;
|
||||||
|
|
||||||
|
//if this is a RAS system, set the CRC to okay if irrecoverable errors are okay
|
||||||
|
//if we don't do this, then we can't view some data (CSBKs on RAS enabled systems)
|
||||||
|
if (is_ras == 1)
|
||||||
|
{
|
||||||
|
crc_original_validity = CRCCorrect;
|
||||||
|
CRCCorrect = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
|
||||||
|
{
|
||||||
|
if (CRCCorrect)
|
||||||
|
{
|
||||||
|
state->data_block_crc_valid[slot][0] = 1;
|
||||||
|
}
|
||||||
|
else state->data_block_crc_valid[slot][0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert corrected x bytes into x*8 bits */
|
||||||
|
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
||||||
|
{
|
||||||
|
BPTCDmrDataBit[j + 0] = (BPTCDmrDataByte[i+pdu_start] >> 7) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 1] = (BPTCDmrDataByte[i+pdu_start] >> 6) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 2] = (BPTCDmrDataByte[i+pdu_start] >> 5) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 3] = (BPTCDmrDataByte[i+pdu_start] >> 4) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 4] = (BPTCDmrDataByte[i+pdu_start] >> 3) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 5] = (BPTCDmrDataByte[i+pdu_start] >> 2) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 6] = (BPTCDmrDataByte[i+pdu_start] >> 1) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert to DMR_PDU and DMR_PDU_bits
|
||||||
|
for (i = 0; i < pdu_len; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < pdu_len * 8; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU_bits[i] = BPTCDmrDataBit[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Embedded Signalling will use BPTC 128x77
|
||||||
|
if (is_emb)
|
||||||
|
{
|
||||||
|
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCComputed = 0;
|
||||||
|
IrrecoverableErrors = 0;
|
||||||
|
|
||||||
|
/* First step : Reconstitute the BPTC 16x8 matrix */
|
||||||
|
Burst = 1; /* Burst B to E contains embedded signaling data */
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
for(j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
/* Only the LSBit of the byte is stored */
|
||||||
|
BptcDataMatrix[j][i] = state->dmr_embedded_signalling[slot][Burst][k+8];
|
||||||
|
k++;
|
||||||
|
|
||||||
|
/* Go on to the next burst once 32 bit
|
||||||
|
* of the SNYC have been stored */
|
||||||
|
if(k >= 32)
|
||||||
|
{
|
||||||
|
k = 0;
|
||||||
|
Burst++;
|
||||||
|
}
|
||||||
|
} /* End for(j = 0; j < 8; j++) */
|
||||||
|
} /* End for(i = 0; i < 16; i++) */
|
||||||
|
|
||||||
|
/* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */
|
||||||
|
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
||||||
|
|
||||||
|
/* Reconstitute the 5 bit CRC */
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5);
|
||||||
|
|
||||||
|
/* Compute the 5 bit CRC */
|
||||||
|
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
||||||
|
|
||||||
|
if(CRCExtracted == CRCComputed) CRCCorrect = 1;
|
||||||
|
else CRCCorrect = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 72; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU_bits[i] = LC_DataBit[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&LC_DataBit[i*8], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//the ugly one
|
||||||
|
if (is_trellis)
|
||||||
|
{
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCComputed = 0;
|
||||||
|
IrrecoverableErrors = 1;
|
||||||
|
bool trellis_good = TRUE;
|
||||||
|
|
||||||
|
uint8_t tdibits[98];
|
||||||
|
memset (tdibits, 0, sizeof(tdibits));
|
||||||
|
|
||||||
|
//reconstitute info bits into reverse ordered dibits for the trellis decoder
|
||||||
|
for (i = 0; i < 98; i++)
|
||||||
|
{
|
||||||
|
tdibits[97-i] = (info[i*2] << 1) | info[i*2+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char TrellisReturn[18];
|
||||||
|
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
||||||
|
|
||||||
|
trellis_good = CDMRTrellisDecode(tdibits, TrellisReturn);
|
||||||
|
if (trellis_good == TRUE) IrrecoverableErrors = 0;
|
||||||
|
// else (fprintf (stderr, " Trellis Failure")); //debug print
|
||||||
|
|
||||||
|
for (i = 0; i < pdu_len; i++) //18
|
||||||
|
{
|
||||||
|
DMR_PDU[i] = (uint8_t)TrellisReturn[i+pdu_start];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0, j = 0; i < 18; i++, j+=8)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
//confirmed working now!
|
||||||
|
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
|
||||||
|
{
|
||||||
|
// IrrecoverableErrors = 9; //set as a shim since the trellis decoder is a pain to fix
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
DMR_PDU_bits[j + 0] = (TrellisReturn[i+pdu_start] >> 7) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 1] = (TrellisReturn[i+pdu_start] >> 6) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 2] = (TrellisReturn[i+pdu_start] >> 5) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 3] = (TrellisReturn[i+pdu_start] >> 4) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 4] = (TrellisReturn[i+pdu_start] >> 3) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 5] = (TrellisReturn[i+pdu_start] >> 2) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 6] = (TrellisReturn[i+pdu_start] >> 1) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_full) //Rate 1 Data
|
||||||
|
{
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCComputed = 0;
|
||||||
|
IrrecoverableErrors = 0; //implicit, since there is no encoding
|
||||||
|
|
||||||
|
for (i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8);
|
||||||
|
}
|
||||||
|
//leftover 4 bits, manual doesn't say which 4 so assuming last 4
|
||||||
|
//pushing last 4 to 25 and shifting it
|
||||||
|
DMR_PDU[25] = (uint8_t)ConvertBitIntoBytes(&info[192], 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
|
||||||
|
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//time for some pi
|
||||||
|
if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
//full link control
|
||||||
|
if (databurst == 0x01) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 1); //VLC
|
||||||
|
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
|
||||||
|
|
||||||
|
//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, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors);
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
//control signalling types (CSBK, MBC)
|
||||||
|
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
|
||||||
|
if (databurst == 0x04)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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 != 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
|
||||||
|
if (opts->payload == 1 && databurst != 0x09)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " DMR PDU Payload ");
|
||||||
|
for (i = 0; i < pdu_len; i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", DMR_PDU[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
// if (dbsn) fprintf (stderr, " SN %X", dbsn);
|
||||||
|
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
1075
src/dmr_flco.c
Normal file
1075
src/dmr_flco.c
Normal file
File diff suppressed because it is too large
Load Diff
316
src/dmr_le.c
Normal file
316
src/dmr_le.c
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_le.c
|
||||||
|
* DMR Late Entry MI Fragment Assembly, Procesing, and related Alg functions
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//gather ambe_fr mi fragments for processing
|
||||||
|
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])
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
|
||||||
|
//collect our fragments and place them into storage
|
||||||
|
state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
|
||||||
|
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
|
||||||
|
state->late_entry_mi_fragment[slot][vc][2] = (uint64_t)ConvertBitIntoBytes(&ambe_fr3[3][0], 4);
|
||||||
|
|
||||||
|
if (vc == 6) dmr_late_entry_mi (opts, state);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
int i, j;
|
||||||
|
int g[3];
|
||||||
|
unsigned char mi_go_bits[24];
|
||||||
|
|
||||||
|
uint64_t mi_test = 0;
|
||||||
|
uint64_t go_test = 0;
|
||||||
|
uint64_t mi_corrected = 0;
|
||||||
|
uint64_t go_corrected = 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) |
|
||||||
|
(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][2] << 8) | (state->late_entry_mi_fragment[slot][2][2] << 4) | (state->late_entry_mi_fragment[slot][3][2] << 0);
|
||||||
|
|
||||||
|
go_test = (state->late_entry_mi_fragment[slot][4][0] << 32L) | (state->late_entry_mi_fragment[slot][5][0] << 28) | (state->late_entry_mi_fragment[slot][6][0] << 24) |
|
||||||
|
(state->late_entry_mi_fragment[slot][4][1] << 20) | (state->late_entry_mi_fragment[slot][5][1] << 16) | (state->late_entry_mi_fragment[slot][6][1] << 12) |
|
||||||
|
(state->late_entry_mi_fragment[slot][4][2] << 8) | (state->late_entry_mi_fragment[slot][5][2] << 4) | (state->late_entry_mi_fragment[slot][6][2] << 0);
|
||||||
|
|
||||||
|
for (j = 0; j < 3; j++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
mi_go_bits[i] = (( mi_test << (i+j*12) ) & 0x800000000) >> 35;
|
||||||
|
mi_go_bits[i+12] = (( go_test << (i+j*12) ) & 0x800000000) >> 35;
|
||||||
|
}
|
||||||
|
//execute golay decode and assign pass or fail to g
|
||||||
|
if ( Golay_24_12_decode(mi_go_bits) ) g[j] = 1;
|
||||||
|
else g[j] = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
mi_corrected = mi_corrected << 1;
|
||||||
|
mi_corrected |= mi_go_bits[i];
|
||||||
|
go_corrected = go_corrected << 1;
|
||||||
|
go_corrected |= mi_go_bits[i+12];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mi_final = 0;
|
||||||
|
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
if (g[0] && g[1] && g[2])
|
||||||
|
{
|
||||||
|
if (slot == 0 && state->payload_algid != 0)
|
||||||
|
{
|
||||||
|
if (state->payload_mi != mi_final)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
|
||||||
|
state->payload_mi = mi_final;
|
||||||
|
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 (state->payload_miR != mi_final)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
|
||||||
|
state->payload_miR = mi_final;
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
//run afterwards, or le verification won't match up properly
|
||||||
|
if (state->payload_algidR != 0x21)
|
||||||
|
{
|
||||||
|
LFSR64 (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
state->dropL = 256;
|
||||||
|
|
||||||
|
if (state->K1 != 0)
|
||||||
|
{
|
||||||
|
state->DMRvcL = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->payload_algid == 0x21)
|
||||||
|
{
|
||||||
|
LFSR (state);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
state->dropR = 256;
|
||||||
|
|
||||||
|
if (state->K1 != 0)
|
||||||
|
{
|
||||||
|
state->DMRvcR = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->payload_algidR == 0x21)
|
||||||
|
{
|
||||||
|
LFSR (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
state->dropL = 256;
|
||||||
|
state->dropR = 256;
|
||||||
|
state->DMRvcL = 0;
|
||||||
|
state->DMRvcR = 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];
|
||||||
|
memset (sbrc_interleaved, 0, sizeof(sbrc_interleaved));
|
||||||
|
memset (sbrc_return, 0, sizeof(sbrc_return));
|
||||||
|
uint32_t irr_err = 0;
|
||||||
|
uint32_t sbrc_hex = 0;
|
||||||
|
uint16_t crc_extracted = 7777;
|
||||||
|
uint16_t crc_computed = 9999;
|
||||||
|
uint8_t crc_okay = 0;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
//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
|
||||||
|
//unknown what the other values are (see Cap+ 0x313 and 0x643)
|
||||||
|
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, 11);
|
||||||
|
if (crc_extracted == crc_computed) crc_okay = 1;
|
||||||
|
}
|
||||||
|
else crc_okay = 1; //SB
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide the sb/rc behind the payload printer, won't be useful to most people right now
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n %s", KCYN);
|
||||||
|
if (power == 0) fprintf (stderr, " SB: ");
|
||||||
|
if (power == 1) fprintf (stderr, " RC: ");
|
||||||
|
for(i = 0; i < 11; i++)
|
||||||
|
{
|
||||||
|
sbrc_hex = sbrc_hex << 1;
|
||||||
|
sbrc_hex |= sbrc_return[i] & 1; //sbrc_return
|
||||||
|
fprintf (stderr, "%d", sbrc_return[i]);
|
||||||
|
}
|
||||||
|
fprintf (stderr, " - %03X", sbrc_hex);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
if (crc_okay == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
if (opts->payload == 1) fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Disabling the lower portion until this can be completely ironed out
|
||||||
|
|
||||||
|
//sbrc_hex value of 0x313 seems to be some Cap+ Thing,
|
||||||
|
//also observed 0x643 on another cap+ system (site id, status? something?)
|
||||||
|
// uint8_t sbrc_opcode = sbrc_hex;
|
||||||
|
// uint8_t alg = sbrc_hex & 0x7;
|
||||||
|
// uint8_t key = (sbrc_hex >> 3) & 0xFF;
|
||||||
|
|
||||||
|
//this pattern seems to fit the multiple cap+ systems I've observed
|
||||||
|
//test on site '7' to make sure (works on 1 and 4)
|
||||||
|
// uint8_t cap_site = (sbrc_hex >> 4) & 0x7;
|
||||||
|
|
||||||
|
// if (opts->payload == 1)
|
||||||
|
// {
|
||||||
|
// if (irr_err != 0) fprintf (stderr, "%s (FEC ERR) %d %s", KRED, irr_err, KNRM);
|
||||||
|
// if (irr_err == 0)
|
||||||
|
// {
|
||||||
|
// if (sbrc_hex == 0) ; //NULL
|
||||||
|
// else if (strcmp (state->dmr_branding_sub, "Cap+ ") == 0)
|
||||||
|
// {
|
||||||
|
// //Cap+ Site ID seems to fit here on samples I've seen with the alternate SB signalling, but no idea what signals that info and not the emb alg/key info
|
||||||
|
// fprintf (stderr, "%s ", KYEL);
|
||||||
|
// fprintf (stderr, "\n ");
|
||||||
|
// fprintf (stderr, " Capacity Plus Site %d", cap_site);
|
||||||
|
// fprintf (stderr, "%s ", KNRM);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
|
||||||
|
// if (slot == 0)
|
||||||
|
// {
|
||||||
|
// //key and alg only present SOME times, not all,
|
||||||
|
// //also, intermixed with other signalling
|
||||||
|
// //needs more study first!
|
||||||
|
// if (state->dmr_so & 0x40 && key != 0 && state->payload_keyid == 0)
|
||||||
|
// {
|
||||||
|
// if (opts->payload == 1)
|
||||||
|
// {
|
||||||
|
// fprintf (stderr, "%s ", KYEL);
|
||||||
|
// fprintf (stderr, "\n Slot 1");
|
||||||
|
// fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
|
||||||
|
// fprintf (stderr, "%s ", KNRM);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //needs more study before assignment
|
||||||
|
// // state->payload_keyid = key;
|
||||||
|
// // state->payload_algid = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (slot == 1)
|
||||||
|
// {
|
||||||
|
// if (state->dmr_soR & 0x40 && key != 0 && state->payload_keyidR == 0)
|
||||||
|
// {
|
||||||
|
// if (opts->payload == 1)
|
||||||
|
// {
|
||||||
|
// fprintf (stderr, "%s ", KYEL);
|
||||||
|
// fprintf (stderr, "\n Slot 2");
|
||||||
|
// fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
|
||||||
|
// fprintf (stderr, "%s ", KNRM);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //needs more study before assignment
|
||||||
|
// // 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
763
src/dmr_ms.c
763
src/dmr_ms.c
File diff suppressed because it is too large
Load Diff
534
src/dmr_pdu.c
Normal file
534
src/dmr_pdu.c
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_bs.c
|
||||||
|
* DMR Data (1/2, 3/4, 1) PDU Decoding
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
char * getTimeL(void) //get pretty hh:mm:ss timestamp
|
||||||
|
{
|
||||||
|
time_t t = time(NULL);
|
||||||
|
|
||||||
|
char * curr;
|
||||||
|
char * stamp = asctime(localtime( & t));
|
||||||
|
|
||||||
|
curr = strtok(stamp, " ");
|
||||||
|
curr = strtok(NULL, " ");
|
||||||
|
curr = strtok(NULL, " ");
|
||||||
|
curr = strtok(NULL, " ");
|
||||||
|
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * getDateL(void) {
|
||||||
|
char datename[120];
|
||||||
|
char * curr2;
|
||||||
|
struct tm * to;
|
||||||
|
time_t t;
|
||||||
|
t = time(NULL);
|
||||||
|
to = localtime( & t);
|
||||||
|
strftime(datename, sizeof(datename), "%Y/%m/%d", to);
|
||||||
|
curr2 = strtok(datename, " ");
|
||||||
|
return curr2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t message_len = 0;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t blocks = state->data_header_blocks[slot];
|
||||||
|
uint8_t padding = state->data_header_padding[slot];
|
||||||
|
uint8_t lrrp_conf = 0;
|
||||||
|
|
||||||
|
//check for more available flag info, etc on these prior to running
|
||||||
|
if (DMR_PDU[0] == 0x01) dmr_locn (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
|
||||||
|
state->data_conf_data[slot] = 0; //flag off confirmed data after processing it
|
||||||
|
state->data_p_head[slot] = 0; //flag off prop_head data after processing it
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//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)
|
||||||
|
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
|
||||||
|
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
uint16_t message_len = 0;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t blocks = state->data_header_blocks[slot];
|
||||||
|
uint8_t padding = state->data_header_padding[slot];
|
||||||
|
uint8_t lrrp_confidence = 0; //variable to increment based on number of tokens found, the more, the higher the confidence level
|
||||||
|
|
||||||
|
//source/dest and ports
|
||||||
|
uint32_t source = 0;
|
||||||
|
uint32_t dest = 0;
|
||||||
|
uint16_t port_s = 0;
|
||||||
|
uint16_t port_d = 0;
|
||||||
|
|
||||||
|
//time
|
||||||
|
uint16_t year = 0;
|
||||||
|
uint16_t month = 0;
|
||||||
|
uint16_t day = 0;
|
||||||
|
uint16_t hour = 0;
|
||||||
|
uint16_t minute = 0;
|
||||||
|
uint16_t second = 0;
|
||||||
|
|
||||||
|
//lat-long-radius-alt
|
||||||
|
uint32_t lat = 0;
|
||||||
|
uint32_t lon = 0;
|
||||||
|
uint16_t rad = 0; //or Azimuth in Circle 3D?
|
||||||
|
uint8_t alt = 0;
|
||||||
|
double lat_unit = (double)180/(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
|
||||||
|
uint16_t vel = 0;
|
||||||
|
double velocity = 0;
|
||||||
|
uint8_t vel_set = 0;
|
||||||
|
|
||||||
|
//track, direction, degrees
|
||||||
|
uint8_t degrees = 0;
|
||||||
|
uint8_t deg_set = 0;
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
// sprintf (deg_glyph, "%s", "d");
|
||||||
|
|
||||||
|
//triggered information report
|
||||||
|
uint8_t report = 0;
|
||||||
|
uint8_t pot_report = 0; //potential report by finding 0x0D and backtracking a few bytes
|
||||||
|
|
||||||
|
|
||||||
|
//start looking for tokens (using my best understanding of the ok-dmr library xml and python files)
|
||||||
|
for (i = 0; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch(DMR_PDU[i]){
|
||||||
|
case 0x0C: //LRRP_TriggeredLocationReport
|
||||||
|
if (source == 0)
|
||||||
|
{
|
||||||
|
source = (DMR_PDU[i+1] << 16 ) + (DMR_PDU[i+2] << 8) + DMR_PDU[i+3];
|
||||||
|
dest = (DMR_PDU[i+5] << 16 ) + (DMR_PDU[i+6] << 8) + DMR_PDU[i+7];
|
||||||
|
port_s = (DMR_PDU[i+8] << 8) + DMR_PDU[i+9];
|
||||||
|
port_d = (DMR_PDU[i+10] << 8) + DMR_PDU[i+11];
|
||||||
|
i += 11;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//may need more 'report' types to trigger various scenarios
|
||||||
|
//disable all but 0x1F if issues or falsing happens often
|
||||||
|
//case 0x1D: //ARRP_TriggeredInformationAnswer_NCDT
|
||||||
|
//case 0x1E: //ARRP_TriggeredInformationReport = FALSE?
|
||||||
|
//case 0x25: //ARRP_UnsolicitedInformationReport_NCDT = (0x25, True, "")
|
||||||
|
//case 0x26: //ARRP_UnsolicitedInformationReport_NCDT
|
||||||
|
//case 0x13: //LRRP_UnsolicitedLocationReport_NCDT
|
||||||
|
//case 0x15: //LRRP_LocationProtocolReport_NCDT
|
||||||
|
//case 0x21: //ARRP_TriggeredInformationStopRequest_NCDT
|
||||||
|
case 0x1F: //ARRP_TriggeredInformationReport_NCDT
|
||||||
|
if (report == 0)
|
||||||
|
{
|
||||||
|
report = DMR_PDU[i];
|
||||||
|
i += 1;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//0x0D always seems to follow 0x1F or other 'report' types two/three bytes later
|
||||||
|
case 0x0D: //message len indicator //LRRP_TriggeredLocationReport_NCDT
|
||||||
|
if (report > 0 && message_len == 0)
|
||||||
|
{
|
||||||
|
message_len = DMR_PDU[i+1];
|
||||||
|
i += 1;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
else if (message_len == 0)
|
||||||
|
{
|
||||||
|
if (i > 3)
|
||||||
|
{
|
||||||
|
if (DMR_PDU[i-3] > 0) pot_report = DMR_PDU[i-3];
|
||||||
|
if (pot_report < 0x27)
|
||||||
|
{
|
||||||
|
report = pot_report;
|
||||||
|
message_len = DMR_PDU[i+1];
|
||||||
|
i += 1;
|
||||||
|
//no lrrp_confidence on speculative reporting, but will print it as such.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//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
|
||||||
|
i += 2;
|
||||||
|
break;
|
||||||
|
//answer and report tokens
|
||||||
|
case 0x51: //circle-2d
|
||||||
|
case 0x54: //circle-3d
|
||||||
|
case 0x55: //circle-3d
|
||||||
|
if (message_len > 0 && lat == 0)
|
||||||
|
{
|
||||||
|
// 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 );
|
||||||
|
rad = (DMR_PDU[i+9] << 8) + DMR_PDU[i+10];
|
||||||
|
i += 10;
|
||||||
|
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
||||||
|
else lat = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x34: //Time Interval Periodic Trigger (timestamp)
|
||||||
|
case 0x35: //Time Interval Periodic Trigger (timestamp)
|
||||||
|
if (message_len > 0 && year == 0)
|
||||||
|
{
|
||||||
|
year = (DMR_PDU[i+1] << 6) + (DMR_PDU[i+2] >> 2);
|
||||||
|
month = ((DMR_PDU[i+2] & 0x3) << 2) + ((DMR_PDU[i+3] & 0xC0) >> 6);
|
||||||
|
day = ((DMR_PDU[i+3] & 0x30) >> 1) + ((DMR_PDU[i+3] & 0x0E) >> 1);
|
||||||
|
hour = ((DMR_PDU[i+3] & 0x01) << 4) + ((DMR_PDU[i+4] & 0xF0) >> 4);
|
||||||
|
minute = ((DMR_PDU[i+4] & 0x0F) << 2) + ((DMR_PDU[i+5] & 0xC0) >> 6);
|
||||||
|
second = (DMR_PDU[i+5] & 0x3F);
|
||||||
|
i += 5;
|
||||||
|
//sanity check
|
||||||
|
if (year > 2000 && year <= 2025) lrrp_confidence++;
|
||||||
|
if (year > 2025 || year < 2000) year = 0; //needs future proofing
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x66: //point-2d
|
||||||
|
if (message_len > 0 && lat == 0)
|
||||||
|
{
|
||||||
|
// 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 );
|
||||||
|
i += 8;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x69: //point-3d
|
||||||
|
case 0x6A: //point-3d
|
||||||
|
if (message_len > 0 && lat == 0)
|
||||||
|
{
|
||||||
|
// 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 );
|
||||||
|
alt = DMR_PDU[i+9];
|
||||||
|
i += 9;
|
||||||
|
if (lat > 0 && lon > 0) lrrp_confidence++;
|
||||||
|
else lat = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x36: //protocol-version
|
||||||
|
i += 1;
|
||||||
|
//lrrp_confidence++;
|
||||||
|
break;
|
||||||
|
//speed/velocity
|
||||||
|
case 0x70: //speed-virt
|
||||||
|
case 0x6C: //speed-hor
|
||||||
|
if (message_len > 0 && vel_set == 0)
|
||||||
|
{
|
||||||
|
vel = (DMR_PDU[i+1] << 8) + DMR_PDU[i+2]; //don't think this is meant to be shifted, but doing it anyways for comparison
|
||||||
|
velocity = ( ((double)( (DMR_PDU[i+1] ) + DMR_PDU[i+2] )) / ( (double)128));
|
||||||
|
vel_set = 1;
|
||||||
|
i += 2;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x56: //direction-hor
|
||||||
|
if (message_len > 0 && deg_set == 0)
|
||||||
|
{
|
||||||
|
degrees = DMR_PDU[i+1] * 2;
|
||||||
|
deg_set = 1;
|
||||||
|
i += 1;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//below all unknown
|
||||||
|
case 0x37: //result
|
||||||
|
case 0x38: //result
|
||||||
|
case 0x39: //result 'operations error'? attributes 0x22?
|
||||||
|
case 0x6B: //unknown-uint8
|
||||||
|
case 0x65: //lev-conf
|
||||||
|
default:
|
||||||
|
//do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (report && message_len > 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
||||||
|
if (lrrp_confidence >= 3) //find the sweet magical number
|
||||||
|
{
|
||||||
|
//now we can open our lrrp file and write to it as well
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
|
//write current date/time if not present 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, "%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 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, "%08d\t", source); //add source form decoded audio if available, else its from the header
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pot_report)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||||
|
}
|
||||||
|
if (report)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Report: 0x%02X", report);
|
||||||
|
if (report == 0x1F) fprintf (stderr, " ARRP_TriggeredInformationReport_NCDT "); //customize later when more is learned
|
||||||
|
if (report == 0x21) fprintf (stderr, " ARRP_TriggeredInformationStopRequest_NCDT ");
|
||||||
|
if (report == 0x22) fprintf (stderr, " ARRP_TriggeredInformationStopAnswer ");
|
||||||
|
if (report == 0x25) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
|
||||||
|
if (report == 0x26) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
|
||||||
|
if (report == 0x27) fprintf (stderr, " ARRP_InformationProtocolRequest_NCDT ");
|
||||||
|
if (report == 0x13) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
|
||||||
|
if (report == 0x15) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (source)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Source: %08d - %04d", source, port_s);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Destination: %08d - %04d", dest, port_d);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (year)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Time: ");
|
||||||
|
fprintf (stderr, " %04d.%02d.%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (lat)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
//need to check these new calcs for accuracy accross the globe, both lat and lon
|
||||||
|
|
||||||
|
//two's compliment-ish testing on these bytes
|
||||||
|
if (lat & 0x80000000) //8
|
||||||
|
{
|
||||||
|
lat = lat & 0x7FFFFFFF;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
fprintf (pFile, "%.5lf\t", lat_fin);
|
||||||
|
fprintf (pFile, "%.5lf\t", lon_fin);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (rad)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Radius: %dm", rad); //unsure of 'units' or calculation for radius (meters?)
|
||||||
|
}
|
||||||
|
if (alt)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Altitude: %dm", alt); //unsure of 'units' or calculation for alt (meters?)
|
||||||
|
}
|
||||||
|
if (vel_set)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Velocity: %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity));
|
||||||
|
if (opts->lrrp_file_output == 1) fprintf (pFile, "%.3lf\t ", (velocity * 3.6) );
|
||||||
|
}
|
||||||
|
if (deg_set)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Track: %d%s", degrees, deg_glyph);
|
||||||
|
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
||||||
|
}
|
||||||
|
|
||||||
|
//close open file
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
fprintf (pFile, "\n");
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
//save to string for ncurses
|
||||||
|
if (!source) source = state->dmr_lrrp_source[state->currentslot];
|
||||||
|
char velstr[20];
|
||||||
|
char degstr[20];
|
||||||
|
char lrrpstr[100];
|
||||||
|
sprintf (lrrpstr, "%s", "");
|
||||||
|
sprintf (velstr, "%s", "");
|
||||||
|
sprintf (degstr, "%s", "");
|
||||||
|
if (lat) sprintf (lrrpstr, "LRRP %0d (%lf, %lf)", source, lat_fin, lon_fin);
|
||||||
|
if (vel_set) sprintf (velstr, " %.4lf km/h", velocity * 3.6);
|
||||||
|
if (deg_set) sprintf (degstr, " %d%s ", degrees, deg_glyph);
|
||||||
|
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (pot_report)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||||
|
}
|
||||||
|
|
||||||
|
LRRP_END:
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
156
src/dmr_pi.c
Normal file
156
src/dmr_pi.c
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_pi.c
|
||||||
|
* DMR Privacy Indicator and LFSR Function
|
||||||
|
*
|
||||||
|
* LFSR code courtesy of https://github.com/mattames/LFSR/
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||||
|
{
|
||||||
|
|
||||||
|
if((IrrecoverableErrors == 0))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
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]) );
|
||||||
|
if (state->payload_algid < 0x26)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s ", KYEL);
|
||||||
|
fprintf (stderr, "\n Slot 1");
|
||||||
|
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);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (state->payload_algid != 0x21)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
LFSR64 (state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->payload_algid >= 0x26)
|
||||||
|
{
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
state->payload_mi = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
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]) );
|
||||||
|
if (state->payload_algidR < 0x26)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s ", KYEL);
|
||||||
|
fprintf (stderr, "\n Slot 2");
|
||||||
|
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);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (state->payload_algidR != 0x21)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
LFSR64 (state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->payload_algidR >= 0x26)
|
||||||
|
{
|
||||||
|
state->payload_algidR = 0;
|
||||||
|
state->payload_keyidR = 0;
|
||||||
|
state->payload_miR = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LFSR(dsd_state * state)
|
||||||
|
{
|
||||||
|
int lfsr = 0;
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
lfsr = state->payload_mi;
|
||||||
|
}
|
||||||
|
else lfsr = state->payload_miR;
|
||||||
|
|
||||||
|
uint8_t cnt = 0;
|
||||||
|
|
||||||
|
for(cnt=0;cnt<32;cnt++)
|
||||||
|
{
|
||||||
|
// Polynomial is C(x) = x^32 + x^4 + x^2 + 1
|
||||||
|
int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 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%08X", lfsr);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->payload_mi = lfsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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%08X", lfsr);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,395 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* 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
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include "dmr_const.h"
|
|
||||||
#include "p25p1_check_hdu.h"
|
|
||||||
|
|
||||||
void ProcessDMR (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
uint32_t i, j;
|
|
||||||
uint32_t Frame;
|
|
||||||
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameL = NULL;
|
|
||||||
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameR = NULL;
|
|
||||||
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameRC = NULL; //Reverse Test for MS Backwards
|
|
||||||
|
|
||||||
int *errs;
|
|
||||||
int *errs2;
|
|
||||||
unsigned long long int k;
|
|
||||||
int x;
|
|
||||||
k = 0;
|
|
||||||
|
|
||||||
TSVoiceSupFrameL = &state->TS1SuperFrame;
|
|
||||||
TSVoiceSupFrameR = &state->TS2SuperFrame;
|
|
||||||
|
|
||||||
int Pr[256] = {
|
|
||||||
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
|
|
||||||
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
|
|
||||||
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
|
|
||||||
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E, //31
|
|
||||||
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
|
|
||||||
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
|
|
||||||
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
|
|
||||||
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36, //63
|
|
||||||
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
|
|
||||||
0x614C, 0xAE4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
|
|
||||||
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
|
|
||||||
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57, //95
|
|
||||||
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
|
|
||||||
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
|
|
||||||
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
|
|
||||||
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F, //127
|
|
||||||
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
|
|
||||||
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
|
|
||||||
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
|
|
||||||
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897, //159
|
|
||||||
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
|
|
||||||
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
|
|
||||||
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
|
|
||||||
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF, //191
|
|
||||||
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
|
|
||||||
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
|
|
||||||
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
|
|
||||||
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3, //223
|
|
||||||
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
|
|
||||||
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
|
|
||||||
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
|
|
||||||
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if ( (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
|
||||||
(state->currentslot == 0 && state->K > 0 && state->M == 1) )
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf(stderr, " PrK %lld", state->K);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
k = Pr[state->K];
|
|
||||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
|
||||||
|
|
||||||
for(Frame = 0; Frame < 6; Frame++)
|
|
||||||
{
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
for(j = 0; j < 48; j++)
|
|
||||||
{
|
|
||||||
x = ( ((k << j) & 0x800000000000) >> 47 );
|
|
||||||
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (state->currentslot == 1 && state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
|
||||||
(state->currentslot == 1 && state->K > 0 && state->M == 1) )
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf(stderr, " PrK %lld", state->K);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
k = Pr[state->K];
|
|
||||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
|
||||||
|
|
||||||
for(Frame = 0; Frame < 6; Frame++)
|
|
||||||
{
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
for(j = 0; j < 48; j++)
|
|
||||||
{
|
|
||||||
x = ( ((k << j) & 0x800000000000) >> 47 );
|
|
||||||
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (state->currentslot == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
|
||||||
(state->currentslot == 0 && state->K1 > 0 && state->M == 1) )
|
|
||||||
{
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
unsigned long long int k1 = state->K1;
|
|
||||||
unsigned long long int k2 = state->K2;
|
|
||||||
unsigned long long int k3 = state->K3;
|
|
||||||
unsigned long long int k4 = state->K4;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf(stderr, " SPT %010llX", k1);
|
|
||||||
|
|
||||||
int T_Key[256] = {0};
|
|
||||||
int pN[882] = {0};
|
|
||||||
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
if (k2 == 0)
|
|
||||||
{
|
|
||||||
len = 39;
|
|
||||||
k1 = k1 << 24;
|
|
||||||
}
|
|
||||||
if (k2 != 0)
|
|
||||||
{
|
|
||||||
len = 127;
|
|
||||||
fprintf(stderr, " %016llX", k2);
|
|
||||||
}
|
|
||||||
if (k4 != 0)
|
|
||||||
{
|
|
||||||
len = 255;
|
|
||||||
fprintf(stderr, " %016llX", k3);
|
|
||||||
fprintf(stderr, " %016llX", k4);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
|
|
||||||
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
|
|
||||||
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
|
|
||||||
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 882; i++)
|
|
||||||
{
|
|
||||||
pN[i] = T_Key[pos];
|
|
||||||
pos++;
|
|
||||||
if (pos > len)
|
|
||||||
{
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = 0;
|
|
||||||
for(Frame = 0; Frame < 6; Frame++)
|
|
||||||
{
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
for(j = 0; j < 49; j++)
|
|
||||||
{
|
|
||||||
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= pN[pos];
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (state->currentslot == 1 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
|
|
||||||
(state->currentslot == 1 && state->K1 > 0 && state->M == 1) )
|
|
||||||
{
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
unsigned long long int k1 = state->K1;
|
|
||||||
unsigned long long int k2 = state->K2;
|
|
||||||
unsigned long long int k3 = state->K3;
|
|
||||||
unsigned long long int k4 = state->K4;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf(stderr, " SPT %010llX", k1);
|
|
||||||
|
|
||||||
int T_Key[256] = {0};
|
|
||||||
int pN[882] = {0};
|
|
||||||
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
if (k2 == 0)
|
|
||||||
{
|
|
||||||
len = 39;
|
|
||||||
k1 = k1 << 24;
|
|
||||||
}
|
|
||||||
if (k2 != 0)
|
|
||||||
{
|
|
||||||
len = 127;
|
|
||||||
fprintf(stderr, " %016llX", k2);
|
|
||||||
}
|
|
||||||
if (k4 != 0)
|
|
||||||
{
|
|
||||||
len = 255;
|
|
||||||
fprintf(stderr, " %016llX", k3);
|
|
||||||
fprintf(stderr, " %016llX", k4);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
|
|
||||||
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
|
|
||||||
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
|
|
||||||
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 882; i++)
|
|
||||||
{
|
|
||||||
pN[i] = T_Key[pos];
|
|
||||||
pos++;
|
|
||||||
if (pos > len)
|
|
||||||
{
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = 0;
|
|
||||||
for(Frame = 0; Frame < 6; Frame++)
|
|
||||||
{
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
for(j = 0; j < 49; j++)
|
|
||||||
{
|
|
||||||
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= pN[pos];
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
for(Frame = 0; Frame < 6; Frame++) //6
|
|
||||||
{
|
|
||||||
/* 1 DMR frame contains 3 AMBE voice samples */
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
errs = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]);
|
|
||||||
errs2 = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]);
|
|
||||||
state->errs = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement
|
|
||||||
state->errs2 = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement
|
|
||||||
|
|
||||||
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str,
|
|
||||||
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i],
|
|
||||||
state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
PrintAMBEData(opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//quick enc toggle to determine whether or not to play enc traffic
|
|
||||||
int enc_bit = 0;
|
|
||||||
enc_bit = (state->dmr_so >> 6) & 0x1;
|
|
||||||
if (enc_bit == 1)
|
|
||||||
{
|
|
||||||
state->dmr_encL = 1;
|
|
||||||
}
|
|
||||||
else state->dmr_encL = 0;
|
|
||||||
|
|
||||||
if (state->K != 0 || state->K1 != 0)
|
|
||||||
{
|
|
||||||
state->dmr_encL = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//quick test to determine whether or not to run the next few steps below
|
|
||||||
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f != NULL)
|
|
||||||
{
|
|
||||||
saveAmbe2450Data (opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
state->debug_audio_errors += *errs2;
|
|
||||||
|
|
||||||
processAudio(opts, state);
|
|
||||||
|
|
||||||
if (opts->wav_out_f != NULL)
|
|
||||||
{
|
|
||||||
writeSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->audio_out == 1) //opts->audio_out == 1
|
|
||||||
{
|
|
||||||
playSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
} //second bracket for the test enc condition
|
|
||||||
} /* End for(i = 0; i < 3; i++) */
|
|
||||||
} /* End for(Frame = 0; Frame < 6; Frame++) */
|
|
||||||
} //End if Current Slot == 0
|
|
||||||
|
|
||||||
if (state->currentslot == 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
for(Frame = 0; Frame < 6; Frame++)
|
|
||||||
{
|
|
||||||
/* 1 DMR frame contains 3 AMBE voice samples */
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
errs = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]);
|
|
||||||
errs2 = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]);
|
|
||||||
state->errs = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement
|
|
||||||
state->errs2 = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement
|
|
||||||
|
|
||||||
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str,
|
|
||||||
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i],
|
|
||||||
state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
|
|
||||||
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
PrintAMBEData(opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//quick enc toggle to determine whether or not to play enc traffic
|
|
||||||
int enc_bit = 0;
|
|
||||||
enc_bit = (state->dmr_soR >> 6) & 0x1;
|
|
||||||
if (enc_bit == 1)
|
|
||||||
{
|
|
||||||
state->dmr_encR = 1;
|
|
||||||
}
|
|
||||||
else state->dmr_encR = 0;
|
|
||||||
|
|
||||||
if (state->K != 0 || state->K1 != 0)
|
|
||||||
{
|
|
||||||
state->dmr_encR = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//quick test to determine whether or not to run the next few steps below
|
|
||||||
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f != NULL)
|
|
||||||
{
|
|
||||||
saveAmbe2450Data (opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
state->debug_audio_errors += *errs2;
|
|
||||||
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
processAudio(opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->wav_out_f != NULL)
|
|
||||||
{
|
|
||||||
writeSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
playSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} /* End for(i = 0; i < 3; i++) */
|
|
||||||
} /* End for(Frame = 0; Frame < 6; Frame++) */
|
|
||||||
} //End if Current Slot == 1
|
|
||||||
|
|
||||||
|
|
||||||
} /* End ProcessDMR */
|
|
||||||
/* End of file */
|
|
||||||
2922
src/dmr_sync.c
2922
src/dmr_sync.c
File diff suppressed because it is too large
Load Diff
440
src/dmr_utils.c
Normal file
440
src/dmr_utils.c
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
//DMR CRC/Utility Functions
|
||||||
|
//Original File - dmr_sync.c
|
||||||
|
//ConvertBitIntoBytes, ComputeCrcCCITT, ComputeCrc5Bit, ComputeAndCorrectFullLinkControlCrc, CRC32, CRC9
|
||||||
|
//Original Source - https://github.com/LouisErigHerve/dsd
|
||||||
|
|
||||||
|
//Additional Functions
|
||||||
|
//Hamming17123, crc7, crc8, crc8ok functions
|
||||||
|
//Original Souce - https://github.com/boatbod/op25
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//modified to accept variable payload size and len
|
||||||
|
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||||
|
/* Polynomial x^16 + x^12 + x^5 + 1
|
||||||
|
* Normal = 0x1021
|
||||||
|
* Reciprocal = 0x0811
|
||||||
|
* Reversed = 0x8408
|
||||||
|
* Reversed reciprocal = 0x8810 */
|
||||||
|
uint16_t Polynome = 0x1021;
|
||||||
|
for(i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if(((CRC >> 15) & 1) ^ (buf[i] & 1))
|
||||||
|
{
|
||||||
|
CRC = (CRC << 1) ^ Polynome;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRC <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invert the CRC */
|
||||||
|
CRC ^= 0xFFFF;
|
||||||
|
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrcCCITTd() */
|
||||||
|
|
||||||
|
// A Hamming (17,12,3) Check for completed SLC message
|
||||||
|
bool Hamming17123(uint8_t* d)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Calculate the checksum this column should have
|
||||||
|
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
|
||||||
|
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
|
||||||
|
bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
|
||||||
|
bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
|
||||||
|
bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
|
||||||
|
|
||||||
|
// Compare these with the actual bits
|
||||||
|
unsigned char n = 0x00U;
|
||||||
|
n |= (c0 != d[12]) ? 0x01U : 0x00U;
|
||||||
|
n |= (c1 != d[13]) ? 0x02U : 0x00U;
|
||||||
|
n |= (c2 != d[14]) ? 0x04U : 0x00U;
|
||||||
|
n |= (c3 != d[15]) ? 0x08U : 0x00U;
|
||||||
|
n |= (c4 != d[16]) ? 0x10U : 0x00U;
|
||||||
|
|
||||||
|
switch (n) {
|
||||||
|
// Parity bit errors
|
||||||
|
case 0x01U: d[12] = !d[12]; return true;
|
||||||
|
case 0x02U: d[13] = !d[13]; return true;
|
||||||
|
case 0x04U: d[14] = !d[14]; return true;
|
||||||
|
case 0x08U: d[15] = !d[15]; return true;
|
||||||
|
case 0x10U: d[16] = !d[16]; return true;
|
||||||
|
|
||||||
|
// Data bit errors
|
||||||
|
case 0x1BU: d[0] = !d[0]; return true;
|
||||||
|
case 0x1FU: d[1] = !d[1]; return true;
|
||||||
|
case 0x17U: d[2] = !d[2]; return true;
|
||||||
|
case 0x07U: d[3] = !d[3]; return true;
|
||||||
|
case 0x0EU: d[4] = !d[4]; return true;
|
||||||
|
case 0x1CU: d[5] = !d[5]; return true;
|
||||||
|
case 0x11U: d[6] = !d[6]; return true;
|
||||||
|
case 0x0BU: d[7] = !d[7]; return true;
|
||||||
|
case 0x16U: d[8] = !d[8]; return true;
|
||||||
|
case 0x05U: d[9] = !d[9]; return true;
|
||||||
|
case 0x0AU: d[10] = !d[10]; return true;
|
||||||
|
case 0x14U: d[11] = !d[11]; return true;
|
||||||
|
|
||||||
|
// No bit errors
|
||||||
|
case 0x00U: return true;
|
||||||
|
|
||||||
|
// Unrecoverable errors
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crc8(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint8_t crc=0;
|
||||||
|
unsigned int K = 8;
|
||||||
|
uint8_t poly[9] = {1,0,0,0,0,0,1,1,1}; // crc8 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool crc8_ok(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint16_t crc = 0;
|
||||||
|
for (unsigned int i=0; i < 8; i++) {
|
||||||
|
crc = (crc << 1) + bits[len+i];
|
||||||
|
}
|
||||||
|
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
|
||||||
|
* by using the polynomial x^16 + x^12 + x^5 + 1
|
||||||
|
*
|
||||||
|
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
||||||
|
*
|
||||||
|
* @return The 16 bit CRC
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint16_t ComputeCrcCCITT(uint8_t * DMRData)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||||
|
/* Polynomial x^16 + x^12 + x^5 + 1
|
||||||
|
* Normal = 0x1021
|
||||||
|
* Reciprocal = 0x0811
|
||||||
|
* Reversed = 0x8408
|
||||||
|
* Reversed reciprocal = 0x8810 */
|
||||||
|
uint16_t Polynome = 0x1021;
|
||||||
|
for(i = 0; i < 80; i++)
|
||||||
|
{
|
||||||
|
if(((CRC >> 15) & 1) ^ (DMRData[i] & 1))
|
||||||
|
{
|
||||||
|
CRC = (CRC << 1) ^ Polynome;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRC <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invert the CRC */
|
||||||
|
CRC ^= 0xFFFF;
|
||||||
|
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrcCCITT() */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the CRC-24 bit of the full
|
||||||
|
* link control by using the Reed-Solomon(12,9) FEC
|
||||||
|
*
|
||||||
|
* @param FullLinkControlDataBytes : A buffer pointer of the DMR data bytes (12 bytes)
|
||||||
|
*
|
||||||
|
* @param CRCComputed : A 32 bit pointer where the computed CRC 24-bit will be stored
|
||||||
|
*
|
||||||
|
* @param CRCMask : The 24 bit CRC mask to apply
|
||||||
|
*
|
||||||
|
* @return 0 = CRC error
|
||||||
|
* 1 = CRC is correct
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
rs_12_9_codeword_t VoiceLCHeaderStr;
|
||||||
|
rs_12_9_poly_t syndrome;
|
||||||
|
uint8_t errors_found = 0;
|
||||||
|
rs_12_9_correct_errors_result_t result = RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND;
|
||||||
|
uint32_t CrcIsCorrect = 0;
|
||||||
|
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
VoiceLCHeaderStr.data[i] = FullLinkControlDataBytes[i];
|
||||||
|
|
||||||
|
/* Apply CRC mask on each 3 last bytes
|
||||||
|
* of the full link control */
|
||||||
|
if(i == 9)
|
||||||
|
{
|
||||||
|
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 16);
|
||||||
|
}
|
||||||
|
else if(i == 10)
|
||||||
|
{
|
||||||
|
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 8);
|
||||||
|
}
|
||||||
|
else if(i == 11)
|
||||||
|
{
|
||||||
|
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check and correct the full link LC control with Reed Solomon (12,9) FEC */
|
||||||
|
rs_12_9_calc_syndrome(&VoiceLCHeaderStr, &syndrome);
|
||||||
|
if(rs_12_9_check_syndrome(&syndrome) != 0) result = rs_12_9_correct_errors(&VoiceLCHeaderStr, &syndrome, &errors_found);
|
||||||
|
|
||||||
|
/* Reconstitue the CRC */
|
||||||
|
*CRCComputed = (uint32_t)((VoiceLCHeaderStr.data[9] << 16) & 0xFF0000);
|
||||||
|
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[10] << 8) & 0x00FF00);
|
||||||
|
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[11] << 0) & 0x0000FF);
|
||||||
|
|
||||||
|
if((result == RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND) ||
|
||||||
|
(result == RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED))
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "CRC OK : 0x%06X\n", *CRCComputed);
|
||||||
|
CrcIsCorrect = 1;
|
||||||
|
|
||||||
|
/* Reconstitue full link control data after FEC correction */
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
FullLinkControlDataBytes[i] = VoiceLCHeaderStr.data[i];
|
||||||
|
|
||||||
|
/* Apply CRC mask on each 3 last bytes
|
||||||
|
* of the full link control */
|
||||||
|
if(i == 9)
|
||||||
|
{
|
||||||
|
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 16);
|
||||||
|
}
|
||||||
|
else if(i == 10)
|
||||||
|
{
|
||||||
|
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 8);
|
||||||
|
}
|
||||||
|
else if(i == 11)
|
||||||
|
{
|
||||||
|
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "CRC ERROR : 0x%06X\n", *CRCComputed);
|
||||||
|
CrcIsCorrect = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the CRC status */
|
||||||
|
return CrcIsCorrect;
|
||||||
|
} /* End ComputeAndCorrectFullLinkControlCrc() */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the 5 bit CRC of the DMR voice burst data
|
||||||
|
* See ETSI TS 102 361-1 chapter B.3.11
|
||||||
|
*
|
||||||
|
* @param Input : A buffer pointer of the DMR data (72 bytes)
|
||||||
|
*
|
||||||
|
* @return The 5 bit CRC
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t ComputeCrc5Bit(uint8_t * DMRData)
|
||||||
|
{
|
||||||
|
uint32_t i, j, k;
|
||||||
|
uint8_t Buffer[9];
|
||||||
|
uint32_t Sum;
|
||||||
|
uint8_t CRC = 0;
|
||||||
|
|
||||||
|
/* Convert the 72 bit into 9 bytes */
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
Buffer[i] = 0;
|
||||||
|
for(j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
Buffer[i] = Buffer[i] << 1;
|
||||||
|
Buffer[i] = Buffer[i] | DMRData[k++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add all 9 bytes */
|
||||||
|
Sum = 0;
|
||||||
|
for(i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
Sum += (uint32_t)Buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sum MOD 31 = CRC */
|
||||||
|
CRC = (uint8_t)(Sum % 31);
|
||||||
|
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrc5Bit() */
|
||||||
|
|
||||||
|
uint64_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
||||||
|
{
|
||||||
|
uint64_t Output = 0;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for(i = 0; i < BitLength; i++)
|
||||||
|
{
|
||||||
|
Output <<= 1;
|
||||||
|
Output |= (uint64_t)(BufferIn[i] & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
} /* End ConvertBitIntoBytes() */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the CRC-9 of the DMR data
|
||||||
|
* by using the polynomial x^9 + x^6 + x^4 + x^3 + 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 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() */
|
||||||
660
src/dmr_voice.c
660
src/dmr_voice.c
@@ -1,660 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* 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
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include "dmr_const.h"
|
|
||||||
|
|
||||||
void processDMRvoice (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
// extracts AMBE frames from DMR frame
|
|
||||||
int i, j, k, l, dibit;
|
|
||||||
int *dibit_p;
|
|
||||||
char ambe_fr[4][24] = {0};
|
|
||||||
char ambe_fr2[4][24] = {0};
|
|
||||||
char ambe_fr3[4][24] = {0};
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
char sync[25];
|
|
||||||
char syncdata[48];
|
|
||||||
char cachdata[13] = {0};
|
|
||||||
int mutecurrentslot;
|
|
||||||
int msMode;
|
|
||||||
char cc[4];
|
|
||||||
unsigned char EmbeddedSignalling[16];
|
|
||||||
unsigned int EmbeddedSignallingOk;
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
int k;
|
|
||||||
char syncbits[49];
|
|
||||||
char cachbits[25];12
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Init the superframe buffers */
|
|
||||||
memset(&state->TS1SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t));
|
|
||||||
memset(&state->TS2SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t));
|
|
||||||
|
|
||||||
/* Init the color code status */
|
|
||||||
state->color_code_ok = 0;
|
|
||||||
|
|
||||||
mutecurrentslot = 0;
|
|
||||||
msMode = 0;
|
|
||||||
|
|
||||||
dibit_p = state->dibit_buf_p - 144;
|
|
||||||
for(j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
// 2nd half of previous slot
|
|
||||||
for(i = 0; i < 54; i++)
|
|
||||||
{
|
|
||||||
if(j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if(opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CACH
|
|
||||||
for(i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
if(j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if(opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cachdata[i] = dibit;
|
|
||||||
if(i == 2)
|
|
||||||
{
|
|
||||||
/* Change the current slot only if we are in relayed mode (not in direct mode) */
|
|
||||||
if(state->directmode == 0) state->currentslot = (1 & (dibit >> 1)); // bit 1
|
|
||||||
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->slot1light[0] = '[';
|
|
||||||
state->slot1light[6] = ']';
|
|
||||||
state->slot2light[0] = ' ';
|
|
||||||
state->slot2light[6] = ' ';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->slot2light[0] = '[';
|
|
||||||
state->slot2light[6] = ']';
|
|
||||||
state->slot1light[0] = ' ';
|
|
||||||
state->slot1light[6] = ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cachdata[12] = 0;
|
|
||||||
|
|
||||||
//set state-dmrburstL and R here to a value for ncurses
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->dmrburstL = 16; //use 16 for Voice?
|
|
||||||
}
|
|
||||||
if (state->currentslot == 1)
|
|
||||||
{
|
|
||||||
state->dmrburstR = 16; //Use 16 for Voice?
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = cachdata[i];
|
|
||||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
cachbits[24] = 0;
|
|
||||||
fprintf(stderr, "%s ", cachbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// current slot frame 1
|
|
||||||
w = rW;
|
|
||||||
x = rX;
|
|
||||||
y = rY;
|
|
||||||
z = rZ;
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 36; i++)
|
|
||||||
{
|
|
||||||
if(j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if(opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Save the current frame in the appropriate superframe buffer */
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
/* Time slot 1 superframe buffer filling => RAW voice bit */
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
|
|
||||||
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Time slot 2 superframe buffer filling => RAW voice bit */
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
|
|
||||||
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// current slot frame 2 first half
|
|
||||||
w = rW;
|
|
||||||
x = rX;
|
|
||||||
y = rY;
|
|
||||||
z = rZ;
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
if(j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if(opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Save the current frame in the appropriate superframe buffer */
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
/* Time slot 1 superframe buffer filling => RAW voice bit */
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
|
|
||||||
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Time slot 2 superframe buffer filling => RAW voice bit */
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
|
|
||||||
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
|
|
||||||
l = 0;
|
|
||||||
// signaling data or sync
|
|
||||||
for(i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
if(j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if(opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
|
||||||
sync[i] = (dibit | 1) + 48;
|
|
||||||
|
|
||||||
/* Save the current frame in the appropriate superframe buffer */
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
/* Time slot 1 superframe buffer filling => SYNC data */
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Time slot 2 superframe buffer filling => SYNC data */
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sync[24] = 0;
|
|
||||||
|
|
||||||
/* Copy the Embedded signaling (1st part) */
|
|
||||||
for(i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
EmbeddedSignalling[i] = syncdata[i];
|
|
||||||
}
|
|
||||||
/* Copy the Embedded signaling (2nd part) */
|
|
||||||
for(i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
EmbeddedSignalling[i + 8] = syncdata[i + 40];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
|
|
||||||
if(QR_16_7_6_decode(EmbeddedSignalling)) EmbeddedSignallingOk = 1;
|
|
||||||
else EmbeddedSignallingOk = 0;
|
|
||||||
|
|
||||||
/* Get the color code */
|
|
||||||
cc[0] = EmbeddedSignalling[0];
|
|
||||||
cc[1] = EmbeddedSignalling[1];
|
|
||||||
cc[2] = EmbeddedSignalling[2];
|
|
||||||
cc[3] = EmbeddedSignalling[3];
|
|
||||||
|
|
||||||
/* The color code is normally the same for all
|
|
||||||
* 6 received voice frame, so in case of error
|
|
||||||
* do not store it except if the last SYNC frame
|
|
||||||
* has been received. */
|
|
||||||
if(EmbeddedSignallingOk || ((state->color_code_ok == 0) && (j == 5)))
|
|
||||||
{
|
|
||||||
/* Save the color code */
|
|
||||||
state->color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]);
|
|
||||||
state->color_code_ok = EmbeddedSignallingOk;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save the Power Control (PI) indicator */
|
|
||||||
state->PI = (unsigned int)EmbeddedSignalling[4];
|
|
||||||
state->PI_ok = EmbeddedSignallingOk;
|
|
||||||
|
|
||||||
/* Save the Link Control Start Stop (LCSS) indicator */
|
|
||||||
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
|
||||||
state->LCSS_ok = EmbeddedSignallingOk;
|
|
||||||
|
|
||||||
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
|
||||||
{
|
|
||||||
mutecurrentslot = 1;
|
|
||||||
state->directmode = 0;
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->slot1light, "[slot1]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf(state->slot2light, "[slot2]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
|
|
||||||
{
|
|
||||||
mutecurrentslot = 1;
|
|
||||||
state->currentslot = 0;
|
|
||||||
state->directmode = 1; /* Direct mode */
|
|
||||||
sprintf(state->slot1light, "[sLoT1]");
|
|
||||||
}
|
|
||||||
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
|
||||||
{
|
|
||||||
mutecurrentslot = 1;
|
|
||||||
state->currentslot = 1;
|
|
||||||
state->directmode = 1; /* Direct mode */
|
|
||||||
sprintf(state->slot1light, "[sLoT2]");
|
|
||||||
}
|
|
||||||
else if((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) ) //|| (strcmp (sync, DMR_MS_VOICE_SYNC) == 0))
|
|
||||||
{
|
|
||||||
mutecurrentslot = 0;
|
|
||||||
state->directmode = 0;
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->slot1light, "[SLOT1]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf(state->slot2light, "[SLOT2]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
mutecurrentslot = 0;
|
|
||||||
state->currentslot = 0;
|
|
||||||
state->directmode = 1; /* Direct mode */
|
|
||||||
sprintf(state->slot1light, "[sLoT1]");
|
|
||||||
}
|
|
||||||
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
mutecurrentslot = 0;
|
|
||||||
state->currentslot = 1;
|
|
||||||
state->directmode = 1; /* Direct mode */
|
|
||||||
sprintf(state->slot2light, "[sLoT2]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
dibit = syncdata[i];
|
|
||||||
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
syncbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
syncbits[48] = 0;
|
|
||||||
fprintf(stderr, "%s ", syncbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// current slot frame 2 second half
|
|
||||||
for(i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Save the current frame in the appropriate superframe buffer */
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
/* Time slot 1 superframe buffer filling => RAW voice bit */
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
|
|
||||||
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Time slot 2 superframe buffer filling */
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
|
|
||||||
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mutecurrentslot == 0)
|
|
||||||
{
|
|
||||||
if(state->firstframe == 1)
|
|
||||||
{ // we don't know if anything received before the first sync after no carrier is valid
|
|
||||||
state->firstframe = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
|
||||||
//processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// current slot frame 3
|
|
||||||
w = rW;
|
|
||||||
x = rX;
|
|
||||||
y = rY;
|
|
||||||
z = rZ;
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 36; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Save the current frame in the appropriate superframe buffer */
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
/* Time slot 1 superframe buffer filling => RAW voice bit */
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
|
|
||||||
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Time slot 2 superframe buffer filling => RAW voice bit */
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
|
|
||||||
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
if(mutecurrentslot == 0)
|
|
||||||
{
|
|
||||||
//processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CACH
|
|
||||||
for(i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
cachdata[i] = dibit;
|
|
||||||
}
|
|
||||||
cachdata[12] = 0;
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = cachdata[i];
|
|
||||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
cachbits[24] = 0;
|
|
||||||
fprintf(stderr, "%s ", cachbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// next slot
|
|
||||||
skipDibit (opts, state, 54);
|
|
||||||
|
|
||||||
// signaling data or sync
|
|
||||||
for(i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
syncdata[i] = dibit;
|
|
||||||
sync[i] = (dibit | 1) + 48;
|
|
||||||
}
|
|
||||||
sync[24] = 0;
|
|
||||||
syncdata[24] = 0;
|
|
||||||
|
|
||||||
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (msMode == 1))
|
|
||||||
{
|
|
||||||
state->directmode = 0;
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->slot2light, " slot2 ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf(state->slot1light, " slot1 ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->directmode = 0;
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf(state->slot2light, " SLOT2 ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf(state->slot1light, " SLOT1 ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if((strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0))
|
|
||||||
{
|
|
||||||
state->currentslot = 0;
|
|
||||||
state->directmode = 1; /* Direct mode */
|
|
||||||
sprintf(state->slot1light, " sLoT1 ");
|
|
||||||
}
|
|
||||||
else if((strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0))
|
|
||||||
{
|
|
||||||
state->currentslot = 1;
|
|
||||||
state->directmode = 1; /* Direct mode */
|
|
||||||
sprintf(state->slot1light, " sLoT2 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
dibit = syncdata[i];
|
|
||||||
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
syncbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
syncbits[48] = 0;
|
|
||||||
fprintf(stderr, "%s ", syncbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(j == 5) //j == 5
|
|
||||||
{
|
|
||||||
|
|
||||||
// 2nd half next slot
|
|
||||||
skipDibit (opts, state, 54);
|
|
||||||
|
|
||||||
// CACH
|
|
||||||
skipDibit (opts, state, 12);
|
|
||||||
|
|
||||||
// first half current slot
|
|
||||||
skipDibit (opts, state, 54);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract the 49 bits AMBE of the voice frame */
|
|
||||||
if(state->currentslot == 0)
|
|
||||||
{
|
|
||||||
/* 3 x 49 bit of AMBE sample per voice frame => Slot 1buffer here */
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
|
|
||||||
|
|
||||||
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i];
|
|
||||||
|
|
||||||
mbe_demodulateAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
|
|
||||||
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i],
|
|
||||||
&(state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* 3 x 49 bit of AMBE sample per voice frame => Slot 2 buffer here */
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
|
|
||||||
|
|
||||||
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i];
|
|
||||||
|
|
||||||
mbe_demodulateAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
|
|
||||||
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i],
|
|
||||||
&(state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* End for(j = 0; j < 6; j++) */
|
|
||||||
|
|
||||||
if(opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
|
||||||
|
|
||||||
/* Print the color code */
|
|
||||||
fprintf (stderr, "%s", KCYN);
|
|
||||||
fprintf(stderr, "| Color Code=%02d ", (int)state->color_code);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
//if(state->color_code_ok) fprintf(stderr, "(OK) |");
|
|
||||||
if(state->color_code_ok)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "| (CRC OK ) |"); //add line break
|
|
||||||
state->dmr_color_code = state->color_code; //set here when voice? otherwise is set off of idle.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf(stderr, "| (CRC ERR) |"); //add line break
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, " VOICE ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform the SYNC DMR data embedded decoding */
|
|
||||||
ProcessVoiceBurstSync(opts, state);
|
|
||||||
|
|
||||||
/* Perform the DMR voice decoding */
|
|
||||||
ProcessDMR(opts, state);
|
|
||||||
|
|
||||||
//LFSR if required, change condition?
|
|
||||||
if ( (state->currentslot == 0 && state->payload_algid == 0x21 ) || //&& opts->payload == 1
|
|
||||||
(state->currentslot == 1 && state->payload_algidR == 0x21 ) ) //&& opts->payload == 1
|
|
||||||
{
|
|
||||||
LFSR(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* End processDMRvoice() */
|
|
||||||
259
src/dpmr_voice.c
259
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,18 +175,10 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
x++;
|
x++;
|
||||||
y++;
|
y++;
|
||||||
z++;
|
z++;
|
||||||
if (j == 3 && i == 28) //find sweet spot to cut off early
|
|
||||||
{
|
|
||||||
i = 40; //break early so FS2 sync can resync
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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++)
|
||||||
{
|
{
|
||||||
@@ -209,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;
|
||||||
@@ -239,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);
|
||||||
@@ -302,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;
|
||||||
|
|
||||||
@@ -368,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;
|
||||||
|
|
||||||
@@ -394,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])))
|
||||||
{
|
{
|
||||||
@@ -422,133 +356,144 @@ 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;
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
}
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
//Display the CCH content
|
//Display the CCH content
|
||||||
|
|||||||
202
src/dsd_audio.c
202
src/dsd_audio.c
@@ -92,11 +92,7 @@ processAudio (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
// detect max level
|
// detect max level
|
||||||
max = 0;
|
max = 0;
|
||||||
//attempt to reduce crackle by overriding the max value when not using pulse input
|
|
||||||
if (opts->audio_in_type != 0)
|
|
||||||
{
|
|
||||||
max = 3000;
|
|
||||||
}
|
|
||||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||||
for (n = 0; n < 160; n++)
|
for (n = 0; n < 160; n++)
|
||||||
{
|
{
|
||||||
@@ -238,11 +234,7 @@ processAudioR (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
// detect max level
|
// detect max level
|
||||||
max = 0;
|
max = 0;
|
||||||
//attempt to reduce crackle by overriding the max value when not using pulse input
|
|
||||||
if (opts->audio_in_type != 0)
|
|
||||||
{
|
|
||||||
max = 3000;
|
|
||||||
}
|
|
||||||
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||||
for (n = 0; n < 160; n++)
|
for (n = 0; n < 160; n++)
|
||||||
{
|
{
|
||||||
@@ -452,17 +444,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
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -486,15 +479,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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,31 +541,63 @@ openAudioOutDevice (dsd_opts * opts, int speed)
|
|||||||
void
|
void
|
||||||
openAudioInDevice (dsd_opts * opts)
|
openAudioInDevice (dsd_opts * opts)
|
||||||
{
|
{
|
||||||
// get info of device/file
|
char * extension;
|
||||||
if(strncmp(opts->audio_in_dev, "-", 1) == 0)
|
const char ch = '.';
|
||||||
{
|
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
||||||
opts->audio_in_type = 1;
|
|
||||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
|
||||||
opts->audio_in_file_info->samplerate=48000;
|
|
||||||
opts->pulse_digi_rate_out = 8000; //set out rate to 8000 for stdin input
|
|
||||||
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_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0);
|
|
||||||
|
|
||||||
if(opts->audio_in_file == NULL) {
|
//if no extension set, give default of .wav -- bugfix for github issue #105
|
||||||
fprintf (stderr,"Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL));
|
// if (extension == NULL) extension = ".wav";
|
||||||
exit(1); //had this one disabled, re-enabling it now
|
|
||||||
}
|
// get info of device/file
|
||||||
}
|
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
||||||
//converted to handle any calls to use portaudio
|
|
||||||
else if(strncmp(opts->audio_in_dev, "pa:", 2) == 0)
|
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 0;
|
opts->audio_in_type = 1;
|
||||||
fprintf (stderr,"Error, Port Audio is not supported by FME!\n");
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
fprintf (stderr,"Using Pulse Audio Input Stream Instead! \n");
|
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||||
sprintf (opts->audio_in_dev, "pulse");
|
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_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
|
||||||
|
if(opts->audio_in_file == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 8;
|
||||||
|
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 open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 6;
|
||||||
|
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->udp_file_in = sf_open_fd(opts->udp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
|
||||||
|
if(opts->udp_file_in == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't open UDP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 3;
|
opts->audio_in_type = 3;
|
||||||
@@ -578,41 +606,83 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
{
|
{
|
||||||
opts->audio_in_type = 0;
|
opts->audio_in_type = 0;
|
||||||
}
|
}
|
||||||
//convert from opening wav files to OP25 symbol capture files since opening wav files is busted
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strncmp(extension, ".bin", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//open as wav file as last resort, wav files subseptible to sample rate issues if not 48000
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct stat stat_buf;
|
struct stat stat_buf;
|
||||||
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev);
|
fprintf (stderr,"Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (S_ISREG(stat_buf.st_mode))
|
if (S_ISREG(stat_buf.st_mode))
|
||||||
{
|
{
|
||||||
// is this a regular file? then process with libsndfile.
|
opts->audio_in_type = 2; //two now, seperating STDIN and wav files
|
||||||
//opts->pulse_digi_rate_out = 8000; //this for wav files input?
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
// opts->audio_in_type = 1;
|
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||||
// opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
opts->audio_in_file_info->channels=1;
|
||||||
// opts->audio_in_file_info->channels = 1;
|
opts->audio_in_file_info->channels = 1;
|
||||||
// opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
opts->audio_in_file_info->seekable=0;
|
||||||
|
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||||
//
|
//
|
||||||
// if(opts->audio_in_file == NULL)
|
opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||||
// {
|
|
||||||
// fprintf (stderr,"Error, couldn't open file %s\n", opts->audio_in_dev);
|
if(opts->audio_in_file == NULL)
|
||||||
// exit(1);
|
{
|
||||||
// }
|
fprintf(stderr, "Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
|
||||||
opts->audio_in_type = 4; //symbol capture bin files
|
|
||||||
}
|
}
|
||||||
else
|
//open pulse audio if no bin or wav
|
||||||
|
else //seems this condition is never met
|
||||||
{
|
{
|
||||||
// this is a device, use old handling, pulse audio now
|
//opts->audio_in_type = 5; //not sure if this works or needs to openPulse here
|
||||||
opts->audio_in_type = 0;
|
fprintf(stderr, "Error, couldn't open input file.\n");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->split == 1)
|
if (opts->split == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Audio In Device: %s\n", opts->audio_in_dev);
|
fprintf (stderr,"Audio In Device: %s\n", opts->audio_in_dev);
|
||||||
|
|||||||
@@ -221,12 +221,13 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
//int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
//int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||||
{
|
{
|
||||||
// determine dibit state
|
// determine dibit state
|
||||||
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18))
|
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37))
|
||||||
|
|
||||||
{
|
{
|
||||||
// 6 +D-STAR
|
// 6 +D-STAR
|
||||||
// 14 +ProVoice
|
// 14 +ProVoice
|
||||||
// 18 +D-STAR_HD
|
// 18 +D-STAR_HD
|
||||||
|
// 37 +EDACS
|
||||||
|
|
||||||
if (symbol > state->center)
|
if (symbol > state->center)
|
||||||
{
|
{
|
||||||
@@ -241,11 +242,12 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
return (1); // +3
|
return (1); // +3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19))
|
else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19) || (state->synctype == 38))
|
||||||
{
|
{
|
||||||
// 7 -D-STAR
|
// 7 -D-STAR
|
||||||
// 15 -ProVoice
|
// 15 -ProVoice
|
||||||
// 19 -D-STAR_HD
|
// 19 -D-STAR_HD
|
||||||
|
// 38 -EDACS
|
||||||
|
|
||||||
if (symbol > state->center)
|
if (symbol > state->center)
|
||||||
{
|
{
|
||||||
@@ -263,7 +265,7 @@ 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 == 36) )
|
(state->synctype == 17) || (state->synctype == 29) || (state->synctype == 36) )
|
||||||
|
|
||||||
{
|
{
|
||||||
// 1 -P25p1
|
// 1 -P25p1
|
||||||
@@ -273,16 +275,19 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
// 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 -NXDN (inverted data frame)
|
||||||
|
// 29 -NXDN (inverted FSW)
|
||||||
|
// 36 -P25p2
|
||||||
|
|
||||||
int valid;
|
int valid;
|
||||||
int dibit;
|
int dibit;
|
||||||
|
|
||||||
valid = 0;
|
valid = 0;
|
||||||
|
|
||||||
if (state->synctype == 1)
|
//disabling again, causing issues with trunking P25
|
||||||
|
if (state->synctype == 1 && opts->p25_trunk == 1)
|
||||||
{
|
{
|
||||||
// Use the P25 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)
|
||||||
@@ -334,16 +339,19 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
// 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 +NXDN (non inverted data frame)
|
||||||
|
// 28 +NXND (FSW)
|
||||||
|
// 35 +p25p2
|
||||||
|
|
||||||
int valid;
|
int valid;
|
||||||
int dibit;
|
int dibit;
|
||||||
|
|
||||||
valid = 0;
|
valid = 0;
|
||||||
|
|
||||||
if (state->synctype == 0)
|
//disabling again, causing issues with trunking P25
|
||||||
|
if (state->synctype == 0 && opts->p25_trunk == 1)
|
||||||
{
|
{
|
||||||
// Use the P25 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)
|
||||||
|
|||||||
375
src/dsd_file.c
375
src/dsd_file.c
@@ -34,7 +34,7 @@ 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
|
char datename[80]; //32-bit needs this to be 80
|
||||||
char * curr2;
|
char * curr2;
|
||||||
struct tm * to;
|
struct tm * to;
|
||||||
time_t t;
|
time_t t;
|
||||||
@@ -45,8 +45,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 +55,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 +80,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 +157,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) //For DMR Stereo, may use this for NXDN too
|
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
@@ -174,7 +165,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
|
|||||||
|
|
||||||
err = (unsigned char) state->errs2;
|
err = (unsigned char) state->errs2;
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->dmr_stereo == 0)
|
if (opts->dmr_stereo == 0 && opts->dmr_mono == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
@@ -183,7 +174,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
|
|||||||
fprintf(stderr, " AMBE ");
|
fprintf(stderr, " AMBE ");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 7; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
|
for (i = 0; i < 7; i++)
|
||||||
{
|
{
|
||||||
b = 0;
|
b = 0;
|
||||||
for (j = 0; j < 8; j++)
|
for (j = 0; j < 8; j++)
|
||||||
@@ -192,13 +183,13 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
|
|||||||
b = b + ambe_d[k];
|
b = b + ambe_d[k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
|
if (opts->payload == 1 && i < 6)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", b);
|
fprintf (stderr, "%02X", b);
|
||||||
}
|
}
|
||||||
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit, value will be either 0x00 or 0x80?
|
if (opts->payload == 1 && i == 6)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", b & 0x80); //7th octet should only contain 1 bit
|
fprintf (stderr, "%02X", b & 0x80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
@@ -207,7 +198,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
|
|||||||
}
|
}
|
||||||
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
|
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
|
||||||
b = ambe_d[48];
|
b = ambe_d[48];
|
||||||
if (opts->dmr_stereo == 1) //need to fix the printouts again
|
if (opts->dmr_stereo == 1 || opts->dmr_mono == 1) //need to fix the printouts again
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
@@ -226,7 +217,7 @@ readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|||||||
|
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
@@ -248,12 +239,12 @@ readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|||||||
b = b & 255;
|
b = b & 255;
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
//new printout on reading MBE files back in
|
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", x);
|
fprintf (stderr, "%02X", x);
|
||||||
}
|
}
|
||||||
//
|
|
||||||
}
|
}
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
@@ -273,11 +264,11 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
state->errs = state->errs2;
|
state->errs = state->errs2;
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
//for (i = 0; i < 6; i++)
|
|
||||||
for (i = 0; i < 6; i++) //breaks backwards compatablilty with 6 files
|
for (i = 0; i < 6; i++) //breaks backwards compatablilty with 6 files
|
||||||
{
|
{
|
||||||
b = fgetc (opts->mbe_in_f);
|
b = fgetc (opts->mbe_in_f);
|
||||||
@@ -296,13 +287,13 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
b = b & 255;
|
b = b & 255;
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
|
if (opts->payload == 1 && i < 6)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", x);
|
fprintf (stderr, "%02X", x);
|
||||||
}
|
}
|
||||||
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit? value will be either 0x00 or 0x80?
|
if (opts->payload == 1 && i == 6)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", x & 0x80); //7th octet should only contain 1 bit?
|
fprintf (stderr, "%02X", x & 0x80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
@@ -333,14 +324,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,83 +347,7 @@ openMbeInFile (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//temporarily disabled until the compiler warnings can be fixed, use symbol capture or per call instead!
|
//slot 1
|
||||||
//probably the easiest thing is to just have this run fclose, and have name assignment on open instead.
|
|
||||||
// void
|
|
||||||
// closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// char shell[255], newfilename[64], ext[5], datestr[32], new_path[1024];
|
|
||||||
// char tgid[17];
|
|
||||||
// int sum, i, j;
|
|
||||||
// int talkgroup;
|
|
||||||
// struct tm timep;
|
|
||||||
// int result;
|
|
||||||
//
|
|
||||||
// if (opts->mbe_out_f != NULL)
|
|
||||||
// {
|
|
||||||
// if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
|
||||||
// {
|
|
||||||
// sprintf (ext, ".imb");
|
|
||||||
// //strptime (opts->mbe_out_file, "%s.imb", &timep);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// sprintf (ext, ".amb");
|
|
||||||
// //strptime (opts->mbe_out_file, "%s.amb", &timep);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (state->tgcount > 0)
|
|
||||||
// {
|
|
||||||
// for (i = 0; i < 16; i++)
|
|
||||||
// {
|
|
||||||
// sum = 0;
|
|
||||||
// for (j = 0; j < state->tgcount; j++)
|
|
||||||
// {
|
|
||||||
// sum = sum + state->tg[j][i] - 48;
|
|
||||||
// }
|
|
||||||
// tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
|
|
||||||
// }
|
|
||||||
// tgid[16] = 0;
|
|
||||||
// talkgroup = (int) strtol (tgid, NULL, 2);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// talkgroup = 0;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fflush (opts->mbe_out_f);
|
|
||||||
// fclose (opts->mbe_out_f);
|
|
||||||
// opts->mbe_out_f = NULL;
|
|
||||||
// strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
|
|
||||||
//
|
|
||||||
// sprintf (newfilename, "%s%s", datestr, ext); //this one
|
|
||||||
//
|
|
||||||
// if (state->lastsynctype == 0 || state->lastsynctype == 1)
|
|
||||||
// {
|
|
||||||
// sprintf (newfilename, "%s-nac%X-tg%i%s", datestr, state->nac, talkgroup, ext);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// //sprintf (new_path, "%s%s", opts->mbe_out_dir, newfilename);
|
|
||||||
// #ifdef _WIN32
|
|
||||||
// //sprintf (shell, "move %s %s", opts->mbe_out_path, new_path);
|
|
||||||
// #else
|
|
||||||
// //sprintf (shell, "mv %s %s", opts->mbe_out_path, new_path);
|
|
||||||
// #endif
|
|
||||||
// //result = system (shell);
|
|
||||||
//
|
|
||||||
// state->tgcount = 0;
|
|
||||||
// for (i = 0; i < 25; i++)
|
|
||||||
// {
|
|
||||||
// for (j = 0; j < 16; j++)
|
|
||||||
// {
|
|
||||||
// state->tg[i][j] = 48;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
//much simpler version
|
|
||||||
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)
|
||||||
@@ -436,53 +358,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)
|
||||||
{
|
{
|
||||||
|
|
||||||
//struct timeval tv;
|
|
||||||
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;
|
||||||
|
|
||||||
//gettimeofday (&tv, NULL);
|
sprintf (opts->mbe_out_file, "%s %s S1%s", getDateF(), getTimeF(), ext);
|
||||||
//rewrite below line to take getTime function instead?
|
|
||||||
// sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
|
|
||||||
|
|
||||||
sprintf (opts->mbe_out_file, "%s %s%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;
|
||||||
|
|
||||||
@@ -492,15 +428,60 @@ 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;
|
||||||
info.samplerate = 8000; //8000
|
info.samplerate = 8000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
|
|
||||||
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||||
|
|
||||||
if (opts->wav_out_f == NULL)
|
if (opts->wav_out_f == NULL)
|
||||||
@@ -508,18 +489,15 @@ openWavOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// state->wav_out_bytes = 0;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
SF_INFO info;
|
SF_INFO info;
|
||||||
info.samplerate = 8000; //8000
|
info.samplerate = 8000;
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
|
|
||||||
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||||
|
|
||||||
if (opts->wav_out_f == NULL)
|
if (opts->wav_out_f == NULL)
|
||||||
@@ -527,9 +505,6 @@ void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// state->wav_out_bytes = 0;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
@@ -539,7 +514,6 @@ void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
|||||||
info.samplerate = 8000; //8000
|
info.samplerate = 8000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
|
|
||||||
opts->wav_out_fR = sf_open (opts->wav_out_fileR, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
opts->wav_out_fR = sf_open (opts->wav_out_fileR, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||||
|
|
||||||
if (opts->wav_out_f == NULL)
|
if (opts->wav_out_f == NULL)
|
||||||
@@ -547,9 +521,6 @@ void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_fileR);
|
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_fileR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// state->wav_out_bytes = 0;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
||||||
@@ -559,17 +530,12 @@ void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
|||||||
info.samplerate = 48000; //8000
|
info.samplerate = 48000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
|
|
||||||
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
|
||||||
opts->wav_out_raw = sf_open (opts->wav_out_file_raw, SFM_RDWR, &info);
|
opts->wav_out_raw = sf_open (opts->wav_out_file_raw, SFM_RDWR, &info);
|
||||||
if (opts->wav_out_raw == NULL)
|
if (opts->wav_out_raw == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
|
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// state->wav_out_bytes = 0;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
@@ -609,15 +575,12 @@ void closeSymbolOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
//Do something
|
//Do something
|
||||||
if (opts->symbol_out == 1)
|
if (opts->symbol_out == 1)
|
||||||
{
|
{
|
||||||
if (opts->symbol_out_file != NULL) //check first, or issuing a second fclose will crash the SOFTWARE
|
if (opts->symbol_out_file != NULL)
|
||||||
{
|
{
|
||||||
fclose(opts->symbol_out_f); //free(): double free detected in tcache 2 (this is a new one) happens when closing more than once
|
fclose(opts->symbol_out_f);
|
||||||
//noCarrier(opts, state); //free(): double free detected in tcache 2 (this is a new one)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts->symbol_out = 0; //set flag to 1
|
opts->symbol_out = 0; //set flag to 1
|
||||||
//sprintf (opts->symbol_out_file, "fme-capture.bin"); //use fme for the symbol bin ext
|
|
||||||
//openSymbolOutFile (opts, state);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 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];
|
||||||
@@ -21,70 +21,48 @@ float xcoeffs[] =
|
|||||||
-0.0083649323f,
|
-0.0083649323f,
|
||||||
};
|
};
|
||||||
|
|
||||||
// NXDN48 filter
|
//reverted to original NXDN filters, do better with LICH/Data handling
|
||||||
//#define NXZEROS 134
|
// NXDN48 filter - Original Version
|
||||||
//float nxgain = 15.95930463f;
|
#define NXZEROS 134
|
||||||
|
float nxgain = 15.95930463f;
|
||||||
#define NXZEROS 91 //Osmocom OP25
|
|
||||||
float nxgain = 1.0f; //Osmocom OP25
|
|
||||||
|
|
||||||
static float nxv[NXZEROS+1];
|
static float nxv[NXZEROS+1];
|
||||||
|
|
||||||
//OP25 Osmocom NXDN48 Coeffs seem to do well, will want to do some VS testing
|
|
||||||
float nxcoeffs[] =
|
float nxcoeffs[] =
|
||||||
{ 0.00203767f, 0.00491622f, 0.00748189f, 0.00934561f, 0.01017049f, 0.00972167f,
|
{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
||||||
0.00790748f, 0.00480597f, 0.00067202f, -0.00407743f, -0.00890019f, -0.01318478f,
|
+0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
||||||
-0.01632047f, -0.01777371f, -0.01716241f, -0.01431852f, -0.00933066f, -0.00255979f,
|
-0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
||||||
0.00537624f, 0.01364992f, 0.02130038f, 0.02733235f, 0.03082824f, 0.03106179f,
|
-0.045828927f, -0.052119261f, -0.056372283f, -0.058221106f,
|
||||||
0.02760061f, 0.02038545f, 0.00977572f, -0.0034462f, -0.01811514f, -0.03276675f,
|
-0.057387924f, -0.053703443f, -0.047122444f, -0.037734535f,
|
||||||
-0.04575844f, -0.05541762f, -0.06020353f, -0.0588669f, -0.05059129f, -0.03510097f,
|
-0.025769308f, -0.011595336f, +0.004287292f, +0.021260954f,
|
||||||
-0.01272309f, 0.01560398f, 0.04837926f, 0.08364189f, 0.11912012f, 0.15241359f,
|
+0.038610717f, +0.055550276f, +0.071252765f, +0.084885375f,
|
||||||
0.18119246f, 0.20339503f, 0.21740525f, 0.2221932f, 0.21740525f, 0.20339503f,
|
+0.095646450f, +0.102803611f, +0.105731303f, +0.103946126f,
|
||||||
0.18119246f, 0.15241359f, 0.11912012f, 0.08364189f, 0.04837926f, 0.01560398f,
|
+0.097138329f, +0.085197939f, +0.068234131f, +0.046586711f,
|
||||||
-0.01272309f, -0.03510097f, -0.05059129f, -0.0588669f, -0.06020353f, -0.05541762f,
|
+0.020828821f, -0.008239664f, -0.039608255f, -0.072081234f,
|
||||||
-0.04575844f, -0.03276675f, -0.01811514f, -0.0034462f, 0.00977572f, 0.02038545f,
|
-0.104311776f, -0.134843790f, -0.162160200f, -0.184736015f,
|
||||||
0.02760061f, 0.03106179f, 0.03082824f, 0.02733235f, 0.02130038f, 0.01364992f,
|
-0.201094346f, -0.209863285f, -0.209831516f, -0.200000470f,
|
||||||
0.00537624f, -0.00255979f, -0.00933066f, -0.01431852f, -0.01716241f, -0.01777371f,
|
-0.179630919f, -0.148282051f, -0.105841323f, -0.052543664f,
|
||||||
-0.01632047f, -0.01318478f, -0.00890019f, -0.00407743f, 0.00067202f, 0.00480597f,
|
+0.011020985f, +0.083912428f, +0.164857408f, +0.252278939f,
|
||||||
0.00790748f, 0.00972167f, 0.01017049f, 0.00934561f, 0.00748189f, 0.00491622f,
|
+0.344336996f, +0.438979335f, +0.534000832f, +0.627109358f,
|
||||||
0.00203767f};
|
+0.715995947f, +0.798406824f, +0.872214756f, +0.935487176f,
|
||||||
|
+0.986548646f, +1.024035395f, +1.046939951f, +1.054644241f,
|
||||||
//OLD nxCOEFFS
|
+1.046939951f, +1.024035395f, +0.986548646f, +0.935487176f,
|
||||||
// { +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
+0.872214756f, +0.798406824f, +0.715995947f, +0.627109358f,
|
||||||
// +0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
+0.534000832f, +0.438979335f, +0.344336996f, +0.252278939f,
|
||||||
// -0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
+0.164857408f, +0.083912428f, +0.011020985f, -0.052543664f,
|
||||||
// -0.045828927f, -0.052119261f, -0.056372283f, -0.058221106f,
|
-0.105841323f, -0.148282051f, -0.179630919f, -0.200000470f,
|
||||||
// -0.057387924f, -0.053703443f, -0.047122444f, -0.037734535f,
|
-0.209831516f, -0.209863285f, -0.201094346f, -0.184736015f,
|
||||||
// -0.025769308f, -0.011595336f, +0.004287292f, +0.021260954f,
|
-0.162160200f, -0.134843790f, -0.104311776f, -0.072081234f,
|
||||||
// +0.038610717f, +0.055550276f, +0.071252765f, +0.084885375f,
|
-0.039608255f, -0.008239664f, +0.020828821f, +0.046586711f,
|
||||||
// +0.095646450f, +0.102803611f, +0.105731303f, +0.103946126f,
|
+0.068234131f, +0.085197939f, +0.097138329f, +0.103946126f,
|
||||||
// +0.097138329f, +0.085197939f, +0.068234131f, +0.046586711f,
|
+0.105731303f, +0.102803611f, +0.095646450f, +0.084885375f,
|
||||||
// +0.020828821f, -0.008239664f, -0.039608255f, -0.072081234f,
|
+0.071252765f, +0.055550276f, +0.038610717f, +0.021260954f,
|
||||||
// -0.104311776f, -0.134843790f, -0.162160200f, -0.184736015f,
|
+0.004287292f, -0.011595336f, -0.025769308f, -0.037734535f,
|
||||||
// -0.201094346f, -0.209863285f, -0.209831516f, -0.200000470f,
|
-0.047122444f, -0.053703443f, -0.057387924f, -0.058221106f,
|
||||||
// -0.179630919f, -0.148282051f, -0.105841323f, -0.052543664f,
|
-0.056372283f, -0.052119261f, -0.045828927f, -0.037935027f,
|
||||||
// +0.011020985f, +0.083912428f, +0.164857408f, +0.252278939f,
|
-0.028914291f, -0.019262057f, -0.009468531f, +0.000003302f,
|
||||||
// +0.344336996f, +0.438979335f, +0.534000832f, +0.627109358f,
|
+0.008737200f, +0.016379869f, +0.022653298f, +0.027362877f,
|
||||||
// +0.715995947f, +0.798406824f, +0.872214756f, +0.935487176f,
|
+0.030401148f, +0.031747267f, +0.031462429f,
|
||||||
// +0.986548646f, +1.024035395f, +1.046939951f, +1.054644241f,
|
};
|
||||||
// +1.046939951f, +1.024035395f, +0.986548646f, +0.935487176f,
|
|
||||||
// +0.872214756f, +0.798406824f, +0.715995947f, +0.627109358f,
|
|
||||||
// +0.534000832f, +0.438979335f, +0.344336996f, +0.252278939f,
|
|
||||||
// +0.164857408f, +0.083912428f, +0.011020985f, -0.052543664f,
|
|
||||||
// -0.105841323f, -0.148282051f, -0.179630919f, -0.200000470f,
|
|
||||||
// -0.209831516f, -0.209863285f, -0.201094346f, -0.184736015f,
|
|
||||||
// -0.162160200f, -0.134843790f, -0.104311776f, -0.072081234f,
|
|
||||||
// -0.039608255f, -0.008239664f, +0.020828821f, +0.046586711f,
|
|
||||||
// +0.068234131f, +0.085197939f, +0.097138329f, +0.103946126f,
|
|
||||||
// +0.105731303f, +0.102803611f, +0.095646450f, +0.084885375f,
|
|
||||||
// +0.071252765f, +0.055550276f, +0.038610717f, +0.021260954f,
|
|
||||||
// +0.004287292f, -0.011595336f, -0.025769308f, -0.037734535f,
|
|
||||||
// -0.047122444f, -0.053703443f, -0.057387924f, -0.058221106f,
|
|
||||||
// -0.056372283f, -0.052119261f, -0.045828927f, -0.037935027f,
|
|
||||||
// -0.028914291f, -0.019262057f, -0.009468531f, +0.000003302f,
|
|
||||||
// +0.008737200f, +0.016379869f, +0.022653298f, +0.027362877f,
|
|
||||||
// +0.030401148f, +0.031747267f, +0.031462429f,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// DMR filter F4EXB
|
// DMR filter F4EXB
|
||||||
#define DMRNZEROS 60
|
#define DMRNZEROS 60
|
||||||
@@ -110,6 +88,52 @@ const float dmrcoeffs[] =
|
|||||||
0.0147202645, -0.0186176083, -0.0428325003, -0.0543747957, -0.0528141756,
|
0.0147202645, -0.0186176083, -0.0428325003, -0.0543747957, -0.0528141756,
|
||||||
-0.0404938748, -0.0216605133, -0.0013114705, 0.0159662432, 0.0269200615, 0.0301506278};
|
-0.0404938748, -0.0216605133, -0.0013114705, 0.0159662432, 0.0269200615, 0.0301506278};
|
||||||
|
|
||||||
|
// dPMR filter, version of F4EXB
|
||||||
|
static float dpmrxv[NXZEROS+1];
|
||||||
|
|
||||||
|
// dPMR filter, version of F4EXB
|
||||||
|
#define DPMRNZEROS 134
|
||||||
|
|
||||||
|
// dPMR filter, version of F4EXB
|
||||||
|
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
|
||||||
|
const float dpmrcoeffs[] =
|
||||||
|
{-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816,
|
||||||
|
0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307,
|
||||||
|
0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763,
|
||||||
|
-0.0013831396, -0.0119878087, -0.0228442151, -0.0333082708,
|
||||||
|
-0.0427067804, -0.0503756642, -0.0557003599, -0.0581561791,
|
||||||
|
-0.0573462646, -0.0530347941, -0.0451732069, -0.0339174991,
|
||||||
|
-0.0196350217, -0.0028997157, 0.0155246961, 0.0347134030,
|
||||||
|
0.0536202583, 0.0711271166, 0.0861006725, 0.0974542022,
|
||||||
|
0.1042112035, 0.1055676660, 0.1009496091, 0.0900625944,
|
||||||
|
0.0729301774, 0.0499186839, 0.0217462748, -0.0105250265,
|
||||||
|
-0.0455148664, -0.0815673067, -0.1168095612, -0.1492246435,
|
||||||
|
-0.1767350726, -0.1972941202, -0.2089805758, -0.2100926829,
|
||||||
|
-0.1992367833, -0.1754063031, -0.1380470370, -0.0871052089,
|
||||||
|
-0.0230554989, 0.0530929052, 0.1398131936, 0.2351006721,
|
||||||
|
0.3365341927, 0.4413570929, 0.5465745033, 0.6490630781,
|
||||||
|
0.7456885564, 0.8334261381, 0.9094784589, 0.9713859928,
|
||||||
|
1.0171250045, 1.0451886943, 1.0546479089, 1.0451886943,
|
||||||
|
1.0171250045, 0.9713859928, 0.9094784589, 0.8334261381,
|
||||||
|
0.7456885564, 0.6490630781, 0.5465745033, 0.4413570929,
|
||||||
|
0.3365341927, 0.2351006721, 0.1398131936, 0.0530929052,
|
||||||
|
-0.0230554989, -0.0871052089, -0.1380470370, -0.1754063031,
|
||||||
|
-0.1992367833, -0.2100926829, -0.2089805758, -0.1972941202,
|
||||||
|
-0.1767350726, -0.1492246435, -0.1168095612, -0.0815673067,
|
||||||
|
-0.0455148664, -0.0105250265, 0.0217462748, 0.0499186839,
|
||||||
|
0.0729301774, 0.0900625944, 0.1009496091, 0.1055676660,
|
||||||
|
0.1042112035, 0.0974542022, 0.0861006725, 0.0711271166,
|
||||||
|
0.0536202583, 0.0347134030, 0.0155246961, -0.0028997157,
|
||||||
|
-0.0196350217, -0.0339174991, -0.0451732069, -0.0530347941,
|
||||||
|
-0.0573462646, -0.0581561791, -0.0557003599, -0.0503756642,
|
||||||
|
-0.0427067804, -0.0333082708, -0.0228442151, -0.0119878087,
|
||||||
|
-0.0013831396, 0.0083888763, 0.0168387650, 0.0235897433,
|
||||||
|
0.0283911865, 0.0311240307, 0.0317982965, 0.0305433586,
|
||||||
|
0.0275919612, 0.0232592816, 0.0179185547, 0.0119748846,
|
||||||
|
0.0058388841, -0.0000983004};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
short dsd_input_filter(short sample, int mode);
|
short dsd_input_filter(short sample, int mode);
|
||||||
@@ -126,6 +150,12 @@ nxdn_filter(short sample)
|
|||||||
return dsd_input_filter(sample, 2);
|
return dsd_input_filter(sample, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
short
|
||||||
|
dpmr_filter(short sample)
|
||||||
|
{
|
||||||
|
return dsd_input_filter(sample, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
short
|
short
|
||||||
dsd_input_filter(short sample, int mode)
|
dsd_input_filter(short sample, int mode)
|
||||||
@@ -144,7 +174,7 @@ dsd_input_filter(short sample, int mode)
|
|||||||
case 2:
|
case 2:
|
||||||
gain = nxgain;
|
gain = nxgain;
|
||||||
v = nxv;
|
v = nxv;
|
||||||
coeffs = nxcoeffs;
|
coeffs = (float *)nxcoeffs;
|
||||||
zeros = NXZEROS;
|
zeros = NXZEROS;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
@@ -153,6 +183,12 @@ dsd_input_filter(short sample, int mode)
|
|||||||
coeffs = (float *)dmrcoeffs;
|
coeffs = (float *)dmrcoeffs;
|
||||||
zeros = DMRNZEROS;
|
zeros = DMRNZEROS;
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
gain = dpmrgain;
|
||||||
|
v = dpmrxv;
|
||||||
|
coeffs = (float *)dpmrcoeffs;
|
||||||
|
zeros = DPMRNZEROS;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
@@ -160,7 +196,7 @@ dsd_input_filter(short sample, int mode)
|
|||||||
for (i = 0; i < zeros; i++)
|
for (i = 0; i < zeros; i++)
|
||||||
v[i] = v[i+1];
|
v[i] = v[i+1];
|
||||||
|
|
||||||
v[zeros] = sample; // unfiltered sample in
|
v[zeros] = (float)sample; // unfiltered sample in
|
||||||
sum = 0.0f;
|
sum = 0.0f;
|
||||||
|
|
||||||
for (i = 0; i <= zeros; i++)
|
for (i = 0; i <= zeros; i++)
|
||||||
|
|||||||
473
src/dsd_frame.c
473
src/dsd_frame.c
@@ -84,54 +84,13 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
state->minref = state->min;
|
state->minref = state->min;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((state->synctype == 8) || (state->synctype == 9)) //NXDN Voice
|
//NXDN FSW
|
||||||
{
|
if ((state->synctype == 28) || (state->synctype == 29))
|
||||||
//state->rf_mod = 2; //wrong type of modulation HERE HERE
|
{
|
||||||
state->nac = 0;
|
//MBEout restored, is not handled internally by nxdn_frame.c
|
||||||
state->lastsrc = 0;
|
nxdn_frame (opts, state);
|
||||||
state->lasttg = 0;
|
return;
|
||||||
if (opts->errorbars == 1)
|
}
|
||||||
{
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
level = (int) state->max / 164;
|
|
||||||
//fprintf (stderr, "inlvl: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->nac = 0;
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
|
||||||
processNXDNVoice (opts, state);
|
|
||||||
//ProcessNXDNFrame (opts, state, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 16) || (state->synctype == 17)) //NXDN Data
|
|
||||||
{
|
|
||||||
//state->rf_mod = 2;
|
|
||||||
state->nac = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
level = (int) state->max / 164;
|
|
||||||
//fprintf (stderr, "inlvl: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->nac = 0;
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " DATA ");
|
|
||||||
processNXDNData (opts, state);
|
|
||||||
//ProcessNXDNFrame (opts, state, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 6) || (state->synctype == 7))
|
else if ((state->synctype == 6) || (state->synctype == 7))
|
||||||
{
|
{
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
@@ -148,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);
|
||||||
@@ -169,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;
|
||||||
@@ -179,89 +138,107 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
//Start DMR Types
|
//Start DMR Types
|
||||||
else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC
|
else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//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?
|
||||||
|
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 == 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?
|
||||||
|
//or, its just a decode error somewhere
|
||||||
|
// else if (state->dmr_mfid == 0x20) sprintf (state->dmr_branding, "%s", "JVC Kenwood");
|
||||||
|
// else if (state->dmr_mfid == 0x04) sprintf (state->dmr_branding, "%s", "Flyde Micro");
|
||||||
|
// else if (state->dmr_mfid == 0x05) sprintf (state->dmr_branding, "%s", "PROD-EL SPA");
|
||||||
|
// else if (state->dmr_mfid == 0x06) sprintf (state->dmr_branding, "%s", "Motorola"); //trident/moto con+
|
||||||
|
// else if (state->dmr_mfid == 0x07) sprintf (state->dmr_branding, "%s", "RADIODATA");
|
||||||
|
// else if (state->dmr_mfid == 0x08) sprintf (state->dmr_branding, "%s", "Hytera");
|
||||||
|
// else if (state->dmr_mfid == 0x09) sprintf (state->dmr_branding, "%s", "ASELSAN");
|
||||||
|
// else if (state->dmr_mfid == 0x0A) sprintf (state->dmr_branding, "%s", "Kirisun");
|
||||||
|
// else if (state->dmr_mfid == 0x0B) sprintf (state->dmr_branding, "%s", "DMR Association");
|
||||||
|
// else if (state->dmr_mfid == 0x13) sprintf (state->dmr_branding, "%s", "EMC S.P.A.");
|
||||||
|
// else if (state->dmr_mfid == 0x1C) sprintf (state->dmr_branding, "%s", "EMC S.P.A.");
|
||||||
|
// else if (state->dmr_mfid == 0x33) sprintf (state->dmr_branding, "%s", "Radio Activity");
|
||||||
|
// else if (state->dmr_mfid == 0x3C) sprintf (state->dmr_branding, "%s", "Radio Activity");
|
||||||
|
// else if (state->dmr_mfid == 0x77) sprintf (state->dmr_branding, "%s", "Vertex Standard");
|
||||||
|
|
||||||
//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) // -T option for DMR (TDMA) stereo
|
||||||
{
|
{
|
||||||
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 ");
|
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 ");
|
|
||||||
processDMRvoice (opts, state);
|
|
||||||
}
|
|
||||||
if (opts->dmr_stereo == 0 && state->synctype == 32)
|
|
||||||
{
|
|
||||||
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) //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);
|
||||||
|
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);
|
||||||
|
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
|
||||||
{
|
{
|
||||||
if (opts->dmr_stereo == 0)
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
{
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
closeMbeOutFile (opts, state);
|
dmrMSData (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
|
state->payload_miP = 0; //zero out when switching back to data sync
|
||||||
processDMRdata (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
|
state->payload_miP = 0; //zero out when switching back to data sync
|
||||||
processDMRdata (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)
|
||||||
@@ -271,15 +248,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);
|
||||||
}
|
}
|
||||||
@@ -287,25 +264,29 @@ 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;
|
||||||
}
|
}
|
||||||
|
//edacs
|
||||||
|
else if ((state->synctype == 37) || (state->synctype == 38))
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
edacs (opts, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
//ysf
|
//ysf
|
||||||
else if ((state->synctype == 30) || (state->synctype == 31))
|
else if ((state->synctype == 30) || (state->synctype == 31))
|
||||||
{
|
{
|
||||||
@@ -322,13 +303,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
//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;
|
||||||
}
|
}
|
||||||
@@ -337,6 +312,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))
|
||||||
{
|
{
|
||||||
@@ -355,12 +331,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;
|
||||||
|
|
||||||
@@ -369,11 +345,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
|
||||||
@@ -472,15 +450,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;
|
||||||
@@ -492,26 +470,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);
|
||||||
}
|
}
|
||||||
@@ -523,27 +497,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;
|
||||||
@@ -555,25 +529,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;
|
||||||
}
|
}
|
||||||
@@ -582,14 +556,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;
|
||||||
@@ -603,120 +577,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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
212
src/dsd_import.c
Normal file
212
src/dsd_import.c
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
#define BSIZE 999
|
||||||
|
|
||||||
|
int csvGroupImport(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
char filename[1024] = "filename.csv";
|
||||||
|
sprintf (filename, "%s", opts->group_in_file);
|
||||||
|
//filename[1023] = '\0'; //necessary?
|
||||||
|
char buffer[BSIZE];
|
||||||
|
FILE * fp;
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("Unable to open group file '%s'\n", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int row_count = 0;
|
||||||
|
int field_count = 0;
|
||||||
|
long int group_number = 0; //local group number for array index value
|
||||||
|
int i = 0;
|
||||||
|
while (fgets(buffer, BSIZE, fp)) {
|
||||||
|
field_count = 0;
|
||||||
|
row_count++;
|
||||||
|
if (row_count == 1)
|
||||||
|
continue; //don't want labels
|
||||||
|
char * field = strtok(buffer, ","); //seperate by comma
|
||||||
|
while (field) {
|
||||||
|
|
||||||
|
if (field_count == 0)
|
||||||
|
{
|
||||||
|
//group_number = atol(field);
|
||||||
|
state->group_array[i].groupNumber = atol(field);
|
||||||
|
fprintf (stderr, "%ld, ", state->group_array[i].groupNumber);
|
||||||
|
}
|
||||||
|
if (field_count == 1)
|
||||||
|
{
|
||||||
|
strcpy(state->group_array[i].groupMode, field);
|
||||||
|
fprintf (stderr, "%s, ", state->group_array[i].groupMode);
|
||||||
|
}
|
||||||
|
if (field_count == 2)
|
||||||
|
{
|
||||||
|
strcpy(state->group_array[i].groupName, field);
|
||||||
|
fprintf (stderr, "%s ", state->group_array[i].groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
field = strtok(NULL, ",");
|
||||||
|
field_count++;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
i++;
|
||||||
|
state->group_tally++;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//LCN import for EDACS, migrated to channel map (channel map does both)
|
||||||
|
int csvLCNImport(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
char filename[1024] = "filename.csv";
|
||||||
|
sprintf (filename, "%s", opts->lcn_in_file);
|
||||||
|
//filename[1023] = '\0'; //necessary?
|
||||||
|
char buffer[BSIZE];
|
||||||
|
FILE * fp;
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("Unable to open lcn file '%s'\n", filename);
|
||||||
|
//have this return -1 and handle it inside of main
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int row_count = 0;
|
||||||
|
int field_count = 0;
|
||||||
|
|
||||||
|
while (fgets(buffer, BSIZE, fp)) {
|
||||||
|
field_count = 0;
|
||||||
|
row_count++;
|
||||||
|
if (row_count == 1)
|
||||||
|
continue; //don't want labels
|
||||||
|
char * field = strtok(buffer, ","); //seperate by comma
|
||||||
|
while (field) {
|
||||||
|
|
||||||
|
state->trunk_lcn_freq[field_count] = atol (field);
|
||||||
|
state->lcn_freq_count++; //keep tally of number of Frequencies imported
|
||||||
|
fprintf (stderr, "LCN [%d] [%ld]", field_count+1, state->trunk_lcn_freq[field_count]);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
|
||||||
|
field = strtok(NULL, ",");
|
||||||
|
field_count++;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "LCN Count %d\n", state->lcn_freq_count);
|
||||||
|
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import
|
||||||
|
{
|
||||||
|
char filename[1024] = "filename.csv";
|
||||||
|
sprintf (filename, "%s", opts->chan_in_file);
|
||||||
|
|
||||||
|
char buffer[BSIZE];
|
||||||
|
FILE * fp;
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("Unable to open channel map file '%s'\n", filename);
|
||||||
|
//have this return -1 and handle it inside of main
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int row_count = 0;
|
||||||
|
int field_count = 0;
|
||||||
|
|
||||||
|
long int chan_number;
|
||||||
|
|
||||||
|
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, "%ld", &chan_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_count == 1)
|
||||||
|
{
|
||||||
|
sscanf (field, "%ld", &state->trunk_chan_map[chan_number]);
|
||||||
|
//adding this should be compatible with EDACS, test and obsolete the LCN Import function if desired
|
||||||
|
sscanf (field, "%ld", &state->trunk_lcn_freq[state->lcn_freq_count]);
|
||||||
|
state->lcn_freq_count++; //keep tally of number of Frequencies imported
|
||||||
|
}
|
||||||
|
|
||||||
|
field = strtok(NULL, ",");
|
||||||
|
field_count++;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "Channel [%05ld] [%09ld]", chan_number, state->trunk_chan_map[chan_number]);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int csvKeyImport(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;
|
||||||
|
}
|
||||||
1456
src/dsd_main.c
1456
src/dsd_main.c
File diff suppressed because it is too large
Load Diff
410
src/dsd_mbe.c
410
src/dsd_mbe.c
@@ -17,8 +17,42 @@
|
|||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
void
|
int Pr[256] = {
|
||||||
playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
|
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
|
||||||
|
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
|
||||||
|
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
|
||||||
|
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E, //31
|
||||||
|
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
|
||||||
|
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
|
||||||
|
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
|
||||||
|
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36, //63
|
||||||
|
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
|
||||||
|
0x614C, 0xAE4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
|
||||||
|
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
|
||||||
|
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57, //95
|
||||||
|
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
|
||||||
|
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
|
||||||
|
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
|
||||||
|
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F, //127
|
||||||
|
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
|
||||||
|
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
|
||||||
|
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
|
||||||
|
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897, //159
|
||||||
|
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
|
||||||
|
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
|
||||||
|
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
|
||||||
|
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF, //191
|
||||||
|
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
|
||||||
|
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
|
||||||
|
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
|
||||||
|
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3, //223
|
||||||
|
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
|
||||||
|
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
|
||||||
|
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
|
||||||
|
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255
|
||||||
|
};
|
||||||
|
|
||||||
|
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
@@ -26,99 +60,79 @@ playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
|
|||||||
char ambe_d[49];
|
char ambe_d[49];
|
||||||
|
|
||||||
for (i = state->optind; i < argc; i++)
|
for (i = state->optind; i < argc; i++)
|
||||||
|
{
|
||||||
|
sprintf (opts->mbe_in_file, "%s", argv[i]);
|
||||||
|
openMbeInFile (opts, state);
|
||||||
|
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||||
|
fprintf (stderr, "playing %s\n", opts->mbe_in_file);
|
||||||
|
while (feof (opts->mbe_in_f) == 0)
|
||||||
{
|
{
|
||||||
sprintf (opts->mbe_in_file, "%s", argv[i]);
|
if (state->mbe_file_type == 0)
|
||||||
openMbeInFile (opts, state);
|
{
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
readImbe4400Data (opts, state, imbe_d);
|
||||||
fprintf (stderr, "playing %s\n", opts->mbe_in_file);
|
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
while (feof (opts->mbe_in_f) == 0)
|
if (opts->audio_out == 1)
|
||||||
{
|
{
|
||||||
if (state->mbe_file_type == 0)
|
processAudio(opts, state);
|
||||||
{
|
|
||||||
readImbe4400Data (opts, state, imbe_d);
|
|
||||||
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
processAudio(opts, state);
|
|
||||||
}
|
|
||||||
if (opts->wav_out_f != NULL)
|
|
||||||
{
|
|
||||||
writeSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
playSynthesizedVoice (opts, state);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (state->mbe_file_type == 1)
|
|
||||||
{
|
|
||||||
readAmbe2450Data (opts, state, ambe_d);
|
|
||||||
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
processAudio(opts, state);
|
|
||||||
}
|
|
||||||
if (opts->wav_out_f != NULL)
|
|
||||||
{
|
|
||||||
writeSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
playSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (exitflag == 1)
|
|
||||||
{
|
|
||||||
cleanupAndExit (opts, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (opts->wav_out_f != NULL)
|
||||||
|
{
|
||||||
|
writeSynthesizedVoice (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->audio_out == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
playSynthesizedVoice (opts, state);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state->mbe_file_type > 0) //ambe files
|
||||||
|
{
|
||||||
|
readAmbe2450Data (opts, state, ambe_d);
|
||||||
|
int j, x;
|
||||||
|
unsigned long long int k;
|
||||||
|
if (state->K != 0) //apply Pr key
|
||||||
|
{
|
||||||
|
k = Pr[state->K];
|
||||||
|
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||||
|
for (short int j = 0; j < 48; j++) //49
|
||||||
|
{
|
||||||
|
x = ( ((k << j) & 0x800000000000) >> 47 );
|
||||||
|
ambe_d[j] ^= x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ambe+2
|
||||||
|
if (state->mbe_file_type == 1) mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
|
//dstar ambe
|
||||||
|
if (state->mbe_file_type == 2) mbe_processAmbe2400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
|
|
||||||
|
if (opts->audio_out == 1)
|
||||||
|
{
|
||||||
|
processAudio(opts, state);
|
||||||
|
}
|
||||||
|
if (opts->wav_out_f != NULL)
|
||||||
|
{
|
||||||
|
writeSynthesizedVoice (opts, state);
|
||||||
|
}
|
||||||
|
if (opts->audio_out == 1)
|
||||||
|
{
|
||||||
|
playSynthesizedVoice (opts, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exitflag == 1)
|
||||||
|
{
|
||||||
|
cleanupAndExit (opts, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
|
processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
|
||||||
{
|
{
|
||||||
|
|
||||||
//
|
|
||||||
int Pr[256] = {
|
|
||||||
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
|
|
||||||
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
|
|
||||||
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
|
|
||||||
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E, //31
|
|
||||||
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
|
|
||||||
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
|
|
||||||
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
|
|
||||||
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36, //63
|
|
||||||
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
|
|
||||||
0x614C, 0xAE4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
|
|
||||||
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
|
|
||||||
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57, //95
|
|
||||||
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
|
|
||||||
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
|
|
||||||
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
|
|
||||||
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F, //127
|
|
||||||
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
|
|
||||||
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
|
|
||||||
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
|
|
||||||
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897, //159
|
|
||||||
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
|
|
||||||
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
|
|
||||||
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
|
|
||||||
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF, //191
|
|
||||||
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
|
|
||||||
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
|
|
||||||
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
|
|
||||||
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3, //223
|
|
||||||
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
|
|
||||||
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
|
|
||||||
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
|
|
||||||
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255
|
|
||||||
};
|
|
||||||
//
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
char imbe_d[88];
|
char imbe_d[88];
|
||||||
char ambe_d[49];
|
char ambe_d[49];
|
||||||
@@ -126,17 +140,48 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
unsigned long long int k;
|
unsigned long long int k;
|
||||||
int x;
|
int x;
|
||||||
|
|
||||||
|
//24-bit TG to 16-bit hash
|
||||||
|
uint32_t hash = 0;
|
||||||
|
uint8_t hash_bits[24];
|
||||||
|
memset (hash_bits, 0, sizeof(hash_bits));
|
||||||
|
|
||||||
|
int preempt = 0; //TDMA dual voice slot preemption(when using OSS output)
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < 88; i++)
|
for (i = 0; i < 88; i++)
|
||||||
|
{
|
||||||
|
imbe_d[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
{
|
||||||
|
ambe_d[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set playback mode for this frame
|
||||||
|
char mode[8];
|
||||||
|
|
||||||
|
//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");
|
||||||
|
|
||||||
|
int groupNumber = 0;
|
||||||
|
|
||||||
|
if (state->currentslot == 0) groupNumber = state->lasttg;
|
||||||
|
else groupNumber = state->lasttgR;
|
||||||
|
|
||||||
|
for (i = 0; i < state->group_tally; i++)
|
||||||
|
{
|
||||||
|
if (state->group_array[i].groupNumber == groupNumber)
|
||||||
{
|
{
|
||||||
imbe_d[i] = 0;
|
strcpy (mode, state->group_array[i].groupMode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 49; i++)
|
//set flag to not play audio this time, but won't prevent writing to wav files
|
||||||
{
|
if (strcmp(mode, "B") == 0) opts->audio_out = 0;
|
||||||
ambe_d[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//end set playback mode for this frame
|
||||||
|
|
||||||
if ((state->synctype == 0) || (state->synctype == 1))
|
if ((state->synctype == 0) || (state->synctype == 1))
|
||||||
{
|
{
|
||||||
@@ -173,17 +218,16 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
//state->errs2 = state->errs;
|
//state->errs2 = state->errs;
|
||||||
mbe_demodulateImbe7100x4400Data (imbe7100_fr);
|
mbe_demodulateImbe7100x4400Data (imbe7100_fr);
|
||||||
state->errs2 = mbe_eccImbe7100x4400Data (imbe7100_fr, imbe_d);
|
state->errs2 = mbe_eccImbe7100x4400Data (imbe7100_fr, imbe_d);
|
||||||
mbe_convertImbe7100to7200(imbe_d); //needs extra conversion step apparently
|
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
PrintIMBEData (opts, state, imbe_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
mbe_convertImbe7100to7200(imbe_d);
|
||||||
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
||||||
imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
|
|
||||||
//below line the old way of doing this
|
|
||||||
// mbe_processImbe7100x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe7100_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
PrintIMBEData (opts, state, imbe_d);
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_f != NULL)
|
if (opts->mbe_out_f != NULL)
|
||||||
{
|
{
|
||||||
saveImbe4400Data (opts, state, imbe_d);
|
saveImbe4400Data (opts, state, imbe_d);
|
||||||
@@ -201,7 +245,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
saveAmbe2450Data (opts, state, ambe_d);
|
saveAmbe2450Data (opts, state, ambe_d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 8) || (state->synctype == 9))
|
else if ((state->synctype == 28) || (state->synctype == 29)) //was 8 and 9
|
||||||
{
|
{
|
||||||
|
|
||||||
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||||
@@ -245,7 +289,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//stereo slots and slot 0 (left slot)
|
//stereo slots and slot 0 (left slot)
|
||||||
if (state->currentslot == 0 && opts->dmr_stereo == 1)
|
if (state->currentslot == 0) //&& opts->dmr_stereo == 1
|
||||||
{
|
{
|
||||||
|
|
||||||
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||||
@@ -253,6 +297,34 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||||
state->errs2 = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
state->errs2 = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||||
|
|
||||||
|
//EXPERIMENTAL!!
|
||||||
|
//load basic privacy key number from array by the tg value (if not forced)
|
||||||
|
//currently only Moto BP and Hytera 10 Char BP (converted to decimal)
|
||||||
|
if (state->M == 0)
|
||||||
|
{
|
||||||
|
//see if we need to hash a value larger than 16-bits
|
||||||
|
hash = state->lasttg & 0xFFFFFF;
|
||||||
|
// fprintf (stderr, "TG: %lld Hash: %ld ", state->lasttg, hash);
|
||||||
|
if (hash > 0xFFFF) //if greater than 16-bits
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
hash_bits[i] = ((hash << i) & 0x800000) >> 23; //load into array for CRC16
|
||||||
|
}
|
||||||
|
hash = ComputeCrcCCITT16d (hash_bits, 24);
|
||||||
|
hash = hash & 0xFFFF; //make sure its no larger than 16-bits
|
||||||
|
// fprintf (stderr, "Hash: %d ", hash);
|
||||||
|
}
|
||||||
|
if (state->rkey_array[hash] != 0)
|
||||||
|
{
|
||||||
|
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
|
||||||
|
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
|
||||||
|
opts->dmr_mute_encL = 0;
|
||||||
|
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
|
||||||
|
}
|
||||||
|
// else opts->dmr_mute_encL = 1; //may cause issues for manual key entry (non-csv)
|
||||||
|
}
|
||||||
|
|
||||||
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||||
(state->K > 0 && state->M == 1) )
|
(state->K > 0 && state->M == 1) )
|
||||||
{
|
{
|
||||||
@@ -338,9 +410,16 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
{
|
{
|
||||||
PrintAMBEData (opts, state, ambe_d);
|
PrintAMBEData (opts, state, ambe_d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//restore MBE file save, slot 1 -- consider saving even if enc
|
||||||
|
if (opts->mbe_out_f != NULL && (state->dmr_encL == 0 || opts->dmr_mute_encL == 0) )
|
||||||
|
{
|
||||||
|
saveAmbe2450Data (opts, state, ambe_d);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
//stereo slots and slot 1 (right slot)
|
//stereo slots and slot 1 (right slot)
|
||||||
if (state->currentslot == 1 && opts->dmr_stereo == 1)
|
if (state->currentslot == 1) //&& opts->dmr_stereo == 1
|
||||||
{
|
{
|
||||||
|
|
||||||
state->errsR = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
state->errsR = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||||
@@ -348,6 +427,34 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||||
state->errs2R = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
state->errs2R = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||||
|
|
||||||
|
//EXPERIMENTAL!!
|
||||||
|
//load basic privacy key number from array by the tg value (if not forced)
|
||||||
|
//currently only Moto BP and Hytera 10 Char BP (converted to decimal)
|
||||||
|
if (state->M == 0)
|
||||||
|
{
|
||||||
|
//see if we need to hash a value larger than 16-bits
|
||||||
|
hash = state->lasttgR & 0xFFFFFF;
|
||||||
|
// fprintf (stderr, "TG: %lld Hash: %ld ", state->lasttgR, hash);
|
||||||
|
if (hash > 0xFFFF) //if greater than 16-bits
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
hash_bits[i] = ((hash << i) & 0x800000) >> 23; //load into array for CRC16
|
||||||
|
}
|
||||||
|
hash = ComputeCrcCCITT16d (hash_bits, 24);
|
||||||
|
hash = hash & 0xFFFF; //make sure its no larger than 16-bits
|
||||||
|
// fprintf (stderr, "Hash: %d ", hash);
|
||||||
|
}
|
||||||
|
if (state->rkey_array[hash] != 0)
|
||||||
|
{
|
||||||
|
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
|
||||||
|
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
|
||||||
|
opts->dmr_mute_encR = 0;
|
||||||
|
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
|
||||||
|
}
|
||||||
|
// else opts->dmr_mute_encR = 1; //may cause issues for manual key entry (non-csv)
|
||||||
|
}
|
||||||
|
|
||||||
if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
||||||
(state->K > 0 && state->M == 1) )
|
(state->K > 0 && state->M == 1) )
|
||||||
{
|
{
|
||||||
@@ -432,22 +539,28 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
{
|
{
|
||||||
PrintAMBEData (opts, state, ambe_d);
|
PrintAMBEData (opts, state, ambe_d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//restore MBE file save, slot 2 -- consider saving even if enc
|
||||||
|
if (opts->mbe_out_fR != NULL && (state->dmr_encR == 0 || opts->dmr_mute_encR == 0) )
|
||||||
|
{
|
||||||
|
saveAmbe2450DataR (opts, state, ambe_d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//X2-TDMA? Not sure what still makes it this far to run under Framef
|
//X2-TDMA? Not sure what still makes it this far to run under Framef
|
||||||
if (opts->dmr_stereo == 0)
|
// if (opts->dmr_stereo == 0)
|
||||||
{
|
// {
|
||||||
mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
// mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
if (opts->payload == 1)
|
// if (opts->payload == 1)
|
||||||
{
|
// {
|
||||||
PrintAMBEData (opts, state, ambe_d);
|
// PrintAMBEData (opts, state, ambe_d);
|
||||||
}
|
// }
|
||||||
//only save MBE files if not enc or unmuted, THIS does not seem to work for some reason
|
// //only save MBE files if not enc or unmuted, THIS does not seem to work for some reason
|
||||||
if (opts->mbe_out_f != NULL && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
// if (opts->mbe_out_f != NULL && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
||||||
{
|
// {
|
||||||
saveAmbe2450Data (opts, state, ambe_d);
|
// saveAmbe2450Data (opts, state, ambe_d);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -456,7 +569,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
int enc_bit = 0;
|
int enc_bit = 0;
|
||||||
//end enc check
|
//end enc check
|
||||||
|
|
||||||
if (opts->dmr_stereo == 1 && state->currentslot == 0)
|
if ( (opts->dmr_mono == 1 || opts->dmr_stereo == 1) && state->currentslot == 0) //all mono traffic routed through 'left'
|
||||||
{
|
{
|
||||||
enc_bit = (state->dmr_so >> 6) & 0x1;
|
enc_bit = (state->dmr_so >> 6) & 0x1;
|
||||||
if (enc_bit == 1)
|
if (enc_bit == 1)
|
||||||
@@ -480,6 +593,35 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
|
||||||
|
if (opts->reverse_mute == 1)
|
||||||
|
{
|
||||||
|
if (state->dmr_encL == 0)
|
||||||
|
{
|
||||||
|
state->dmr_encL = 1;
|
||||||
|
opts->unmute_encrypted_p25 = 0;
|
||||||
|
opts->dmr_mute_encL = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->dmr_encL = 0;
|
||||||
|
opts->unmute_encrypted_p25 = 1;
|
||||||
|
opts->dmr_mute_encL = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//end reverse mute test
|
||||||
|
|
||||||
|
#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
|
||||||
|
//OSS Specific Voice Preemption if dual voices on TDMA and one slot has preference over the other
|
||||||
|
if (opts->slot_preference == 1 && opts->audio_out_type == 5 && opts->audio_out == 1 && state->dmrburstR == 16)
|
||||||
|
{
|
||||||
|
opts->audio_out = 0;
|
||||||
|
preempt = 1;
|
||||||
|
if (opts->payload == 0)
|
||||||
|
fprintf (stderr, " *MUTED*");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
||||||
{
|
{
|
||||||
state->debug_audio_errors += state->errs2;
|
state->debug_audio_errors += state->errs2;
|
||||||
@@ -518,6 +660,35 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
|
||||||
|
if (opts->reverse_mute == 1)
|
||||||
|
{
|
||||||
|
if (state->dmr_encR == 0)
|
||||||
|
{
|
||||||
|
state->dmr_encR = 1;
|
||||||
|
opts->unmute_encrypted_p25 = 0;
|
||||||
|
opts->dmr_mute_encR = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->dmr_encR = 0;
|
||||||
|
opts->unmute_encrypted_p25 = 1;
|
||||||
|
opts->dmr_mute_encR = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//end reverse mute test
|
||||||
|
|
||||||
|
#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
|
||||||
|
//OSS Specific Voice Preemption if dual voices on TDMA and one slot has preference over the other
|
||||||
|
if (opts->slot_preference == 0 && opts->audio_out_type == 5 && opts->audio_out == 1 && state->dmrburstL == 16)
|
||||||
|
{
|
||||||
|
opts->audio_out = 0;
|
||||||
|
preempt = 1;
|
||||||
|
if (opts->payload == 0)
|
||||||
|
fprintf (stderr, " *MUTED*");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
||||||
{
|
{
|
||||||
state->debug_audio_errorsR += state->errs2R;
|
state->debug_audio_errorsR += state->errs2R;
|
||||||
@@ -533,7 +704,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
|
|
||||||
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
|
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
|
||||||
if (opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
if (opts->dmr_mono == 0 && opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
||||||
{
|
{
|
||||||
state->debug_audio_errors += state->errs2;
|
state->debug_audio_errors += state->errs2;
|
||||||
if (opts->audio_out == 1)
|
if (opts->audio_out == 1)
|
||||||
@@ -572,5 +743,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (preempt == 1)
|
||||||
|
{
|
||||||
|
opts->audio_out = 1;
|
||||||
|
preempt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset audio out flag for next repitition
|
||||||
|
if (strcmp(mode, "B") == 0) opts->audio_out = 1;
|
||||||
|
//restore flag for null output type
|
||||||
|
if (opts->audio_out_type == 9) opts->audio_out = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
1933
src/dsd_ncurses.c
1933
src/dsd_ncurses.c
File diff suppressed because it is too large
Load Diff
281
src/dsd_rigctl.c
Normal file
281
src/dsd_rigctl.c
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dsd_rigctl.c
|
||||||
|
* Simple RIGCTL Client for DSD (remote control of GQRX, SDR++, etc)
|
||||||
|
*
|
||||||
|
* Portions from https://github.com/neural75/gqrx-scanner
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-10 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
//UDP Specific
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#define BUFSIZE 1024
|
||||||
|
#define FREQ_MAX 4096
|
||||||
|
#define SAVED_FREQ_MAX 1000
|
||||||
|
#define TAG_MAX 100
|
||||||
|
|
||||||
|
//
|
||||||
|
// error - wrapper for perror
|
||||||
|
//
|
||||||
|
void error(char *msg) {
|
||||||
|
perror(msg);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Connect
|
||||||
|
//
|
||||||
|
int Connect (char *hostname, int portno)
|
||||||
|
{
|
||||||
|
int sockfd, n;
|
||||||
|
struct sockaddr_in serveraddr;
|
||||||
|
struct hostent *server;
|
||||||
|
|
||||||
|
|
||||||
|
/* socket: create the socket */
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"ERROR opening socket\n");
|
||||||
|
error("ERROR opening socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* gethostbyname: get the server's DNS entry */
|
||||||
|
server = gethostbyname(hostname);
|
||||||
|
if (server == NULL) {
|
||||||
|
fprintf(stderr,"ERROR, no such host as %s\n", hostname);
|
||||||
|
//exit(0);
|
||||||
|
return (0); //return 0, check on other end and configure pulse input
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the server's Internet address */
|
||||||
|
bzero((char *) &serveraddr, sizeof(serveraddr));
|
||||||
|
serveraddr.sin_family = AF_INET;
|
||||||
|
bcopy((char *)server->h_addr,
|
||||||
|
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
|
||||||
|
serveraddr.sin_port = htons(portno);
|
||||||
|
|
||||||
|
/* connect: create a connection with the server */
|
||||||
|
if (connect(sockfd, (const struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"ERROR opening socket\n");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Send
|
||||||
|
//
|
||||||
|
bool Send(int sockfd, char *buf)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = write(sockfd, buf, strlen(buf));
|
||||||
|
if (n < 0)
|
||||||
|
error("ERROR writing to socket");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Recv
|
||||||
|
//
|
||||||
|
bool Recv(int sockfd, char *buf)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = read(sockfd, buf, BUFSIZE);
|
||||||
|
if (n < 0)
|
||||||
|
error("ERROR reading from socket");
|
||||||
|
buf[n]= '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// GQRX Protocol
|
||||||
|
//
|
||||||
|
long int GetCurrentFreq(int sockfd)
|
||||||
|
{
|
||||||
|
long int freq = 0;
|
||||||
|
char buf[BUFSIZE];
|
||||||
|
char * ptr;
|
||||||
|
char * token;
|
||||||
|
|
||||||
|
Send(sockfd, "f\n");
|
||||||
|
Recv(sockfd, buf);
|
||||||
|
|
||||||
|
if (strcmp(buf, "RPRT 1") == 0 )
|
||||||
|
return freq;
|
||||||
|
|
||||||
|
token = strtok (buf, "\n");
|
||||||
|
freq = strtol (token, &ptr, 10);
|
||||||
|
// fprintf (stderr, "\nRIGCTL VFO Freq: [%ld]\n", freq);
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetFreq(int sockfd, long int freq)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZE];
|
||||||
|
|
||||||
|
sprintf (buf, "F %ld\n", freq);
|
||||||
|
Send(sockfd, buf);
|
||||||
|
Recv(sockfd, buf);
|
||||||
|
|
||||||
|
if (strcmp(buf, "RPRT 1") == 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetModulation(int sockfd, int bandwidth)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZE];
|
||||||
|
//the bandwidth is now a user/system based configurable variable
|
||||||
|
sprintf (buf, "M FM %d\n", bandwidth);
|
||||||
|
Send(sockfd, buf);
|
||||||
|
Recv(sockfd, buf);
|
||||||
|
|
||||||
|
if (strcmp(buf, "RPRT 1") == 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetSignalLevel(int sockfd, double *dBFS)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZE];
|
||||||
|
|
||||||
|
Send(sockfd, "l\n");
|
||||||
|
Recv(sockfd, buf);
|
||||||
|
|
||||||
|
if (strcmp(buf, "RPRT 1") == 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sscanf(buf, "%lf", dBFS);
|
||||||
|
*dBFS = round((*dBFS) * 10)/10;
|
||||||
|
|
||||||
|
if (*dBFS == 0.0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetSquelchLevel(int sockfd, double *dBFS)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZE];
|
||||||
|
|
||||||
|
Send(sockfd, "l SQL\n");
|
||||||
|
Recv(sockfd, buf);
|
||||||
|
|
||||||
|
if (strcmp(buf, "RPRT 1") == 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sscanf(buf, "%lf", dBFS);
|
||||||
|
*dBFS = round((*dBFS) * 10)/10;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetSquelchLevel(int sockfd, double dBFS)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZE];
|
||||||
|
|
||||||
|
sprintf (buf, "L SQL %f\n", dBFS);
|
||||||
|
Send(sockfd, buf);
|
||||||
|
Recv(sockfd, buf);
|
||||||
|
|
||||||
|
if (strcmp(buf, "RPRT 1") == 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// GetSignalLevelEx
|
||||||
|
// Get a bunch of sample with some delay and calculate the mean value
|
||||||
|
//
|
||||||
|
bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp)
|
||||||
|
{
|
||||||
|
double temp_level;
|
||||||
|
*dBFS = 0;
|
||||||
|
int errors = 0;
|
||||||
|
for (int i = 0; i < n_samp; i++)
|
||||||
|
{
|
||||||
|
if ( GetSignalLevel(sockfd, &temp_level) )
|
||||||
|
*dBFS = *dBFS + temp_level;
|
||||||
|
else
|
||||||
|
errors++;
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
*dBFS = *dBFS / (n_samp - errors);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//shoe in UDP input connection here...still having issues that I don't know how to resolve
|
||||||
|
int UDPBind (char *hostname, int portno)
|
||||||
|
{
|
||||||
|
int sockfd, n;
|
||||||
|
struct sockaddr_in serveraddr, client_addr;
|
||||||
|
struct hostent *server;
|
||||||
|
|
||||||
|
/* socket: create the socket */
|
||||||
|
//UDP socket
|
||||||
|
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
|
||||||
|
if (sockfd < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"ERROR opening UDP socket\n");
|
||||||
|
error("ERROR opening UDP socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the server's Internet address */
|
||||||
|
bzero((char *) &serveraddr, sizeof(serveraddr));
|
||||||
|
serveraddr.sin_family = AF_INET;
|
||||||
|
serveraddr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY
|
||||||
|
serveraddr.sin_port = htons(portno);
|
||||||
|
|
||||||
|
//Bind socket to listening
|
||||||
|
if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
|
||||||
|
perror("ERROR on binding UDP Port");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
||||||
|
{
|
||||||
|
int handle;
|
||||||
|
unsigned short udp_port = opts->rtl_udp_port;
|
||||||
|
char data[5] = {0}; //data buffer size is 5 for UDP frequency tuning
|
||||||
|
struct sockaddr_in address;
|
||||||
|
|
||||||
|
uint32_t new_freq = frequency;
|
||||||
|
opts->rtlsdr_center_freq = new_freq; //for ncurses terminal display after rtl is started up
|
||||||
|
|
||||||
|
handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
|
||||||
|
data[0] = 0;
|
||||||
|
data[1] = new_freq & 0xFF;
|
||||||
|
data[2] = (new_freq >> 8) & 0xFF;
|
||||||
|
data[3] = (new_freq >> 16) & 0xFF;
|
||||||
|
data[4] = (new_freq >> 24) & 0xFF;
|
||||||
|
|
||||||
|
memset((char * ) & address, 0, sizeof(address));
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later
|
||||||
|
address.sin_port = htons(udp_port);
|
||||||
|
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
||||||
|
|
||||||
|
//BUGFIX: This was causing an indefinite number of UDP sockets to stay open on each tune request
|
||||||
|
//we open a new socket with each request, so we need to close the socket after writing to it
|
||||||
|
//this would cause the tuning to stop after x number of tune attempts (few hours? on a busy system)
|
||||||
|
close (handle); //close socket after sending.
|
||||||
|
}
|
||||||
252
src/dsd_symbol.c
252
src/dsd_symbol.c
@@ -82,50 +82,50 @@ 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);
|
||||||
}
|
}
|
||||||
else if (opts->audio_in_type == 1) {
|
|
||||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
//stdin only, wav files moving to new number
|
||||||
//fprintf (stderr, "..");
|
else if (opts->audio_in_type == 1) //won't work in windows, needs posix pipe (mintty)
|
||||||
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
|
{
|
||||||
{
|
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||||
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
|
if(result == 0)
|
||||||
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
|
{
|
||||||
}
|
sf_close(opts->audio_in_file);
|
||||||
if(result == 0) {
|
cleanupAndExit (opts, state);
|
||||||
cleanupAndExit (opts, state);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
//wav files, same but using seperate value so we can still manipulate ncurses menu
|
||||||
|
//since we can not worry about getch/stdin conflict
|
||||||
else if (opts->audio_in_type == 2)
|
else if (opts->audio_in_type == 2)
|
||||||
{
|
{
|
||||||
#ifdef USE_PORTAUDIO
|
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||||
PaError err = Pa_ReadStream( opts->audio_in_pa_stream, &sample, 1 );
|
if(result == 0)
|
||||||
if( err != paNoError )
|
|
||||||
{
|
{
|
||||||
fprintf( stderr, "An error occured while using the portaudio input stream\n" );
|
|
||||||
fprintf( stderr, "Error number: %d\n", err );
|
sf_close(opts->audio_in_file);
|
||||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
fprintf (stderr, "\n\nEnd of .wav file.\n");
|
||||||
|
//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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
#ifdef USE_RTLSDR
|
#ifdef USE_RTLSDR
|
||||||
@@ -141,11 +141,139 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TRACE_DSD
|
//tcp socket input from SDR++ -- now with 1 retry if connection is broken
|
||||||
state->debug_sample_index++;
|
else if (opts->audio_in_type == 8)
|
||||||
#endif
|
{
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
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
|
||||||
|
//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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//tcp socket input from SDR++ -- old method to open pulse or quit right away
|
||||||
|
// else if (opts->audio_in_type == 8)
|
||||||
|
// {
|
||||||
|
// result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
// if(result == 0) {
|
||||||
|
|
||||||
|
// fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||||
|
// //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 cleanupAndExit(opts, state);
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//UDP Socket input...not working correct. Reads samples, but no sync
|
||||||
|
// else if (opts->audio_in_type == 6)
|
||||||
|
// {
|
||||||
|
//I think this doesn't get the entire dgram when we run sf_read_short on the udp dgram
|
||||||
|
// result = sf_read_short(opts->udp_file_in, &sample, 1);
|
||||||
|
// if (sample != 0)
|
||||||
|
// fprintf (stderr, "Result = %d Sample = %d \n", result, sample);
|
||||||
|
// }
|
||||||
|
|
||||||
// printf ("res: %zd\n, offset: %lld", result, sf_seek(opts->audio_in_file, 0, SEEK_CUR));
|
|
||||||
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)
|
||||||
@@ -158,18 +286,25 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
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 ||
|
||||||
state->lastsynctype == 26 || state->lastsynctype == 27 ) //||
|
state->lastsynctype == 26 || state->lastsynctype == 27 ||
|
||||||
//state->lastsynctype == 35 || state->lastsynctype == 36) //phase 2
|
state->lastsynctype == 28 || state->lastsynctype == 29 ) //||
|
||||||
|
//state->lastsynctype == 35 || state->lastsynctype == 36) //phase 2 C4FM disc tap input
|
||||||
{
|
{
|
||||||
if(state->samplesPerSymbol == 20)
|
//if(state->samplesPerSymbol == 20)
|
||||||
|
if(opts->frame_nxdn48 == 1)
|
||||||
{
|
{
|
||||||
sample = nxdn_filter(sample);
|
sample = nxdn_filter(sample);
|
||||||
}
|
}
|
||||||
|
//else if (state->lastsynctype >= 20 && state->lastsynctype <=27) //this the right range?
|
||||||
|
else if (opts->frame_dpmr == 1)
|
||||||
|
{
|
||||||
|
sample = dpmr_filter(sample);
|
||||||
|
}
|
||||||
else if (state->samplesPerSymbol == 8) //phase 2 cqpsk
|
else if (state->samplesPerSymbol == 8) //phase 2 cqpsk
|
||||||
{
|
{
|
||||||
//sample = dmr_filter(sample); //work on filter later
|
//sample = dmr_filter(sample); //work on filter later
|
||||||
}
|
}
|
||||||
else // the 12.5KHz NXDN filter is the same as the DMR filter
|
else // the 12.5KHz NXDN filter is the same as the DMR filter...hopefully
|
||||||
{
|
{
|
||||||
sample = dmr_filter(sample);
|
sample = dmr_filter(sample);
|
||||||
}
|
}
|
||||||
@@ -356,6 +491,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)
|
||||||
{
|
{
|
||||||
@@ -372,25 +513,38 @@ 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, "\n\nEnd of .bin file\n");
|
||||||
|
//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
|
||||||
if (state->rf_mod == 2) //GFSK
|
if (state->rf_mod == 2) //GFSK
|
||||||
{
|
{
|
||||||
symbol = state->symbolc;
|
symbol = state->symbolc;
|
||||||
if (state->symbolc == 0 && state->synctype >= 0)
|
if (state->symbolc == 0 )
|
||||||
{
|
{
|
||||||
symbol = -3; //-1
|
symbol = -3; //-1
|
||||||
}
|
}
|
||||||
if (state->symbolc == 1 && state->synctype >= 0)
|
if (state->symbolc == 1 )
|
||||||
{
|
{
|
||||||
symbol = -1; //-3
|
symbol = -1; //-3
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,79 +54,85 @@ upsample (dsd_state * state, float invalue)
|
|||||||
*outbuf1 = d;
|
*outbuf1 = d;
|
||||||
outbuf1++;
|
outbuf1++;
|
||||||
|
|
||||||
if (state->currentslot == 1 && state->dmr_stereo == 1)
|
//only if enabled, produces crackling/buzzing sounds when input type is not pulse audio (reason still unknown)
|
||||||
|
//also, could be improved to provide better audio in the future
|
||||||
|
if (state->audio_smoothing == 1)
|
||||||
{
|
{
|
||||||
if (state->audio_out_idx2R > 24)
|
if (state->currentslot == 1 && state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
// smoothing
|
if (state->audio_out_idx2R > 24)
|
||||||
outbuf1 -= 16;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
{
|
||||||
for (i = 0; i < 6; i++)
|
// smoothing
|
||||||
|
outbuf1 -= 16;
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
sum = 0;
|
||||||
|
outbuf1 -= 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 += 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 += 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 -= 2;
|
||||||
|
*outbuf1 = (sum / (float) 3);
|
||||||
|
outbuf1++;
|
||||||
|
}
|
||||||
|
outbuf1 -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state->currentslot == 0 && state->dmr_stereo == 1)
|
||||||
|
{
|
||||||
|
if (state->audio_out_idx2 > 24)
|
||||||
|
{
|
||||||
|
// smoothing
|
||||||
|
outbuf1 -= 16;
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
sum = 0;
|
for (i = 0; i < 6; i++)
|
||||||
outbuf1 -= 2;
|
{
|
||||||
sum += (int)*outbuf1;
|
sum = 0;
|
||||||
outbuf1 += 2;
|
outbuf1 -= 2;
|
||||||
sum += (int)*outbuf1;
|
sum += (int)*outbuf1;
|
||||||
outbuf1 += 2;
|
outbuf1 += 2;
|
||||||
sum += (int)*outbuf1;
|
sum += (int)*outbuf1;
|
||||||
outbuf1 -= 2;
|
outbuf1 += 2;
|
||||||
*outbuf1 = (sum / (float) 3);
|
sum += (int)*outbuf1;
|
||||||
outbuf1++;
|
outbuf1 -= 2;
|
||||||
|
*outbuf1 = (sum / (float) 3);
|
||||||
|
outbuf1++;
|
||||||
|
}
|
||||||
|
outbuf1 -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state->dmr_stereo == 0)
|
||||||
|
{
|
||||||
|
if (state->audio_out_idx2 > 24)
|
||||||
|
{
|
||||||
|
// smoothing
|
||||||
|
outbuf1 -= 16;
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
sum = 0;
|
||||||
|
outbuf1 -= 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 += 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 += 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 -= 2;
|
||||||
|
*outbuf1 = (sum / (float) 3);
|
||||||
|
outbuf1++;
|
||||||
|
}
|
||||||
|
outbuf1 -= 8;
|
||||||
}
|
}
|
||||||
outbuf1 -= 8;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state->currentslot == 0 && state->dmr_stereo == 1)
|
|
||||||
{
|
|
||||||
if (state->audio_out_idx2 > 24)
|
|
||||||
{
|
|
||||||
// smoothing
|
|
||||||
outbuf1 -= 16;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
*outbuf1 = (sum / (float) 3);
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
outbuf1 -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state->dmr_stereo == 0)
|
|
||||||
{
|
|
||||||
if (state->audio_out_idx2 > 24)
|
|
||||||
{
|
|
||||||
// smoothing
|
|
||||||
outbuf1 -= 16;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
*outbuf1 = (sum / (float) 3);
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
outbuf1 -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
518
src/edacs-bch3.c
Normal file
518
src/edacs-bch3.c
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
/*
|
||||||
|
* File: bch3.h
|
||||||
|
* Title: Encoder/decoder for binary BCH codes in C (Version 3.1)
|
||||||
|
* Author: Robert Morelos-Zaragoza
|
||||||
|
* Date: August 1994-June 13, 1997
|
||||||
|
* Mod: December 14, 2021, for EDACS-FM BCH Polynomial Error Generation and Detection
|
||||||
|
* Mod2: October 2022, for DSD-FME port of EDACS-FM
|
||||||
|
* Source: www.eccpage.com
|
||||||
|
*
|
||||||
|
* =============== Encoder/Decoder for binary BCH codes in C =================
|
||||||
|
*
|
||||||
|
* Version 1: Original program. The user provides the generator polynomial
|
||||||
|
* of the code (cumbersome!).
|
||||||
|
* Version 2: Computes the generator polynomial of the code.
|
||||||
|
* Version 3: No need to input the coefficients of a primitive polynomial of
|
||||||
|
* degree m, used to construct the Galois Field GF(2**m). The
|
||||||
|
* program now works for any binary BCH code of length such that:
|
||||||
|
* 2**(m-1) - 1 < length <= 2**m - 1
|
||||||
|
*
|
||||||
|
* Note: You may have to change the size of the arrays to make it work.
|
||||||
|
*
|
||||||
|
* The encoding and decoding methods used in this program are based on the
|
||||||
|
* book "Error Control Coding: Fundamentals and Applications", by Lin and
|
||||||
|
* Costello, Prentice Hall, 1983.
|
||||||
|
*
|
||||||
|
* Thanks to Patrick Boyle (pboyle@era.com) for his observation that 'bch2.c'
|
||||||
|
* did not work for lengths other than 2**m-1 which led to this new version.
|
||||||
|
* Portions of this program are from 'rs.c', a Reed-Solomon encoder/decoder
|
||||||
|
* in C, written by Simon Rockliff (simon@augean.ua.oz.au) on 21/9/89. The
|
||||||
|
* previous version of the BCH encoder/decoder in C, 'bch2.c', was written by
|
||||||
|
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) on 5/19/92.
|
||||||
|
*
|
||||||
|
* NOTE:
|
||||||
|
* The author is not responsible for any malfunctioning of
|
||||||
|
* this program, nor for any damage caused by it. Please include the
|
||||||
|
* original program along with these comments in any redistribution.
|
||||||
|
*
|
||||||
|
* For more information, suggestions, or other ideas on implementing error
|
||||||
|
* correcting codes, please contact me at:
|
||||||
|
*
|
||||||
|
* Robert Morelos-Zaragoza
|
||||||
|
* 5120 Woodway, Suite 7036
|
||||||
|
* Houston, Texas 77056
|
||||||
|
*
|
||||||
|
* email: r.morelos-zaragoza@ieee.org
|
||||||
|
*
|
||||||
|
* COPYRIGHT NOTICE: This computer program is free for non-commercial purposes.
|
||||||
|
* You may implement this program for any non-commercial application. You may
|
||||||
|
* also implement this program for commercial purposes, provided that you
|
||||||
|
* obtain my written permission. Any modification of this program is covered
|
||||||
|
* by this copyright.
|
||||||
|
*
|
||||||
|
* == Copyright (c) 1994-7, Robert Morelos-Zaragoza. All rights reserved. ==
|
||||||
|
*
|
||||||
|
* m = order of the Galois field GF(2**m)
|
||||||
|
* n = 2**m - 1 = size of the multiplicative group of GF(2**m)
|
||||||
|
* length = length of the BCH code
|
||||||
|
* t = error correcting capability (max. no. of errors the code corrects)
|
||||||
|
* d = 2*t + 1 = designed min. distance = no. of consecutive roots of g(x) + 1
|
||||||
|
* k = n - deg(g(x)) = dimension (no. of information bits/codeword) of the code
|
||||||
|
* p[] = coefficients of a primitive polynomial used to generate GF(2**m)
|
||||||
|
* g[] = coefficients of the generator polynomial, g(x)
|
||||||
|
* alpha_to [] = log table of GF(2**m)
|
||||||
|
* index_of[] = antilog table of GF(2**m)
|
||||||
|
* ddata[] = information bits = coefficients of data polynomial, i(x)
|
||||||
|
* bb[] = coefficients of redundancy polynomial x^(length-k) i(x) modulo g(x)
|
||||||
|
* numerr = number of errors
|
||||||
|
* errpos[] = error positions
|
||||||
|
* recd[] = coefficients of the received polynomial
|
||||||
|
* decerror = number of decoding errors (in _message_ positions)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int m, n, length, k, t, d;
|
||||||
|
int p[21];
|
||||||
|
int alpha_to[1048576], index_of[1048576], g[548576];
|
||||||
|
int recd[1048576], ddata[1048576], bb[548576];
|
||||||
|
int seed;
|
||||||
|
int numerr, errpos[1024], decerror = 0;
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
read_p()
|
||||||
|
/*
|
||||||
|
* Read m, the degree of a primitive polynomial p(x) used to compute the
|
||||||
|
* Galois field GF(2**m). Get precomputed coefficients p[] of p(x). Read
|
||||||
|
* the code length.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int i, ninf;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
m = 6;
|
||||||
|
} while ( !(m>1) || !(m<21) );
|
||||||
|
for (i=1; i<m; i++)
|
||||||
|
p[i] = 0;
|
||||||
|
p[0] = p[m] = 1;
|
||||||
|
if (m == 2) p[1] = 1;
|
||||||
|
else if (m == 3) p[1] = 1;
|
||||||
|
else if (m == 4) p[1] = 1;
|
||||||
|
else if (m == 5) p[2] = 1;
|
||||||
|
else if (m == 6) p[1] = 1;
|
||||||
|
else if (m == 7) p[1] = 1;
|
||||||
|
else if (m == 8) p[4] = p[5] = p[6] = 1;
|
||||||
|
else if (m == 9) p[4] = 1;
|
||||||
|
else if (m == 10) p[3] = 1;
|
||||||
|
else if (m == 11) p[2] = 1;
|
||||||
|
else if (m == 12) p[3] = p[4] = p[7] = 1;
|
||||||
|
else if (m == 13) p[1] = p[3] = p[4] = 1;
|
||||||
|
else if (m == 14) p[1] = p[11] = p[12] = 1;
|
||||||
|
else if (m == 15) p[1] = 1;
|
||||||
|
else if (m == 16) p[2] = p[3] = p[5] = 1;
|
||||||
|
else if (m == 17) p[3] = 1;
|
||||||
|
else if (m == 18) p[7] = 1;
|
||||||
|
else if (m == 19) p[1] = p[5] = p[6] = 1;
|
||||||
|
else if (m == 20) p[3] = 1;
|
||||||
|
|
||||||
|
n = 1;
|
||||||
|
for (i = 0; i <= m; i++) {
|
||||||
|
n *= 2;
|
||||||
|
}
|
||||||
|
n = n / 2 - 1;
|
||||||
|
ninf = (n + 1) / 2 - 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
length = 40;
|
||||||
|
} while ( !((length <= n)&&(length>ninf)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
generate_gf()
|
||||||
|
/*
|
||||||
|
* Generate field GF(2**m) from the irreducible polynomial p(X) with
|
||||||
|
* coefficients in p[0]..p[m].
|
||||||
|
*
|
||||||
|
* Lookup tables:
|
||||||
|
* index->polynomial form: alpha_to[] contains j=alpha^i;
|
||||||
|
* polynomial form -> index form: index_of[j=alpha^i] = i
|
||||||
|
*
|
||||||
|
* alpha=2 is the primitive element of GF(2**m)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
register int i, mask;
|
||||||
|
|
||||||
|
mask = 1;
|
||||||
|
alpha_to[m] = 0;
|
||||||
|
for (i = 0; i < m; i++) {
|
||||||
|
alpha_to[i] = mask;
|
||||||
|
index_of[alpha_to[i]] = i;
|
||||||
|
if (p[i] != 0)
|
||||||
|
alpha_to[m] ^= mask;
|
||||||
|
mask <<= 1;
|
||||||
|
}
|
||||||
|
index_of[alpha_to[m]] = m;
|
||||||
|
mask >>= 1;
|
||||||
|
for (i = m + 1; i < n; i++) {
|
||||||
|
if (alpha_to[i - 1] >= mask)
|
||||||
|
alpha_to[i] = alpha_to[m] ^ ((alpha_to[i - 1] ^ mask) << 1);
|
||||||
|
else
|
||||||
|
alpha_to[i] = alpha_to[i - 1] << 1;
|
||||||
|
index_of[alpha_to[i]] = i;
|
||||||
|
}
|
||||||
|
index_of[0] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
gen_poly()
|
||||||
|
/*
|
||||||
|
* Compute the generator polynomial of a binary BCH code. Fist generate the
|
||||||
|
* cycle sets modulo 2**m - 1, cycle[][] = (i, 2*i, 4*i, ..., 2^l*i). Then
|
||||||
|
* determine those cycle sets that contain integers in the set of (d-1)
|
||||||
|
* consecutive integers {1..(d-1)}. The generator polynomial is calculated
|
||||||
|
* as the product of linear factors of the form (x+alpha^i), for every i in
|
||||||
|
* the above cycle sets.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
register int ii, jj, ll, kaux;
|
||||||
|
register int test, aux, nocycles, root, noterms, rdncy;
|
||||||
|
int cycle[1024][21], size[1024], min[1024], zeros[1024];
|
||||||
|
|
||||||
|
/* Generate cycle sets modulo n, n = 2**m - 1 */
|
||||||
|
cycle[0][0] = 0;
|
||||||
|
size[0] = 1;
|
||||||
|
cycle[1][0] = 1;
|
||||||
|
size[1] = 1;
|
||||||
|
jj = 1; /* cycle set index */
|
||||||
|
if (m > 9) {
|
||||||
|
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
/* Generate the jj-th cycle set */
|
||||||
|
ii = 0;
|
||||||
|
do {
|
||||||
|
ii++;
|
||||||
|
cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % n;
|
||||||
|
size[jj]++;
|
||||||
|
aux = (cycle[jj][ii] * 2) % n;
|
||||||
|
} while (aux != cycle[jj][0]);
|
||||||
|
/* Next cycle set representative */
|
||||||
|
ll = 0;
|
||||||
|
do {
|
||||||
|
ll++;
|
||||||
|
test = 0;
|
||||||
|
for (ii = 1; ((ii <= jj) && (!test)); ii++)
|
||||||
|
/* Examine previous cycle sets */
|
||||||
|
for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++)
|
||||||
|
if (ll == cycle[ii][kaux])
|
||||||
|
test = 1;
|
||||||
|
} while ((test) && (ll < (n - 1)));
|
||||||
|
if (!(test)) {
|
||||||
|
jj++; /* next cycle set index */
|
||||||
|
cycle[jj][0] = ll;
|
||||||
|
size[jj] = 1;
|
||||||
|
}
|
||||||
|
} while (ll < (n - 1));
|
||||||
|
nocycles = jj; /* number of cycle sets modulo n */
|
||||||
|
|
||||||
|
t = 2;
|
||||||
|
|
||||||
|
d = 2 * t + 1;
|
||||||
|
|
||||||
|
/* Search for roots 1, 2, ..., d-1 in cycle sets */
|
||||||
|
kaux = 0;
|
||||||
|
rdncy = 0;
|
||||||
|
for (ii = 1; ii <= nocycles; ii++) {
|
||||||
|
min[kaux] = 0;
|
||||||
|
test = 0;
|
||||||
|
for (jj = 0; ((jj < size[ii]) && (!test)); jj++)
|
||||||
|
for (root = 1; ((root < d) && (!test)); root++)
|
||||||
|
if (root == cycle[ii][jj]) {
|
||||||
|
test = 1;
|
||||||
|
min[kaux] = ii;
|
||||||
|
}
|
||||||
|
if (min[kaux]) {
|
||||||
|
rdncy += size[min[kaux]];
|
||||||
|
kaux++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noterms = kaux;
|
||||||
|
kaux = 1;
|
||||||
|
for (ii = 0; ii < noterms; ii++)
|
||||||
|
for (jj = 0; jj < size[min[ii]]; jj++) {
|
||||||
|
zeros[kaux] = cycle[min[ii]][jj];
|
||||||
|
kaux++;
|
||||||
|
}
|
||||||
|
|
||||||
|
k = length - rdncy;
|
||||||
|
|
||||||
|
if (k<0)
|
||||||
|
{
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute the generator polynomial */
|
||||||
|
g[0] = alpha_to[zeros[1]];
|
||||||
|
g[1] = 1; /* g(x) = (X + zeros[1]) initially */
|
||||||
|
for (ii = 2; ii <= rdncy; ii++) {
|
||||||
|
g[ii] = 1;
|
||||||
|
for (jj = ii - 1; jj > 0; jj--)
|
||||||
|
if (g[jj] != 0)
|
||||||
|
g[jj] = g[jj - 1] ^ alpha_to[(index_of[g[jj]] + zeros[ii]) % n];
|
||||||
|
else
|
||||||
|
g[jj] = g[jj - 1];
|
||||||
|
g[0] = alpha_to[(index_of[g[0]] + zeros[ii]) % n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
encode_bch()
|
||||||
|
/*
|
||||||
|
* Compute redundacy bb[], the coefficients of b(x). The redundancy
|
||||||
|
* polynomial b(x) is the remainder after dividing x^(length-k)*ddata(x)
|
||||||
|
* by the generator polynomial g(x).
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
register int i, j;
|
||||||
|
register int feedback;
|
||||||
|
|
||||||
|
for (i = 0; i < length - k; i++)
|
||||||
|
bb[i] = 0;
|
||||||
|
for (i = k - 1; i >= 0; i--) {
|
||||||
|
feedback = ddata[i] ^ bb[length - k - 1];
|
||||||
|
if (feedback != 0) {
|
||||||
|
for (j = length - k - 1; j > 0; j--)
|
||||||
|
if (g[j] != 0)
|
||||||
|
bb[j] = bb[j - 1] ^ feedback;
|
||||||
|
else
|
||||||
|
bb[j] = bb[j - 1];
|
||||||
|
bb[0] = g[0] && feedback;
|
||||||
|
} else {
|
||||||
|
for (j = length - k - 1; j > 0; j--)
|
||||||
|
bb[j] = bb[j - 1];
|
||||||
|
bb[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
decode_bch()
|
||||||
|
/*
|
||||||
|
* Simon Rockliff's implementation of Berlekamp's algorithm.
|
||||||
|
*
|
||||||
|
* Assume we have received bits in recd[i], i=0..(n-1).
|
||||||
|
*
|
||||||
|
* Compute the 2*t syndromes by substituting alpha^i into rec(X) and
|
||||||
|
* evaluating, storing the syndromes in s[i], i=1..2t (leave s[0] zero) .
|
||||||
|
* Then we use the Berlekamp algorithm to find the error location polynomial
|
||||||
|
* elp[i].
|
||||||
|
*
|
||||||
|
* If the degree of the elp is >t, then we cannot correct all the errors, and
|
||||||
|
* we have detected an uncorrectable error pattern. We output the information
|
||||||
|
* bits uncorrected.
|
||||||
|
*
|
||||||
|
* If the degree of elp is <=t, we substitute alpha^i , i=1..n into the elp
|
||||||
|
* to get the roots, hence the inverse roots, the error location numbers.
|
||||||
|
* This step is usually called "Chien's search".
|
||||||
|
*
|
||||||
|
* If the number of errors located is not equal the degree of the elp, then
|
||||||
|
* the decoder assumes that there are more than t errors and cannot correct
|
||||||
|
* them, only detect them. We output the information bits uncorrected.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
register int i, j, u, q, t2, count = 0, syn_error = 0;
|
||||||
|
int elp[1026][1024], d[1026], l[1026], u_lu[1026], s[1025];
|
||||||
|
int root[200], loc[200], err[1024], reg[201];
|
||||||
|
|
||||||
|
t2 = 2 * t;
|
||||||
|
|
||||||
|
/* first form the syndromes */
|
||||||
|
for (i = 1; i <= t2; i++) {
|
||||||
|
s[i] = 0;
|
||||||
|
for (j = 0; j < length; j++)
|
||||||
|
if (recd[j] != 0)
|
||||||
|
s[i] ^= alpha_to[(i * j) % n];
|
||||||
|
if (s[i] != 0)
|
||||||
|
syn_error = 1; /* set error flag if non-zero syndrome */
|
||||||
|
/*
|
||||||
|
* Note: If the code is used only for ERROR DETECTION, then
|
||||||
|
* exit program here indicating the presence of errors.
|
||||||
|
*/
|
||||||
|
/* convert syndrome from polynomial form to index form */
|
||||||
|
s[i] = index_of[s[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syn_error) { /* if there are errors, try to correct them */
|
||||||
|
/*
|
||||||
|
* Compute the error location polynomial via the Berlekamp
|
||||||
|
* iterative algorithm. Following the terminology of Lin and
|
||||||
|
* Costello's book : d[u] is the 'mu'th discrepancy, where
|
||||||
|
* u='mu'+1 and 'mu' (the Greek letter!) is the step number
|
||||||
|
* ranging from -1 to 2*t (see L&C), l[u] is the degree of
|
||||||
|
* the elp at that step, and u_l[u] is the difference between
|
||||||
|
* the step number and the degree of the elp.
|
||||||
|
*/
|
||||||
|
/* initialise table entries */
|
||||||
|
d[0] = 0; /* index form */
|
||||||
|
d[1] = s[1]; /* index form */
|
||||||
|
elp[0][0] = 0; /* index form */
|
||||||
|
elp[1][0] = 1; /* polynomial form */
|
||||||
|
for (i = 1; i < t2; i++) {
|
||||||
|
elp[0][i] = -1; /* index form */
|
||||||
|
elp[1][i] = 0; /* polynomial form */
|
||||||
|
}
|
||||||
|
l[0] = 0;
|
||||||
|
l[1] = 0;
|
||||||
|
u_lu[0] = -1;
|
||||||
|
u_lu[1] = 0;
|
||||||
|
u = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
u++;
|
||||||
|
if (d[u] == -1) {
|
||||||
|
l[u + 1] = l[u];
|
||||||
|
for (i = 0; i <= l[u]; i++) {
|
||||||
|
elp[u + 1][i] = elp[u][i];
|
||||||
|
elp[u][i] = index_of[elp[u][i]];
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
/*
|
||||||
|
* search for words with greatest u_lu[q] for
|
||||||
|
* which d[q]!=0
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
q = u - 1;
|
||||||
|
while ((d[q] == -1) && (q > 0))
|
||||||
|
q--;
|
||||||
|
/* have found first non-zero d[q] */
|
||||||
|
if (q > 0) {
|
||||||
|
j = q;
|
||||||
|
do {
|
||||||
|
j--;
|
||||||
|
if ((d[j] != -1) && (u_lu[q] < u_lu[j]))
|
||||||
|
q = j;
|
||||||
|
} while (j > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* have now found q such that d[u]!=0 and
|
||||||
|
* u_lu[q] is maximum
|
||||||
|
*/
|
||||||
|
/* store degree of new elp polynomial */
|
||||||
|
if (l[u] > l[q] + u - q)
|
||||||
|
l[u + 1] = l[u];
|
||||||
|
else
|
||||||
|
l[u + 1] = l[q] + u - q;
|
||||||
|
|
||||||
|
/* form new elp(x) */
|
||||||
|
for (i = 0; i < t2; i++)
|
||||||
|
elp[u + 1][i] = 0;
|
||||||
|
for (i = 0; i <= l[q]; i++)
|
||||||
|
if (elp[q][i] != -1)
|
||||||
|
elp[u + 1][i + u - q] =
|
||||||
|
alpha_to[(d[u] + n - d[q] + elp[q][i]) % n];
|
||||||
|
for (i = 0; i <= l[u]; i++) {
|
||||||
|
elp[u + 1][i] ^= elp[u][i];
|
||||||
|
elp[u][i] = index_of[elp[u][i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u_lu[u + 1] = u - l[u + 1];
|
||||||
|
|
||||||
|
/* form (u+1)th discrepancy */
|
||||||
|
if (u < t2) {
|
||||||
|
/* no discrepancy computed on last iteration */
|
||||||
|
if (s[u + 1] != -1)
|
||||||
|
d[u + 1] = alpha_to[s[u + 1]];
|
||||||
|
else
|
||||||
|
d[u + 1] = 0;
|
||||||
|
for (i = 1; i <= l[u + 1]; i++)
|
||||||
|
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
|
||||||
|
d[u + 1] ^= alpha_to[(s[u + 1 - i]
|
||||||
|
+ index_of[elp[u + 1][i]]) % n];
|
||||||
|
/* put d[u+1] into index form */
|
||||||
|
d[u + 1] = index_of[d[u + 1]];
|
||||||
|
}
|
||||||
|
} while ((u < t2) && (l[u + 1] <= t));
|
||||||
|
|
||||||
|
u++;
|
||||||
|
if (l[u] <= t) {/* Can correct errors */
|
||||||
|
/* put elp into index form */
|
||||||
|
for (i = 0; i <= l[u]; i++)
|
||||||
|
elp[u][i] = index_of[elp[u][i]];
|
||||||
|
|
||||||
|
for (i = 0; i <= l[u]; i++)
|
||||||
|
|
||||||
|
/* Chien search: find roots of the error location polynomial */
|
||||||
|
for (i = 1; i <= l[u]; i++)
|
||||||
|
reg[i] = elp[u][i];
|
||||||
|
count = 0;
|
||||||
|
for (i = 1; i <= n; i++) {
|
||||||
|
q = 1;
|
||||||
|
for (j = 1; j <= l[u]; j++)
|
||||||
|
if (reg[j] != -1) {
|
||||||
|
reg[j] = (reg[j] + j) % n;
|
||||||
|
q ^= alpha_to[reg[j]];
|
||||||
|
}
|
||||||
|
if (!q) { /* store root and error
|
||||||
|
* location number indices */
|
||||||
|
root[count] = i;
|
||||||
|
loc[count] = n - i;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == l[u])
|
||||||
|
/* no. roots = degree of elp hence <= t errors */
|
||||||
|
for (i = 0; i < l[u]; i++)
|
||||||
|
recd[loc[i]] ^= 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//bugfix on 32-bit cygwin, probably overflowed here
|
||||||
|
unsigned long long int messagepp = 0x0;
|
||||||
|
|
||||||
|
//very simplified version, just to encode, get frame and compare
|
||||||
|
unsigned long long int edacs_bch (unsigned long long int message)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
//not ideal to run these every frame, will want to eventually run once on start of DSD-FME, and store values
|
||||||
|
read_p(); /* Read m */
|
||||||
|
generate_gf(); /* Construct the Galois Field GF(2**m) */
|
||||||
|
gen_poly(); /* Compute the generator polynomial of BCH code */
|
||||||
|
//test shows this works, but output from program is that the poly is backwards, not sure if it corrects okay though
|
||||||
|
for (i = 0; i < k; i++){
|
||||||
|
ddata[i] = ( (message >> i) & 0x1 ); //loaded up backwards? or just outputs backwards?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
encode_bch(); /* encode data */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* recd[] are the coefficients of c(x) = x**(length-k)*data(x) + b(x)
|
||||||
|
*/
|
||||||
|
for (i = 0; i < length - k; i++)
|
||||||
|
recd[i] = bb[i];
|
||||||
|
for (i = 0; i < k; i++)
|
||||||
|
recd[i + length - k] = ddata[i];
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
messagepp = (messagepp << 1) | recd[39-i]; //get it in the correct direction
|
||||||
|
}
|
||||||
|
|
||||||
|
return (messagepp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
465
src/edacs-fme.c
Normal file
465
src/edacs-fme.c
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* EDACS-FME
|
||||||
|
* A program for decoding EDACS (ported to DSD-FME)
|
||||||
|
* https://github.com/lwvmobile/edacs-fm
|
||||||
|
*
|
||||||
|
* Portions of this software originally from:
|
||||||
|
* https://github.com/sp5wwp/ledacs
|
||||||
|
* XTAL Labs
|
||||||
|
* 30 IV 2016
|
||||||
|
* Many thanks to SP5WWP for permission to use and modify this software
|
||||||
|
*
|
||||||
|
* Encoder/decoder for binary BCH codes in C (Version 3.1)
|
||||||
|
* Robert Morelos-Zaragoza
|
||||||
|
* 1994-7
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-10 Version EDACS-FM Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
char * getDateE(void) {
|
||||||
|
char datename[99]; //might honestly just need a much smaller size, and not a larger one
|
||||||
|
char * curr2;
|
||||||
|
struct tm * to;
|
||||||
|
time_t t;
|
||||||
|
t = time(NULL);
|
||||||
|
to = localtime( & t);
|
||||||
|
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
|
||||||
|
curr2 = strtok(datename, " ");
|
||||||
|
return curr2;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * getTimeE(void) //get pretty hh:mm:ss timestamp
|
||||||
|
{
|
||||||
|
time_t t = time(NULL);
|
||||||
|
|
||||||
|
char * curr;
|
||||||
|
char * stamp = asctime(localtime( & t));
|
||||||
|
|
||||||
|
curr = strtok(stamp, " ");
|
||||||
|
curr = strtok(NULL, " ");
|
||||||
|
curr = strtok(NULL, " ");
|
||||||
|
curr = strtok(NULL, " ");
|
||||||
|
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void edacs(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
//changed to ulli here for 32-bit cygwin (not sure if was an issue, but playing it safe)
|
||||||
|
unsigned long long int fr_1 = 0xFFFFFFFFFF; //40-bit frames
|
||||||
|
unsigned long long int fr_2 = 0; //each is a 40 bit frame that repeats 3 times
|
||||||
|
unsigned long long int fr_3 = 0; //two messages per frame
|
||||||
|
unsigned long long int fr_4 = 0xFFFFFFFFFF;
|
||||||
|
unsigned long long int fr_5 = 0;
|
||||||
|
unsigned long long int fr_6 = 0;
|
||||||
|
|
||||||
|
//BCH stuff
|
||||||
|
unsigned long long int fr_1m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler
|
||||||
|
unsigned long long int fr_1t = 0xFFFFFFFFFF; //40 bit return from BCH with poly attached
|
||||||
|
unsigned long long int fr_4m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler
|
||||||
|
unsigned long long int fr_4t = 0xFFFFFFFFFF; //40 bit return from BCH with poly attached
|
||||||
|
|
||||||
|
unsigned char command = 0xFF;
|
||||||
|
unsigned char mt1 = 0x1F;
|
||||||
|
unsigned char mt2 = 0xF;
|
||||||
|
unsigned char mta = 0;
|
||||||
|
unsigned char lcn = 0;
|
||||||
|
|
||||||
|
//commands; may not use these anymore
|
||||||
|
unsigned int vcmd = 0xEE; //voice command variable
|
||||||
|
unsigned int idcmd = 0xFD;
|
||||||
|
unsigned int peercmd = 0xF88; //using for EA detection test
|
||||||
|
unsigned int netcmd = 0xF3; //using for Networked Test
|
||||||
|
|
||||||
|
state->edacs_vc_lcn = -1; //init on negative for ncurses and tuning
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
edacs_bit[i] = getDibit (opts, state); //getDibit returns binary 0 or 1 on GFSK signal (Edacs and PV)
|
||||||
|
}
|
||||||
|
|
||||||
|
//push the edacs_bit array into fr format from EDACS-FM
|
||||||
|
for (i = 0; i < 40; i++)
|
||||||
|
{
|
||||||
|
//only fr_1 and fr4 are going to matter
|
||||||
|
fr_1 = fr_1 << 1;
|
||||||
|
fr_1 = fr_1 | edacs_bit[i];
|
||||||
|
fr_2 = fr_2 << 1;
|
||||||
|
fr_2 = fr_2 | edacs_bit[i+40];
|
||||||
|
fr_3 = fr_3 << 1;
|
||||||
|
fr_3 = fr_3 | edacs_bit[i+80];
|
||||||
|
|
||||||
|
fr_4 = fr_4 << 1;
|
||||||
|
fr_4 = fr_4 | edacs_bit[i+120];
|
||||||
|
fr_5 = fr_5 << 1;
|
||||||
|
fr_5 = fr_5 | edacs_bit[i+160];
|
||||||
|
fr_6 = fr_6 << 1;
|
||||||
|
fr_6 = fr_6 | edacs_bit[i+200];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fr_1 = fr_1 & 0xFFFFFFFFFF;
|
||||||
|
fr_4 = fr_4 & 0xFFFFFFFFFF;
|
||||||
|
|
||||||
|
fr_1m = (fr_1 & 0xFFFFFFF000) >> 12;
|
||||||
|
fr_4m = (fr_4 & 0xFFFFFFF000) >> 12;
|
||||||
|
|
||||||
|
//take our message and create a new crc for it, if it matches the old crc, then we have a good frame
|
||||||
|
fr_1t = edacs_bch (fr_1m);
|
||||||
|
fr_4t = edacs_bch (fr_4m);
|
||||||
|
|
||||||
|
fr_1t = fr_1t & 0xFFFFFFFFFF;
|
||||||
|
fr_4t = fr_4t & 0xFFFFFFFFFF;
|
||||||
|
|
||||||
|
if (fr_1 != fr_1t || fr_4 != fr_4t)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " BCH FAIL ");
|
||||||
|
}
|
||||||
|
else //BCH Pass, continue from here.
|
||||||
|
{
|
||||||
|
|
||||||
|
//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) )
|
||||||
|
{
|
||||||
|
//experimenting with values here, not too high, and not too low
|
||||||
|
if ( (((fr_1t & 0xF000000000) >> 36) <= 0x8 ))
|
||||||
|
{
|
||||||
|
state->esk_mask = 0xA0;
|
||||||
|
}
|
||||||
|
//ideal value would be 5, but some other values exist that don't allow it
|
||||||
|
if ( (((fr_1t & 0xF000000000) >> 36) > 0x8 ) )
|
||||||
|
{
|
||||||
|
state->esk_mask = 0x0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Standard/Networked Auto Detection
|
||||||
|
//if (command == netcmd) //netcmd is F3 Standard/Networked (I think)
|
||||||
|
if ( (fr_1t >> 32 == netcmd) || (fr_1t >> 32 == (netcmd ^ 0xA0)) )
|
||||||
|
{
|
||||||
|
state->ea_mode = 0; //disable extended addressing mode
|
||||||
|
}
|
||||||
|
|
||||||
|
//EA Auto detection //peercmd is 0xF88 peer site relay 0xFF80000000 >> 28
|
||||||
|
if (fr_1t >> 28 == peercmd || fr_1t >> 28 == (peercmd ^ 0xA00) )
|
||||||
|
{
|
||||||
|
state->ea_mode = 1; //enable extended addressing mode
|
||||||
|
}
|
||||||
|
|
||||||
|
//Start Extended Addressing Mode
|
||||||
|
if (state->ea_mode == 1)
|
||||||
|
{
|
||||||
|
command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask;
|
||||||
|
mt1 = (command & 0xF8) >> 3;
|
||||||
|
mt2 = (fr_1t & 0x780000000) >> 31;
|
||||||
|
lcn = (fr_1t & 0x3E0000000) >> 29; //only valid during calls, status
|
||||||
|
|
||||||
|
//Site ID
|
||||||
|
unsigned long long int site_id = 0; //we probably could just make this an int as well as the state variables
|
||||||
|
if (mt1 == 0x1F && mt2 == 0xA)
|
||||||
|
{
|
||||||
|
site_id = ((fr_1 & 0x1F000) >> 12) | ((fr_1 & 0x1F000000) >> 19);
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Site ID [%02llX][%03lld] Extended Addressing", site_id, site_id);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->edacs_site_id = site_id;
|
||||||
|
}
|
||||||
|
//Patch Groups
|
||||||
|
else if (mt1 == 0x1F && mt2 == 0xC)
|
||||||
|
{
|
||||||
|
int patch_site = ((fr_4t & 0xFF00000000) >> 32); //is site info valid, 0 for all sites? else patch only good on site listed?
|
||||||
|
int sourcep = ((fr_1t & 0xFFFF000) >> 12);
|
||||||
|
int targetp = ((fr_4t & 0xFFFF000) >> 12);
|
||||||
|
fprintf (stderr, " Patch -- Site [%d] Source [%d] Target [%d] ", patch_site, sourcep, targetp);
|
||||||
|
}
|
||||||
|
//Adjacent Sites
|
||||||
|
else if (mt1 == 0x1F && mt2 == 0x1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Adjacent Site");
|
||||||
|
if ( ((fr_1t & 0xFF000) >> 12) > 0 )
|
||||||
|
{
|
||||||
|
int adj = (fr_1t & 0xFF000) >> 12;
|
||||||
|
int adj_l = (fr_1t & 0x1F000000) >> 24;
|
||||||
|
fprintf (stderr, " [%02X][%03d] on CC LCN [%02d]", adj, adj, adj_l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Control Channel LCN
|
||||||
|
else if (mt1 == 0x1F && mt2 == 0x8)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Control Channel LCN");
|
||||||
|
if (((fr_4t >> 12) & 0x1F) != 0)
|
||||||
|
{
|
||||||
|
state->edacs_cc_lcn = ((fr_4t >> 12) & 0x1F);
|
||||||
|
if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26) //26, or 27. shouldn't matter don't think cc lcn will give a status lcn val
|
||||||
|
{
|
||||||
|
state->edacs_lcn_count = state->edacs_cc_lcn;
|
||||||
|
}
|
||||||
|
fprintf (stderr, " [%d]", state->edacs_cc_lcn);
|
||||||
|
|
||||||
|
//check for control channel lcn frequency if not provided in channel map or in the lcn list
|
||||||
|
if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0)
|
||||||
|
{
|
||||||
|
long int lcnfreq = 0;
|
||||||
|
//if using rigctl, we can ask for the currrent frequency
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
|
lcnfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||||
|
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
||||||
|
}
|
||||||
|
//if using rtl input, we can ask for the current frequency tuned
|
||||||
|
if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
lcnfreq = (long int)opts->rtlsdr_center_freq;
|
||||||
|
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//set trunking cc here so we know where to come back to
|
||||||
|
if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0)
|
||||||
|
{
|
||||||
|
state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//disabling kick command, data looks like its just FFFF, no actual values, can't verify accuracy
|
||||||
|
// else if (mt1 == 0x1F && mt2 == 0xB) //KICK LISTING for EA?? Unverified, but probably observed in Unitrunker way back when.
|
||||||
|
// {
|
||||||
|
// int kicked = (fr_4t & 0xFFFFF000) >> 12;
|
||||||
|
// fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||||
|
// fprintf (stderr, " FR_3 [%010llX]", fr_3);
|
||||||
|
// fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||||
|
// fprintf (stderr, " FR_6 [%010llX]", fr_6);
|
||||||
|
// fprintf (stderr, " Kick Command?");
|
||||||
|
// }
|
||||||
|
//Voice Call Grant Update
|
||||||
|
// mt1 0x3 is Digital group voice call, 0x2 Group Data Channel, 0x1 TDMA call
|
||||||
|
else if (mt1 >= 0x1 && mt1 <= 0x3)
|
||||||
|
{
|
||||||
|
//LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc"
|
||||||
|
if (lcn > state->edacs_lcn_count && lcn < 26)
|
||||||
|
{
|
||||||
|
state->edacs_lcn_count = lcn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int group = (fr_1t & 0xFFFF000) >> 12;
|
||||||
|
int source = (fr_4t & 0xFFFFF000) >> 12;
|
||||||
|
if (group != 0) state->lasttg = group;
|
||||||
|
if (source != 0) state->lastsrc = source;
|
||||||
|
if (lcn != 0) state->edacs_vc_lcn = lcn;
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, " Group [%05d] Source [%08d] LCN[%02d]", group, source, lcn);
|
||||||
|
|
||||||
|
char mode[8]; //allow, block, digital enc
|
||||||
|
|
||||||
|
//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");
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mt1 == 0x1) fprintf (stderr, " TDMA Call"); //never observed, wonder if any EDACS systems ever carried a TDMA signal (X2-TDMA?)
|
||||||
|
if (mt1 == 0x2) fprintf (stderr, " Group Data Call"); //Never Seen this one before
|
||||||
|
if (mt1 == 0x3) fprintf (stderr, " Digital Call"); //ProVoice, this is what we always get on SLERS EA
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
//openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl)
|
||||||
|
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
|
||||||
|
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 the lcn index starts at zero
|
||||||
|
state->edacs_tuned_lcn = lcn;
|
||||||
|
opts->p25_is_tuned = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->audio_in_type == 3) //rtl dongle
|
||||||
|
{
|
||||||
|
rtl_udp_tune(opts, state, state->trunk_lcn_freq[lcn-1]);
|
||||||
|
state->edacs_tuned_lcn = lcn;
|
||||||
|
opts->p25_is_tuned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //print frames for debug/analysis
|
||||||
|
{
|
||||||
|
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||||
|
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||||
|
fprintf (stderr, " Unknown Command");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//Start Standard or Networked Mode
|
||||||
|
else if (state->ea_mode == 0)
|
||||||
|
{
|
||||||
|
//standard or networked
|
||||||
|
command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask;
|
||||||
|
lcn = (fr_1t & 0xF8000000) >> 27;
|
||||||
|
|
||||||
|
//site ID and CC LCN
|
||||||
|
if (command == 0xFD)
|
||||||
|
{
|
||||||
|
int site_id = (fr_1t & 0xFF000) >> 12;
|
||||||
|
int cc_lcn = (fr_1t & 0x1F000000) >> 24;
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Site ID [%02X][%03d] on CC LCN [%02d] Standard/Networked", site_id, site_id, cc_lcn);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->edacs_site_id = site_id;
|
||||||
|
state->edacs_cc_lcn = cc_lcn;
|
||||||
|
|
||||||
|
if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26)
|
||||||
|
{
|
||||||
|
state->edacs_lcn_count = state->edacs_cc_lcn;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check for control channel lcn frequency if not provided in channel map or in the lcn list
|
||||||
|
if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0)
|
||||||
|
{
|
||||||
|
long int lcnfreq = 0;
|
||||||
|
//if using rigctl, we can ask for the currrent frequency
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
|
lcnfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||||
|
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
||||||
|
}
|
||||||
|
//if using rtl input, we can ask for the current frequency tuned
|
||||||
|
if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
lcnfreq = (long int)opts->rtlsdr_center_freq;
|
||||||
|
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//set trunking cc here so we know where to come back to
|
||||||
|
if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0)
|
||||||
|
{
|
||||||
|
state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//voice call assignment
|
||||||
|
else if (command == 0xEE || command == 0xEF)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (lcn > state->edacs_lcn_count && lcn < 26)
|
||||||
|
{
|
||||||
|
state->edacs_lcn_count = lcn;
|
||||||
|
}
|
||||||
|
state->edacs_vc_lcn = lcn;
|
||||||
|
//just going to use the default 4-4-3 A-FS scheme, making it user configurable would be a pain
|
||||||
|
int afs = (fr_1t & 0x7FF000) >> 12;
|
||||||
|
int a = afs >> 7;
|
||||||
|
int fs = afs & 0x7F;
|
||||||
|
int status = (fr_1t & 0xF00000000) >> 32;
|
||||||
|
if (afs > 0) state->lastsrc = afs;
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, " AFS [0x%03X] [%02d-%03d] LCN [%02d]", afs, a, fs, lcn);
|
||||||
|
|
||||||
|
char mode[8]; //allow, block, digital enc
|
||||||
|
|
||||||
|
//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");
|
||||||
|
|
||||||
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
|
{
|
||||||
|
if (state->group_array[i].groupNumber == afs)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||||
|
strcpy (mode, state->group_array[i].groupMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command == 0xEE)
|
||||||
|
{
|
||||||
|
//no handling for raw audio yet...that works properly atleast
|
||||||
|
fprintf (stderr, " Analog");
|
||||||
|
}
|
||||||
|
else //Digital Call (ProVoice, hopefully not Aegis in 2022)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
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->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
|
||||||
|
state->edacs_tuned_lcn = lcn;
|
||||||
|
opts->p25_is_tuned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->audio_in_type == 3) //rtl dongle
|
||||||
|
{
|
||||||
|
rtl_udp_tune(opts, state, state->trunk_lcn_freq[lcn-1]);
|
||||||
|
state->edacs_tuned_lcn = lcn;
|
||||||
|
opts->p25_is_tuned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
//Network Command, Not sure of how to handle, but just show frames
|
||||||
|
else if (command == 0xF3)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||||
|
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||||
|
fprintf (stderr, " Network Command");
|
||||||
|
}
|
||||||
|
|
||||||
|
else //print frames for debug/analysis
|
||||||
|
{
|
||||||
|
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||||
|
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||||
|
fprintf (stderr, " Unknown Command");
|
||||||
|
}
|
||||||
|
|
||||||
|
} //end Standard or Networked
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -312,11 +312,11 @@ void map_isch()
|
|||||||
isch_map["fb063f092"] = 127;
|
isch_map["fb063f092"] = 127;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long int isch_lookup (unsigned long long int isch)
|
int isch_lookup (unsigned long long int isch)
|
||||||
{
|
{
|
||||||
map_isch(); //initialize the lookup map
|
map_isch(); //initialize the lookup map
|
||||||
char s[64];
|
char s[64];
|
||||||
unsigned long long int decoded = -2; //initialize lookup to an invalid number
|
int decoded = -2; //initialize lookup to an invalid number
|
||||||
sprintf(s, "%llx", isch);
|
sprintf(s, "%llx", isch);
|
||||||
decoded = isch_map[s];
|
decoded = isch_map[s];
|
||||||
return(decoded);
|
return(decoded);
|
||||||
|
|||||||
124
src/nxdn96.c
124
src/nxdn96.c
@@ -1,124 +0,0 @@
|
|||||||
#include "dsd.h"
|
|
||||||
#include "nxdn96_const.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processNXDN96 (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
int i, j, k, dibit;
|
|
||||||
|
|
||||||
char ambe_fr[4][24];
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "VOICE e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (k = 0; k < 4; k++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 222; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, "%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, " ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (k < 3)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
w = nW;
|
|
||||||
x = nX;
|
|
||||||
y = nY;
|
|
||||||
z = nZ;
|
|
||||||
for (i = 0; i < 36; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, "%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, " ");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (j = 0; j < 3; j++) // we skip the last voice frame until frame sync can work with < 24 symbols
|
|
||||||
{
|
|
||||||
w = nW;
|
|
||||||
x = nX;
|
|
||||||
y = nY;
|
|
||||||
z = nZ;
|
|
||||||
for (i = 0; i < 36; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, "%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, " ");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k < 3)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, "%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, " ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 0; i < 30; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, "%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -24,10 +24,8 @@
|
|||||||
|
|
||||||
|
|
||||||
/* Include ------------------------------------------------------------------*/
|
/* Include ------------------------------------------------------------------*/
|
||||||
//#include "nxdnconvolution.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
//#include <assert.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@@ -51,27 +49,16 @@ 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_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_newMetrics = m_metrics2;
|
|
||||||
m_dp = m_decisions;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
|
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
|
||||||
{
|
{
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
@@ -169,3 +156,17 @@ void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsign
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CNXDNConvolution_start(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
m_oldMetrics = m_metrics1;
|
||||||
|
m_newMetrics = m_metrics2;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
127
src/nxdn_data.c
127
src/nxdn_data.c
@@ -1,127 +0,0 @@
|
|||||||
#include "dsd.h"
|
|
||||||
#include "nxdn_const.h"
|
|
||||||
|
|
||||||
void processNXDNData (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
int i, dibit = 0;
|
|
||||||
unsigned char sacch_raw[72] = {0};
|
|
||||||
unsigned char sacch_decoded[32] = {0}; /* 26 bit + 6 bit CRC */
|
|
||||||
unsigned char *pr;
|
|
||||||
uint8_t CrcIsGood = 0;
|
|
||||||
uint8_t StructureField = 0;
|
|
||||||
uint8_t RAN = 0;
|
|
||||||
uint8_t PartOfFrame = 0;
|
|
||||||
|
|
||||||
/* Remove compiler warning */
|
|
||||||
//UNUSED_VARIABLE(dibit);
|
|
||||||
|
|
||||||
//if using NXDN Scrambling, run LFSRN (dummy ambe values) to continue seed
|
|
||||||
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
|
||||||
{
|
|
||||||
char ambe_temp[49] = {0};
|
|
||||||
char ambe_d[49] = {0};
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
LFSRN(ambe_temp, ambe_d, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "DATA - ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start pseudo-random NXDN sequence after
|
|
||||||
* LICH = 16 bit = 8 dibit
|
|
||||||
* ==> Index 8 */
|
|
||||||
pr = (unsigned char *)(&nxdnpr2[8]);
|
|
||||||
for (i = 0; i < 30; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
|
|
||||||
/* Store and de-interleave directly SACCH bits */
|
|
||||||
sacch_raw[(2*i)] = ((*pr) & 1) ^ (1 & (dibit >> 1)); // bit 1
|
|
||||||
sacch_raw[(2*i)+1] = (1 & dibit); // bit 0
|
|
||||||
pr++;
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf(stderr, "%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf(stderr, " ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Decode the SACCH fields */
|
|
||||||
CrcIsGood = NXDN_SACCH_raw_part_decode(sacch_raw, sacch_decoded);
|
|
||||||
StructureField = (sacch_decoded[0]<<1 | sacch_decoded[1]<<0) & 0x03; /* Compute the Structure Field (remove 2 first bits of the SR Information in the SACCH) */
|
|
||||||
RAN = (sacch_decoded[2]<<5 | sacch_decoded[3]<<4 | sacch_decoded[4]<<3 | sacch_decoded[5]<<2 | sacch_decoded[6]<<1 | sacch_decoded[7]<<0) & 0x3F; /* Compute the RAN (6 last bits of the SR Information in the SACCH) */
|
|
||||||
|
|
||||||
/* Compute the Part of Frame according to the structure field */
|
|
||||||
if (StructureField == 3) PartOfFrame = 0;
|
|
||||||
else if(StructureField == 2) PartOfFrame = 1;
|
|
||||||
else if(StructureField == 1) PartOfFrame = 2;
|
|
||||||
else if(StructureField == 0) PartOfFrame = 3;
|
|
||||||
else PartOfFrame = 0; /* Normally we should never go here */
|
|
||||||
|
|
||||||
/* Fill the SACCH structure */
|
|
||||||
state->NxdnSacchRawPart[PartOfFrame].CrcIsGood = CrcIsGood;
|
|
||||||
state->NxdnSacchRawPart[PartOfFrame].StructureField = StructureField;
|
|
||||||
state->NxdnSacchRawPart[PartOfFrame].RAN = RAN;
|
|
||||||
|
|
||||||
memcpy(state->NxdnSacchRawPart[PartOfFrame].Data, &sacch_decoded[8], 18); /* Copy the 18 bits of SACCH content */
|
|
||||||
|
|
||||||
//fprintf(stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
|
|
||||||
if(CrcIsGood)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KCYN);
|
|
||||||
fprintf (stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
//fprintf (stderr, "(CRC OK) ");
|
|
||||||
state->nxdn_last_ran = RAN; //disable, try to grab this in voice instead
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, "(CRC ERR) ");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Decode the SACCH only when all 4 voice frame
|
|
||||||
* SACCH spare parts have been received */
|
|
||||||
if(PartOfFrame == 3)
|
|
||||||
{
|
|
||||||
/* Decode the entire SACCH content */
|
|
||||||
NXDN_SACCH_Full_decode(opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 144; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf(stderr, "%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset the SACCH CRCs when all 4 voice frame
|
|
||||||
* SACCH spare parts have been received */
|
|
||||||
if(PartOfFrame == 3)
|
|
||||||
{
|
|
||||||
/* Reset all CRCs of the SACCH */
|
|
||||||
for(i = 0; i < 4; i++) state->NxdnSacchRawPart[i].CrcIsGood = 0;
|
|
||||||
|
|
||||||
//reset Scrambler seed
|
|
||||||
if (state->nxdn_cipher_type == 0x1)
|
|
||||||
{
|
|
||||||
state->payload_miN = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} /* End processNXDNData() */
|
|
||||||
1072
src/nxdn_deperm.c
Normal file
1072
src/nxdn_deperm.c
Normal file
File diff suppressed because it is too large
Load Diff
1621
src/nxdn_element.c
Normal file
1621
src/nxdn_element.c
Normal file
File diff suppressed because it is too large
Load Diff
513
src/nxdn_frame.c
Normal file
513
src/nxdn_frame.c
Normal file
@@ -0,0 +1,513 @@
|
|||||||
|
//NXDN frame handler
|
||||||
|
//Reworked portions from Osmocom OP25 rx_sync.cc
|
||||||
|
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
#include "nxdn_const.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
// length is implicitly 192, with frame sync in first 10 dibits
|
||||||
|
uint8_t dbuf[182];
|
||||||
|
uint8_t lich;
|
||||||
|
int answer_len = 0;
|
||||||
|
uint8_t answer[32];
|
||||||
|
uint8_t sacch_answer[32];
|
||||||
|
int lich_parity_received;
|
||||||
|
int lich_parity_computed;
|
||||||
|
int voice = 0;
|
||||||
|
int facch = 0;
|
||||||
|
int facch2 = 0;
|
||||||
|
int udch = 0;
|
||||||
|
int sacch = 0;
|
||||||
|
int cac = 0;
|
||||||
|
int sr_structure;
|
||||||
|
int sr_ran;
|
||||||
|
|
||||||
|
//new, and even more confusing NXDN Type-D / "IDAS" acronyms
|
||||||
|
int idas = 0;
|
||||||
|
int scch = 0;
|
||||||
|
int facch3 = 0;
|
||||||
|
int udch2 = 0;
|
||||||
|
|
||||||
|
//new breakdown of lich codes
|
||||||
|
uint8_t lich_rf = 0; //RF Channel Type
|
||||||
|
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 amoount 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 (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
|
||||||
|
for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
||||||
|
|
||||||
|
nxdn_descramble (lich_dibits, 8);
|
||||||
|
|
||||||
|
lich = 0;
|
||||||
|
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
|
||||||
|
|
||||||
|
lich_parity_received = lich & 1;
|
||||||
|
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1;
|
||||||
|
lich = lich >> 1;
|
||||||
|
if (lich_parity_received != lich_parity_computed)
|
||||||
|
{
|
||||||
|
if (opts->payload == 1) fprintf(stderr, " Lich Parity Error %02X\n", lich);
|
||||||
|
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice = 0;
|
||||||
|
facch = 0;
|
||||||
|
facch2 = 0;
|
||||||
|
sacch = 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
|
||||||
|
case 0x01: // CAC types
|
||||||
|
case 0x05:
|
||||||
|
cac = 1;
|
||||||
|
break;
|
||||||
|
case 0x28: //facch2 types
|
||||||
|
case 0x29:
|
||||||
|
case 0x48:
|
||||||
|
case 0x49:
|
||||||
|
facch2 = 1;
|
||||||
|
break;
|
||||||
|
case 0x2e: //udch types
|
||||||
|
case 0x2f:
|
||||||
|
case 0x4e:
|
||||||
|
case 0x4f:
|
||||||
|
udch = 1;
|
||||||
|
break;
|
||||||
|
case 0x32: //facch in 1, vch in 2
|
||||||
|
case 0x33:
|
||||||
|
case 0x52:
|
||||||
|
case 0x53:
|
||||||
|
voice = 2;
|
||||||
|
facch = 1;
|
||||||
|
sacch = 1;
|
||||||
|
break;
|
||||||
|
case 0x34: //vch in 1, facch in 2
|
||||||
|
case 0x35:
|
||||||
|
case 0x54:
|
||||||
|
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
|
||||||
|
voice = 1;
|
||||||
|
facch = 2;
|
||||||
|
sacch = 1;
|
||||||
|
break;
|
||||||
|
case 0x36: //vch in both
|
||||||
|
case 0x37:
|
||||||
|
case 0x56:
|
||||||
|
case 0x57:
|
||||||
|
voice = 3;
|
||||||
|
facch = 0;
|
||||||
|
sacch = 1;
|
||||||
|
break;
|
||||||
|
case 0x20: //facch in both
|
||||||
|
case 0x21:
|
||||||
|
case 0x30:
|
||||||
|
case 0x31:
|
||||||
|
case 0x40:
|
||||||
|
case 0x41:
|
||||||
|
case 0x50:
|
||||||
|
case 0x51:
|
||||||
|
voice = 0;
|
||||||
|
facch = 3;
|
||||||
|
sacch = 1;
|
||||||
|
break;
|
||||||
|
case 0x38: //sacch only (NULL?)
|
||||||
|
case 0x39:
|
||||||
|
sacch = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//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:
|
||||||
|
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
|
||||||
|
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||||
|
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||||
|
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||||
|
voice = 0;
|
||||||
|
goto END;
|
||||||
|
break;
|
||||||
|
} // 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
|
||||||
|
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 || cac)
|
||||||
|
{
|
||||||
|
if (opts->frame_nxdn48 == 1)
|
||||||
|
{
|
||||||
|
printFrameSync (opts, state, "NXDN48 ", 0, "-");
|
||||||
|
}
|
||||||
|
else printFrameSync (opts, state, "NXDN96 ", 0, "-");
|
||||||
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "L%02X - ", lich);
|
||||||
|
}
|
||||||
|
|
||||||
|
//now that we have a good LICH, we can collect all of our dibits
|
||||||
|
//and push them to the proper places for decoding (if LICH calls for that type)
|
||||||
|
for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
|
||||||
|
{
|
||||||
|
dbuf[i+8] = getDibit(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
nxdn_descramble (dbuf, 182); //sizeof(dbuf)
|
||||||
|
|
||||||
|
//seperate our dbuf (dibit_buffer) into individual bit array
|
||||||
|
for (int i = 0; i < 182; i++)
|
||||||
|
{
|
||||||
|
nxdn_bit_buffer[i*2] = dbuf[i] >> 1;
|
||||||
|
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sacch or scch bits
|
||||||
|
for (int i = 0; i < 60; i++)
|
||||||
|
{
|
||||||
|
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
||||||
|
}
|
||||||
|
|
||||||
|
//facch
|
||||||
|
for (int i = 0; i < 144; i++)
|
||||||
|
{
|
||||||
|
facch_bits_a[i] = nxdn_bit_buffer[i+16+60];
|
||||||
|
facch_bits_b[i] = nxdn_bit_buffer[i+16+60+144];
|
||||||
|
}
|
||||||
|
|
||||||
|
//cac
|
||||||
|
for (int i = 0; i < 300; i++)
|
||||||
|
{
|
||||||
|
cac_bits[i] = nxdn_bit_buffer[i+16];
|
||||||
|
}
|
||||||
|
|
||||||
|
//udch or facch2
|
||||||
|
for (int i = 0; i < 348; i++)
|
||||||
|
{
|
||||||
|
facch2_bits[i] = nxdn_bit_buffer[i+16];
|
||||||
|
}
|
||||||
|
|
||||||
|
//udch2 or facch3
|
||||||
|
for (int i = 0; i < 288; i++)
|
||||||
|
{
|
||||||
|
facch3_bits[i] = nxdn_bit_buffer[i+16+60];
|
||||||
|
}
|
||||||
|
|
||||||
|
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
|
||||||
|
//sacch needs extra handling depending on superframe or non-superframe variety
|
||||||
|
|
||||||
|
//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
|
||||||
|
|
||||||
|
//Option/Steal Flags echoed in Voice, V+F, or Data
|
||||||
|
if (voice && !facch) //voice only, no facch steal
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, " Voice ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
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, " Data ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
char ambe_temp[49] = {0};
|
||||||
|
char ambe_d[49] = {0};
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
LFSRN(ambe_temp, ambe_d, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voice && facch == 1) //facch steal 1 -- before voice
|
||||||
|
{
|
||||||
|
//force scrambler here, but with unspecified key (just use what's loaded)
|
||||||
|
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||||
|
//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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
|
||||||
|
END: ; //do nothing
|
||||||
|
}
|
||||||
1692
src/nxdn_lib.c
1692
src/nxdn_lib.c
File diff suppressed because it is too large
Load Diff
216
src/nxdn_voice.c
216
src/nxdn_voice.c
@@ -1,169 +1,101 @@
|
|||||||
#include "dsd.h"
|
//a new and simplified voice handling routine for NXDN based on lich signalling
|
||||||
#include "nxdn_const.h"
|
/*
|
||||||
|
* Copyright (C) 2010 DSD Author
|
||||||
|
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* 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
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
void processNXDNVoice (dsd_opts * opts, dsd_state * state)
|
//Originally found at - https://github.com/LouisErigHerve/dsd
|
||||||
|
//Modified for use in DSD-FME
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 2,
|
||||||
|
0, 2, 0, 2, 0, 2,
|
||||||
|
0, 2, 0, 2, 0, 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
||||||
|
20, 7, 19, 6, 18, 5,
|
||||||
|
17, 4, 16, 3, 15, 2,
|
||||||
|
14, 1, 13, 0, 12, 10,
|
||||||
|
11, 9, 10, 8, 9, 7,
|
||||||
|
8, 6, 7, 5, 6, 4
|
||||||
|
};
|
||||||
|
|
||||||
|
const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
||||||
|
0, 2, 0, 3, 0, 3,
|
||||||
|
1, 3, 1, 3, 1, 3,
|
||||||
|
1, 3, 1, 3, 1, 3,
|
||||||
|
1, 3, 1, 3, 1, 3,
|
||||||
|
1, 3, 1, 3, 1, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
||||||
|
2, 0, 1, 13, 0, 12,
|
||||||
|
22, 11, 21, 10, 20, 9,
|
||||||
|
19, 8, 18, 7, 17, 6,
|
||||||
|
16, 5, 15, 4, 14, 3,
|
||||||
|
13, 2, 12, 1, 11, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182])
|
||||||
{
|
{
|
||||||
int i, j, dibit;
|
int i, j, dibit;
|
||||||
|
int start, stop;
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24];
|
||||||
unsigned char sacch_raw[72] = {0};
|
|
||||||
unsigned char sacch_decoded[32] = {0}; /* 26 bit + 6 bit CRC */
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
unsigned char *pr;
|
unsigned char *pr;
|
||||||
uint8_t CrcIsGood = 0;
|
const int *w, *x, *y, *z;
|
||||||
uint8_t StructureField = 0;
|
|
||||||
uint8_t RAN = 0;
|
|
||||||
uint8_t PartOfFrame = 0;
|
|
||||||
char ambe7bytesArray[7] = {0};
|
|
||||||
int PartOfEncryptedSuperFrame = 0;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
//these conditions will determine our starting and stopping value for voice
|
||||||
{
|
//i.e. voice in first, voice in second, or voice in both
|
||||||
fprintf(stderr, "VOICE - ");
|
if (voice == 1 || voice == 3) start = 0;
|
||||||
}
|
if (voice == 1) stop = 2;
|
||||||
|
if (voice == 2) start = 2;
|
||||||
|
if (voice == 2 || voice == 3) stop = 4;
|
||||||
|
|
||||||
/* Start pseudo-random NXDN sequence after
|
for (start; start < stop; start++)
|
||||||
* LICH = 16 bit = 8 dibit
|
|
||||||
* ==> Index 8 */
|
|
||||||
pr = (unsigned char *)(&nxdnpr2[8]);
|
|
||||||
for (i = 0; i < 30; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
|
|
||||||
/* Store and de-interleave directly SACCH bits */
|
|
||||||
sacch_raw[(2*i)] = ((*pr) & 1) ^ (1 & (dibit >> 1)); // bit 1
|
|
||||||
sacch_raw[(2*i)+1] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
// TODO : Deinterleave is done in the "NXDN_SACCH_decode" function
|
|
||||||
// /* Store and de-interleave directly SACCH bits */
|
|
||||||
// sacch_raw[nsW[2*i]] = *pr ^ (1 & (dibit >> 1)); // bit 1
|
|
||||||
// sacch_raw[nsW[2*i+1]] = (1 & dibit); // bit 0
|
|
||||||
pr++;
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf(stderr, "%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
fprintf(stderr, " ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO : Unpuncture is done in the "NXDN_SACCH_decode" function
|
|
||||||
// /* For the sake of simplicity, handle the re-insertion of punctured bits here.
|
|
||||||
// * This makes the actual Viterbi decoder code a LOT simpler. */
|
|
||||||
// for (i = 30; i < 36; i++)
|
|
||||||
// {
|
|
||||||
// sacch_raw[nsW[2*i]] = 0;
|
|
||||||
// sacch_raw[nsW[2*i+1]] = 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/* Decode the SACCH fields */
|
|
||||||
CrcIsGood = NXDN_SACCH_raw_part_decode(sacch_raw, sacch_decoded);
|
|
||||||
StructureField = (sacch_decoded[0]<<1 | sacch_decoded[1]<<0) & 0x03; /* Compute the Structure Field (remove 2 first bits of the SR Information in the SACCH) */
|
|
||||||
RAN = (sacch_decoded[2]<<5 | sacch_decoded[3]<<4 | sacch_decoded[4]<<3 | sacch_decoded[5]<<2 | sacch_decoded[6]<<1 | sacch_decoded[7]<<0) & 0x3F; /* Compute the RAN (6 last bits of the SR Information in the SACCH) */
|
|
||||||
|
|
||||||
/* Compute the Part of Frame according to the structure field */
|
|
||||||
if (StructureField == 3) PartOfFrame = 0;
|
|
||||||
else if(StructureField == 2) PartOfFrame = 1;
|
|
||||||
else if(StructureField == 1) PartOfFrame = 2;
|
|
||||||
else if(StructureField == 0) PartOfFrame = 3;
|
|
||||||
else PartOfFrame = 0; /* Normally we should never go here */
|
|
||||||
|
|
||||||
/* Fill the SACCH structure */
|
|
||||||
state->NxdnSacchRawPart[PartOfFrame].CrcIsGood = CrcIsGood;
|
|
||||||
state->NxdnSacchRawPart[PartOfFrame].StructureField = StructureField;
|
|
||||||
state->NxdnSacchRawPart[PartOfFrame].RAN = RAN;
|
|
||||||
memcpy(state->NxdnSacchRawPart[PartOfFrame].Data, &sacch_decoded[8], 18); /* Copy the 18 bits of SACCH content */
|
|
||||||
|
|
||||||
//fprintf(stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
|
|
||||||
//if(CrcIsGood) fprintf(stderr, " (OK) - ");
|
|
||||||
if(CrcIsGood)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KCYN);
|
|
||||||
fprintf (stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
//fprintf (stderr, "(CRC OK) ");
|
|
||||||
state->nxdn_last_ran = RAN; //disable, try to grab this in voice instead
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf(stderr, "(CRC ERR) ");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Determine the current part of superframe
|
|
||||||
* (in an AES or DES encrypted frame, two superframes
|
|
||||||
* are used with the same IV, so to decipher the sound
|
|
||||||
* correctly we must know the current superframe part) */
|
|
||||||
if(state->NxdnElementsContent.PartOfNextEncryptedFrame == 1) PartOfEncryptedSuperFrame = 0;
|
|
||||||
else if(state->NxdnElementsContent.PartOfNextEncryptedFrame == 2) PartOfEncryptedSuperFrame = 1;
|
|
||||||
else PartOfEncryptedSuperFrame = 0;
|
|
||||||
|
|
||||||
/* Decode the SACCH only when all 4 voice frame
|
|
||||||
* SACCH spare parts have been received */
|
|
||||||
if(PartOfFrame == 3)
|
|
||||||
{
|
|
||||||
/* Decode the entire SACCH content */
|
|
||||||
NXDN_SACCH_Full_decode(opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start pseudo-random NXDN sequence after
|
|
||||||
* LICH = 16 bit = 8 dibit +
|
|
||||||
* SACCH = 60 bit = 30 dibit
|
|
||||||
* = 76 bit = 38 dibit
|
|
||||||
* ==> Index 38 */
|
|
||||||
pr = (unsigned char *)(&nxdnpr2[38]);
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
{
|
||||||
w = nW;
|
w = nW;
|
||||||
x = nX;
|
x = nX;
|
||||||
y = nY;
|
y = nY;
|
||||||
z = nZ;
|
z = nZ;
|
||||||
|
|
||||||
for (i = 0; i < 36; i++)
|
for (i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
//skip 8 lich and 30 sacch dibits already in buffer plus 36 times start position
|
||||||
fprintf(stderr, "%c", dibit + 48);
|
ambe_fr[*w][*x] = dbuf[i+38+start*36] >> 1;
|
||||||
#endif
|
ambe_fr[*y][*z] = dbuf[i+38+start*36] & 1;
|
||||||
ambe_fr[*w][*x] = *pr ^ (1 & (dibit >> 1)); // bit 1
|
|
||||||
pr++;
|
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
|
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
y++;
|
y++;
|
||||||
z++;
|
z++;
|
||||||
}
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "\n");
|
|
||||||
}
|
}
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
state->p25vc++; //increment counter by one here?
|
}
|
||||||
|
|
||||||
} /* End for (j = 0; j < 4; j++) */
|
if (opts->payload == 1)
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset the SACCH CRCs when all 4 voice frame
|
|
||||||
* SACCH spare parts have been received */
|
|
||||||
if(PartOfFrame == 3)
|
|
||||||
{
|
|
||||||
/* Reset all CRCs of the SACCH */
|
|
||||||
if (state->R > 0 && state->nxdn_cipher_type == 0x1)
|
|
||||||
{
|
|
||||||
state->payload_miN = state->R;
|
|
||||||
}
|
|
||||||
for(i = 0; i < 4; i++) state->NxdnSacchRawPart[i].CrcIsGood = 0;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} /* End processNXDNVoice() */
|
|
||||||
@@ -2,12 +2,15 @@
|
|||||||
* p25_frequency.c
|
* p25_frequency.c
|
||||||
* P25 Channel to Frequency Calculator
|
* P25 Channel to Frequency Calculator
|
||||||
*
|
*
|
||||||
|
* NXDN Channel to Frequency Calculator
|
||||||
|
*
|
||||||
* LWVMOBILE
|
* LWVMOBILE
|
||||||
* 2022-09 DSD-FME Florida Man Edition
|
* 2022-11 DSD-FME Florida Man Edition
|
||||||
*-----------------------------------------------------------------------------*/
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//P25
|
||||||
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel)
|
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -29,16 +32,85 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
|
|||||||
int slots_per_carrier[16] = {1,1,1,2,4,2,2,2,2,2,2,2,2,2,2,2}; //from OP25
|
int slots_per_carrier[16] = {1,1,1,2,4,2,2,2,2,2,2,2,2,2,2,2}; //from OP25
|
||||||
int step = (channel & 0xFFF) / slots_per_carrier[type];
|
int step = (channel & 0xFFF) / slots_per_carrier[type];
|
||||||
|
|
||||||
if (state->p25_base_freq[iden] != 0)
|
//first, check channel map
|
||||||
|
if (state->trunk_chan_map[channel] != 0)
|
||||||
{
|
{
|
||||||
freq = (state->p25_base_freq[iden] * 5) + ( step * state->p25_chan_spac[iden] * 125);
|
freq = state->trunk_chan_map[channel];
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||||
return (freq);
|
return (freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if not found, attempt to find it via calculation
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n Base Frequency Not Found - Iden [%d]", iden);
|
if (state->p25_base_freq[iden] != 0)
|
||||||
return (0);
|
{
|
||||||
}
|
freq = (state->p25_base_freq[iden] * 5) + ( step * state->p25_chan_spac[iden] * 125);
|
||||||
|
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||||
|
return (freq);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n Base Frequency Not Found - Iden [%d]", iden);
|
||||||
|
fprintf(stderr, "\n or Channel not found in import file");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t channel)
|
||||||
|
{
|
||||||
|
long int freq;
|
||||||
|
long int base = 0;
|
||||||
|
long int step = 0;
|
||||||
|
|
||||||
|
//reworked to include Direct Frequency Assignment if available
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
freq = state->trunk_chan_map[channel];
|
||||||
|
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||||
|
return (freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
//then, let's see if its DFA instead -- 6.5.36
|
||||||
|
else if (state->nxdn_rcn == 1)
|
||||||
|
{
|
||||||
|
//determine the base frequency in Hz
|
||||||
|
if (state->nxdn_base_freq == 1) base = 100000000; //100 MHz
|
||||||
|
else if (state->nxdn_base_freq == 2) base = 330000000; //330 Mhz
|
||||||
|
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
|
||||||
|
|
||||||
|
//determine the step value in Hz
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n Custom DFA Settings -- Unknown Freq;");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n Channel not found in import file");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
470
src/p25_lcw.c
470
src/p25_lcw.c
@@ -1,160 +1,350 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* 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/Cancellation");
|
||||||
|
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
|
||||||
|
{
|
||||||
|
//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_udp
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
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_udp_tune (opts, state, state->p25_cc_freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
|||||||
107
src/p25p1_hdu.c
107
src/p25p1_hdu.c
@@ -453,93 +453,40 @@ 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;
|
|
||||||
state->payload_keyid = kidhex;
|
|
||||||
//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%02X MI: 0x%08llX%08llX%02llX", algidhex, kidhex, mihex1, mihex2, mihex3);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->payload_algid = algidhex;
|
||||||
|
state->payload_keyid = kidhex;
|
||||||
|
//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) //print on payload == 1
|
||||||
|
{
|
||||||
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -324,15 +324,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,37 +357,37 @@ 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)
|
|
||||||
{
|
|
||||||
|
|
||||||
algidhex = strtol (algid, NULL, 2);
|
|
||||||
kidhex = strtol (kid, NULL, 2);
|
|
||||||
|
|
||||||
state->payload_algid = algidhex;
|
|
||||||
state->payload_keyid = kidhex;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX", algidhex, kidhex, mihex1, mihex2, mihex3);
|
fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX", algidhex, kidhex, mihex1, mihex2, mihex3);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
|
||||||
|
|
||||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
state->payload_algid = algidhex;
|
||||||
|
state->payload_keyid = kidhex;
|
||||||
|
//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) //print on payload == 1
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " ENC");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf (stderr, " ENC \n");
|
fprintf (stderr, " LDU2 FEC ERR \n");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
506
src/p25p1_mdpu.c
Normal file
506
src/p25p1_mdpu.c
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//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_udp
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
rtl_udp_tune (opts, state, freq1);
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||||
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//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_udp
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
rtl_udp_tune (opts, state, freq1);
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||||
|
opts->p25_is_tuned = 1;
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,8 @@ 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -390,11 +390,60 @@ 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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
102
src/p25p1_tsbk.c
102
src/p25p1_tsbk.c
@@ -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)
|
||||||
{
|
{
|
||||||
|
|
||||||
int tsbkbit[196] = {0}; //tsbk bit array, 196 trellis encoded bits
|
//reset some strings when returning from a call in case they didn't get zipped already
|
||||||
int tsbk_dibit[98] = {0};
|
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;
|
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,24 +134,35 @@ 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",KMAG);
|
fprintf (stderr, "%s",KYEL);
|
||||||
process_MAC_VPDU(opts, state, 0, PDU);
|
process_MAC_VPDU(opts, state, 0, PDU);
|
||||||
fprintf (stderr, "%s",KNRM);
|
fprintf (stderr, "%s",KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
//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",KMAG);
|
fprintf (stderr, "%s",KYEL);
|
||||||
fprintf (stderr, "\n Network Status Broadcast TSBK - Abbreviated \n");
|
fprintf (stderr, "\n Network Status Broadcast TSBK - Abbreviated \n");
|
||||||
fprintf (stderr, " WACN [%05lX] SYSID [%03X] NAC [%03llX]", wacn, sysid, state->p2_cc);
|
fprintf (stderr, " WACN [%05lX] SYSID [%03X] NAC [%03llX]", wacn, sysid, state->p2_cc);
|
||||||
state->p25_cc_freq = process_channel_to_freq(opts, state, channel);
|
state->p25_cc_freq = process_channel_to_freq(opts, state, channel);
|
||||||
|
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
|
//only set IF these values aren't already hard set by the user
|
||||||
if (state->p2_hardset == 0)
|
if (state->p2_hardset == 0)
|
||||||
@@ -135,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;
|
||||||
@@ -157,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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ int vc_counter = 0;
|
|||||||
int framing_counter = 0;
|
int framing_counter = 0;
|
||||||
|
|
||||||
unsigned long long int isch = 0;
|
unsigned long long int 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;
|
||||||
|
|
||||||
@@ -193,8 +193,8 @@ void process_FACCHc (dsd_opts * opts, dsd_state * state)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " R-S ERR Fc");
|
fprintf (stderr, " R-S ERR Fc");
|
||||||
if (state->currentslot == 0) state->dmrburstL = 13;
|
//if (state->currentslot == 0) state->dmrburstL = 13;
|
||||||
else state->dmrburstR = 13;
|
//else state->dmrburstR = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -247,8 +247,8 @@ void process_FACCHs (dsd_opts * opts, dsd_state * state)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " R-S ERR Fs");
|
fprintf (stderr, " R-S ERR Fs");
|
||||||
if (state->currentslot == 0) state->dmrburstL = 13;
|
//if (state->currentslot == 0) state->dmrburstL = 13;
|
||||||
else state->dmrburstR = 13;
|
//else state->dmrburstR = 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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 16 for MBE OSS shim to preempt audio
|
||||||
|
if (state->currentslot == 0) state->dmrburstL = 16;
|
||||||
|
else state->dmrburstR = 16;
|
||||||
|
}
|
||||||
|
#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);
|
||||||
@@ -625,6 +636,16 @@ 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 16 for MBE OSS shim to preempt audio
|
||||||
|
if (state->currentslot == 0) state->dmrburstL = 16;
|
||||||
|
else state->dmrburstR = 16;
|
||||||
|
}
|
||||||
|
#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);
|
||||||
|
|
||||||
@@ -685,19 +706,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 +750,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -752,8 +796,8 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " DUID ERR %d", duid_decoded);
|
fprintf (stderr, " DUID ERR %d", duid_decoded);
|
||||||
if (state->currentslot == 0) state->dmrburstL = 12;
|
//if (state->currentslot == 0) state->dmrburstL = 12;
|
||||||
else state->dmrburstR = 12;
|
//else state->dmrburstR = 12;
|
||||||
err_counter++;
|
err_counter++;
|
||||||
}
|
}
|
||||||
if (err_counter > 1) //&& opts->aggressive_framesync == 1
|
if (err_counter > 1) //&& opts->aggressive_framesync == 1
|
||||||
@@ -804,22 +848,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);
|
||||||
@@ -828,5 +871,17 @@ void processP2 (dsd_opts * opts, dsd_state * state)
|
|||||||
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;
|
||||||
|
|
||||||
|
//bugfix for OSS slot preferred pre-emption on dual voices
|
||||||
|
//in case or marginal signal or bad decoding and both get stuck on 16, indefinitely muting the non-preferred slot
|
||||||
|
//BUG: This causes an annoying blink blink in ncurses, just FYI -- find a better workaround
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
if (opts->audio_out_type == 5)
|
||||||
|
{
|
||||||
|
//set both to blank value
|
||||||
|
state->dmrburstL = 17;
|
||||||
|
state->dmrburstR = 17;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
1313
src/p25p2_vpdu.c
1313
src/p25p2_vpdu.c
File diff suppressed because it is too large
Load Diff
110
src/p25p2_xcch.c
110
src/p25p2_xcch.c
@@ -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;
|
||||||
@@ -57,8 +60,8 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " CRC12 ERR S");
|
fprintf (stderr, " CRC12 ERR S");
|
||||||
if (state->currentslot == 0) state->dmrburstL = 14;
|
//if (state->currentslot == 0) state->dmrburstL = 14;
|
||||||
else state->dmrburstR = 14;
|
//else state->dmrburstR = 14;
|
||||||
goto END_SMAC;
|
goto END_SMAC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,8 +87,8 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
{
|
{
|
||||||
fprintf (stderr, " CRC16 ERR L");
|
fprintf (stderr, " CRC16 ERR L");
|
||||||
state->p2_is_lcch = 0; //turn flag off here
|
state->p2_is_lcch = 0; //turn flag off here
|
||||||
if (state->currentslot == 0) state->dmrburstL = 14;
|
//if (state->currentslot == 0) state->dmrburstL = 14;
|
||||||
else state->dmrburstR = 14;
|
//else state->dmrburstR = 14;
|
||||||
goto END_SMAC;
|
goto END_SMAC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +106,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf (stderr, "CRC16 ERR ");
|
fprintf (stderr, "CRC16 ERR ");
|
||||||
}
|
}
|
||||||
fprintf (stderr, "%s", KMAG);
|
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);
|
||||||
}
|
}
|
||||||
@@ -202,6 +205,13 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -218,6 +228,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);
|
||||||
}
|
}
|
||||||
@@ -226,25 +239,49 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
if (state->currentslot == 1) state->dmrburstL = 24;
|
if (state->currentslot == 1) state->dmrburstL = 24;
|
||||||
else state->dmrburstR = 24;
|
else state->dmrburstR = 24;
|
||||||
fprintf (stderr, " MAC_IDLE ");
|
fprintf (stderr, " MAC_IDLE ");
|
||||||
fprintf (stderr, "%s", KMAG);
|
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", KMAG);
|
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);
|
||||||
}
|
}
|
||||||
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 = 21;
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->dmrburstR = 21;
|
||||||
|
//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", KMAG);
|
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);
|
||||||
}
|
}
|
||||||
@@ -262,6 +299,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;
|
||||||
@@ -303,8 +343,8 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " CRC12 ERR F");
|
fprintf (stderr, " CRC12 ERR F");
|
||||||
if (state->currentslot == 0) state->dmrburstL = 14;
|
//if (state->currentslot == 0) state->dmrburstL = 14;
|
||||||
else state->dmrburstR = 14;
|
//else state->dmrburstR = 14;
|
||||||
goto END_FMAC;
|
goto END_FMAC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +355,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
// if (opcode == 0x0)
|
// if (opcode == 0x0)
|
||||||
// {
|
// {
|
||||||
// fprintf (stderr, " MAC_SIGNAL ");
|
// fprintf (stderr, " MAC_SIGNAL ");
|
||||||
// fprintf (stderr, "%s", KMAG);
|
// 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);
|
||||||
// }
|
// }
|
||||||
@@ -412,6 +452,13 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -428,6 +475,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);
|
||||||
}
|
}
|
||||||
@@ -456,25 +506,49 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
|
|
||||||
}
|
}
|
||||||
fprintf (stderr, " MAC_IDLE ");
|
fprintf (stderr, " MAC_IDLE ");
|
||||||
fprintf (stderr, "%s", KMAG);
|
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", KMAG);
|
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);
|
||||||
}
|
}
|
||||||
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", KMAG);
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
750
src/provoice.c
750
src/provoice.c
@@ -1,578 +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;
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
//raw bits storage for analysis
|
||||||
{
|
uint8_t raw_bits[800];
|
||||||
fprintf (stderr,"VOICE ");
|
memset (raw_bits, 0, sizeof(raw_bits));
|
||||||
}
|
//raw bytes storage for analysis
|
||||||
|
uint8_t raw_bytes[100];
|
||||||
|
memset (raw_bytes, 0, sizeof(raw_bytes));
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
unsigned long long int initial = 0; //initial 64-bits before the lid
|
||||||
{
|
uint16_t lid = 0; //lid value 16-bit
|
||||||
dibit = getDibit (opts, state);
|
unsigned long long int secondary = 0; //secondary 64-bits after lid, before voice
|
||||||
init[i] = dibit;
|
|
||||||
#ifdef PROVOICE_DUMP
|
uint8_t spacer_bit[8]; //check this value to see if its a status thing (like P25 P1)
|
||||||
fprintf (stderr,"%i", dibit);
|
memset (spacer_bit, 0, sizeof (spacer_bit));
|
||||||
#endif
|
uint8_t spacer = 0;
|
||||||
}
|
uint16_t bf = 0; //the 16-bit value in-between imbe 2 and imbe 3 that is usually 2175
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
fprintf (stderr," ");
|
fprintf (stderr," VOICE");
|
||||||
#endif
|
|
||||||
//init Bits (64)...MI?
|
//print group and source values if EA trunked
|
||||||
if (opts->payload == 1)
|
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->ea_mode == 1)
|
||||||
{
|
{
|
||||||
k = 0;
|
fprintf (stderr, "%s", KGRN);
|
||||||
//fprintf (stderr, "\nInit 64 ");
|
fprintf (stderr, " Site: %lld Group: %d Source: %d LCN: %d ",
|
||||||
for(i = 0; i < 8; i++)
|
state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn);
|
||||||
{
|
fprintf (stderr, "%s", KNRM);
|
||||||
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
|
//print afs value if standard/networked trunked
|
||||||
for (i = 0; i < 16; i++)
|
else if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->ea_mode == 0)
|
||||||
{
|
|
||||||
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, "%s", KGRN);
|
||||||
//fprintf (stderr, "LID ");
|
fprintf (stderr, " Site: %lld AFS: %d-%d LCN: %d ",
|
||||||
for(i = 0; i < 10; i++)
|
state->edacs_site_id, (state->lasttg >> 7) & 0xF, state->lasttg & 0x7F, state->edacs_tuned_lcn);
|
||||||
{
|
fprintf (stderr, "%s", KNRM);
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//load all initial bits before voice into raw_bits array for analysis/handling
|
||||||
|
for (i = 0; i < 64+16+64; i++)
|
||||||
|
raw_bits[i] = getDibit (opts, state);
|
||||||
|
|
||||||
|
//Note: the initial 144-bits seem to be provisioned differently depending on system type
|
||||||
|
initial = (unsigned long long int)ConvertBitIntoBytes(&raw_bits[0], 64);
|
||||||
|
lid = (uint16_t)ConvertBitIntoBytes(&raw_bits[64], 16);
|
||||||
|
secondary = (unsigned long long int)ConvertBitIntoBytes(&raw_bits[80], 64);
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n N64: %016llX", initial);
|
||||||
|
fprintf (stderr, "\n LID: %04X", lid);
|
||||||
|
fprintf (stderr, " %016llX", secondary);
|
||||||
|
}
|
||||||
|
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -790,7 +790,7 @@ 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"); //Kindly remind user to double tap CTRL + C
|
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);
|
||||||
@@ -959,7 +959,7 @@ static void *socket_thread_fn(void *arg) {
|
|||||||
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
||||||
//verbose_set_frequency (dongle.dev, new_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, "Tuning to: %d [Hz] (central freq: %d [Hz])\n", new_freq, new_freq + freq_offset);
|
||||||
fprintf (stderr, "Tuning to: %d [Hz] \n", new_freq);
|
fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
if(buffer[0] == 1) {
|
if(buffer[0] == 1) {
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
BIN
tone24.wav
BIN
tone24.wav
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
pacmd 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
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user