95 Commits

Author SHA1 Message Date
lwvmobile
ae58fabb72 DMR Cap+ Block Counter Fix; #126 2023-05-15 02:54:09 -04:00
lwvmobile
e390251b9d Tweaks to NXDN FSW Tests II; #123 2023-05-14 13:54:48 -04:00
lwvmobile
f7ad3e638f Ncurses No Color Trunking Tweaks; #114 2023-05-13 20:11:36 -04:00
lwvmobile
a16c99994c Misc Tweaks and Clear Stale Active Ch Values; #120 2023-05-13 01:39:23 -04:00
lwvmobile
7f187ff6d8 Fix LimaZulu NXDN Tweaks #123 2023-05-12 02:44:16 -04:00
lwvmobile
8485be965b Tweaks to NXDN FSW Tests; #123 2023-05-05 18:30:06 -04:00
lwvmobile
d1e1572139 pV Conventional Frame Sync and pV Tweaks; #122 2023-05-05 06:33:29 -04:00
lwvmobile
95e74c8e28 NXDN Frame Sync Tests and Cmake Option #123 2023-05-05 06:14:56 -04:00
lwvmobile
815fbb026f Various uint64_t/ul/ulli fixes for 32-bit; 2023-05-04 23:58:44 -04:00
lwvmobile
d1ce0121be Misc Tweaks and Fixes; Glamor Shots; 2023-05-03 23:44:57 -04:00
lwvmobile
a55db055d4 NCURSES Active Ch Display Tweaks; #120 2023-05-02 18:34:31 -04:00
lwvmobile
443b7c3031 DMR Active Channel Display; CSBK Tweaks; #120 2023-05-02 18:31:59 -04:00
lwvmobile
4bebb361ec P25 ENC Trunk Toggle and Active Tweaks; #120 #121 2023-05-02 18:25:25 -04:00
lwvmobile
6f538fd166 Active Channel Display NXDN Setup and Misc; #120 2023-05-02 18:19:41 -04:00
lwvmobile
fef36e3761 NXDN Tweaks to clear stale key values on keyload; 2023-04-29 17:01:16 -04:00
lwvmobile
19df3dd7f1 TCP Audio Retry on Disc; Ncurses Key '8'; #119 2023-04-29 16:06:22 -04:00
lwvmobile
acd2b0b134 More NXDN/IDAS Tweaks and Fixes; #115 and #118 2023-04-25 15:37:44 -04:00
lwvmobile
0908baa47c Update Literature; 2023-04-23 20:02:02 -04:00
lwvmobile
c507ed229c NXDN Type-D Decoding/Trunking; NXDN Tweaks; #115 2023-04-23 19:52:32 -04:00
lwvmobile
3101bbde28 Cap Plus Expansion; Minor DMR Tweaks; #116 2023-04-23 19:50:01 -04:00
lwvmobile
5e24ff505d Initial NXDN Type D - 'IDAS' Support; #115 2023-04-13 18:19:25 -04:00
lwvmobile
4a8bf144dd Literature Update; 2023-04-13 12:22:50 -04:00
lwvmobile
d872818e03 NXDN Array Expansion and Message Tweaks; #113 2023-04-13 11:00:28 -04:00
lwvmobile
f967b80217 No Color Cmake Build Option #114 2023-04-13 10:59:40 -04:00
lwvmobile
f3d94ddc2a More XPT Work; #110 2023-04-12 17:53:34 -04:00
lwvmobile
18bec720ae Hashed DMR TG Multi Key Loading Fix; #112 2023-04-12 13:10:11 -04:00
lwvmobile
456687ba86 pacmd to pactl in virtualsink; #111 2023-04-09 14:29:50 -04:00
lwvmobile
ba0e422f23 Hytera XPT WIP #110 2023-04-08 19:14:47 -04:00
lwvmobile
ce75acb41a Yet Another Cmake Warning Fix #107 2023-03-28 23:32:00 -04:00
lwvmobile
68862296bf NXDN -- DFA Base Freq Fix; ADJ_SITE_INFO; #106 2023-03-28 17:29:59 -04:00
lwvmobile
f3c0199b32 Issue #105 - Make Work with Named Pipe (FIFO) 2023-03-22 20:03:39 -04:00
lwvmobile
c8e8e041b7 NXDN DFA Support; Update Literature; 2023-03-22 20:00:14 -04:00
lwvmobile
85eea59598 Fix Cmake Warnings; 2023-03-21 22:33:42 -04:00
lwvmobile
304b5545ba Issue #105 Segfault Bugfix; 2023-03-21 20:59:53 -04:00
lwvmobile
313759333e P2 - TDMA-CC Tuning Bugfix and callstring tweak; 2023-03-21 20:58:37 -04:00
lwvmobile
1737c1f7e9 NXDN48/96 Tweak; 2023-03-12 15:41:27 -04:00
lwvmobile
bd94f83318 More XPT Tweaks and Notes; 2023-03-09 18:54:03 -05:00
lwvmobile
5d7988ea0f Line Space OCD; 2023-03-09 18:40:32 -05:00
lwvmobile
432495c1ad P25 P1 LCW re-write and updates; 2023-03-08 02:07:26 -05:00
lwvmobile
9c75583323 NCurses LO Return to CC; 2023-03-08 02:04:54 -05:00
lwvmobile
b9389742f6 XPT Trunking Tweak; 2023-03-08 02:04:05 -05:00
lwvmobile
fb59406a28 P25 P1 QPSK MSIZE/SSIZE Tweak; 2023-03-08 02:02:53 -05:00
lwvmobile
339323695f Update Literature; 2023-03-08 02:01:49 -05:00
lwvmobile
d8e7c4c3aa Fix trunk_hangtime lf to f; Ncurses Fixes; 2023-03-06 03:07:23 -05:00
lwvmobile
1dc6737cc2 Fix Fast Scanner and NCurses Display (again) 2023-03-05 05:52:41 -05:00
lwvmobile
c1ed3fe038 Fix unintended bug in iden_up_vu; 2023-03-04 20:52:59 -05:00
lwvmobile
d86b370a22 Literature Update 2023-03-04 19:27:36 -05:00
lwvmobile
b7bfa98a50 P25 TSBK PDU Fix; vPDU PDU Fixes; Scanner Freq Fix 2023-03-04 19:26:35 -05:00
lwvmobile
da41dcd5c9 Fast Scan Ncurses Tweak; Update Literature; 2023-03-03 15:36:10 -05:00
lwvmobile
0c27de102d P25 - P1 MBF Format Addition; TSBK/vPDU Tweaks; 2023-03-03 15:15:31 -05:00
lwvmobile
54c75e20c9 NXDN Trunking Tweaks and Misc Tweaks; 2023-03-02 22:13:45 -05:00
lwvmobile
3102770353 Trunking -- Faster CC Hunt; 2023-03-01 18:32:26 -05:00
lwvmobile
477ca97830 Experimental -- Fast Scanner Feature; 2023-03-01 17:56:07 -05:00
lwvmobile
5b51adfc95 P25 - Remove Test Code; NXDN - Remove Freq Calc; 2023-03-01 17:49:37 -05:00
lwvmobile
4ab16b5935 DMR - XPT Tweaks; Data Header Tweaks; 2023-02-25 16:07:01 -05:00
lwvmobile
fe5ec8b0b4 FID 0x10 - FLCO 0x14, 0x15, 0x16, 0x17, 0x18; 2023-02-24 20:51:37 -05:00
lwvmobile
58d8ad8a8c Disable remaining setmod_bw as safety precaution; 2023-02-24 20:46:31 -05:00
lwvmobile
df5d755345 NXDN Trunking Tweak - Snap Back to CC on Disc; 2023-02-24 18:40:59 -05:00
lwvmobile
9a7a836eca Fix P_Protect CSBK GI indicator; 2023-02-24 18:40:07 -05:00
lwvmobile
4243fc9692 Fix NCurses **tera key display when loaded; 2023-02-24 18:38:55 -05:00
lwvmobile
c999c97445 Misc Tweaks; Multi BP; More CapMax FLCO; XPT; 2023-02-23 21:57:49 -05:00
lwvmobile
569f72508a Literature Update; 2023-02-22 20:28:03 -05:00
lwvmobile
d78f516afe NXDN Tweaks and SACCH/FACCH CRC Fixes; 2023-02-22 19:46:03 -05:00
lwvmobile
6e4a373d26 Minor Cap+ and XPT Fix; 2023-02-20 22:42:36 -05:00
lwvmobile
2f644f5ba6 Disable/Tweak some setmod_bw defaults; 2023-02-20 17:58:48 -05:00
lwvmobile
37c24d0ca7 Cap+ NL; Hytera XPT Trunk Support (experimental) 2023-02-20 12:46:23 -05:00
lwvmobile
9cdbd35e51 General DMR Tweaks; 64-bit IV from 32-bit MI; 2023-02-20 12:43:14 -05:00
lwvmobile
67e28c4655 DMR - LRRP tweaks; Add LOCN PDU. 2023-02-20 12:41:17 -05:00
lwvmobile
591df8657a DMR - dheader rewrite; UDT/USBD fix; block tweaks; 2023-02-20 12:40:39 -05:00
lwvmobile
c708ea5911 P25 Dibit, Block and PDU Payload Tweaks; 2023-02-20 12:35:40 -05:00
lwvmobile
a4ecfdca30 DMR - Fix Cap+ CSBK; Various Minor Fixes; 2023-02-05 16:46:02 -05:00
lwvmobile
ac1fd7897c DMR Minor Code Fixes; 2023-02-04 20:09:54 -05:00
lwvmobile
a02f992a25 NCurses Compiler Warning and Call Alert Fix; 2023-02-04 04:34:09 -05:00
lwvmobile
630d3f362c DMR Cap+ CSBK Improvements; 2023-02-03 22:12:48 -05:00
lwvmobile
6666424ff8 DMR FLCO Tweaks; P25 Site/RFSS Display; 2023-02-02 20:02:28 -05:00
lwvmobile
b0fc20cea6 DMR Conf Data CRC9; Message CRC32; Tweaks; 2023-02-01 05:35:15 -05:00
lwvmobile
f61dbd4fe7 DMR FLC Tweaks; CSBK Cleaning; 2023-02-01 05:31:36 -05:00
lwvmobile
f369d645b1 DMR FLCO - Check PF First; 2023-01-29 16:09:44 -05:00
lwvmobile
7148e653e5 DMR Unknown FLCO Tweaks; PI Tweaks; 2023-01-28 21:00:08 -05:00
lwvmobile
bb5da93fcd Fix DMR FEC/CRC/RAS Implementation Issues; 2023-01-21 05:41:22 -05:00
lwvmobile
d2d750e920 Tweak RAS/CRC; Other Misc Tweaks; 2023-01-19 09:39:05 -05:00
lwvmobile
485161f42d NXDN Multi-Key CSV Import; Example Updt; 2023-01-18 12:18:48 -05:00
lwvmobile
67661c2fb0 DMR SB/RC Additions; Cap+ LC/CSBK; RAS; 2023-01-17 23:57:58 -05:00
lwvmobile
dcb21f2cd0 Fix/Restore MBEout Files; 'DSP' CACH; Tweaks; 2023-01-17 02:24:49 -05:00
lwvmobile
9f03bbc8a1 Cap+ FLC/CSBK Tweaks; 2023-01-17 02:08:21 -05:00
lwvmobile
b4ecdf7820 'DSP' File Output for OKDMR - Initial Commit; 2023-01-15 22:03:53 -05:00
lwvmobile
35cc358101 Cap+ Things; 2023-01-15 21:22:18 -05:00
lwvmobile
db23968a6f Allow Standard BP Key on MBE File Playback; 2023-01-15 18:13:39 -05:00
lwvmobile
9efd950b9f Fix Audio Gain on MBE File Playback; 2023-01-15 18:11:34 -05:00
lwvmobile
08461492ee P25 P2 ISCH Var Fix; 2023-01-15 18:09:25 -05:00
lwvmobile
b2ccd3a878 DMR Cap+ Rest Channel FLCO/SLCO Fix; 2023-01-09 19:35:03 -05:00
lwvmobile
9b8a382ea5 RTL Tuning Fix(UDP); Cosmetic Change; 2023-01-09 13:19:34 -05:00
lwvmobile
235eeedcda dPMR 'Version' Check Fix; 2023-01-07 15:16:52 -05:00
lwvmobile
0cff92101c dPMR Scrambler Key Support; 2023-01-07 15:02:04 -05:00
lwvmobile
c44e039772 EDACS 32-bit Variable Fix; 2023-01-05 16:27:44 -05:00
160 changed files with 15043 additions and 46513 deletions

4
.gitignore vendored
View File

@@ -7,6 +7,4 @@ build
examples examples
*.bin *.bin
*.ans *.ans
*.patch
*.zip
result

View File

@@ -1,6 +1,6 @@
#Ubuntu 20.04 uses 3.16.3 #Ubuntu 20.04 uses 3.16.3
#Ubuntu 22.04 uses 3.22.1 #Ubuntu 22.04 uses 3.22.1
#Arch currently on 3.26.3 #Arch currently on 3.26.3
#Cygwin currently using 3.23.2 (or newer) #Cygwin currently using 3.23.2 (or newer)
cmake_minimum_required(VERSION 3.10.2) cmake_minimum_required(VERSION 3.10.2)
project(dsd-fme) project(dsd-fme)
@@ -10,55 +10,46 @@ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
set(CURSES_NEED_NCURSES TRUE) set(CURSES_NEED_NCURSES TRUE)
set(CURSES_NEED_WIDE TRUE) set(CURSES_NEED_WIDE TRUE)
#use cmake option -DCOLORS=OFF to disable color output (ncurses) #use cmake option -DCOLORS=OFF to disable color output
option(COLORS option(COLORS
"Build with Colors Enabled" ON) "Build with Colors Enabled" ON)
if (COLORS) if (COLORS)
add_definitions(-DPRETTY_COLORS) add_definitions(-DPRETTY_COLORS)
endif ()
#use cmake option -DCOLORSLOGS=OFF to disable color output (terminal/logs)
option(COLORSLOGS
"Build with Colors Enabled" ON)
if (COLORSLOGS)
add_definitions(-DPRETTY_COLORS_LOGS)
endif () endif ()
#use cmake option -DPVC=ON to enable Provoice Conventional Frame Sync #use cmake option -DPVC=ON to enable Provoice Conventional Frame Sync
option(PVC option(PVC
"Build with Provoice Conventional Frame Sync Enabled" OFF) "Build with Provoice Conventional Frame Sync Enabled" OFF)
if (PVC) if (PVC)
add_definitions(-DPVCONVENTIONAL) add_definitions(-DPVCONVENTIONAL)
endif () 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 #use cmake option -DLZ=ON to enable LimaZulu Requested NXDN Tweaks
option(LZ option(LZ
"Build with new LimaZulu Requested NXDN Tweaks Enabled" OFF) "Build with new LimaZulu Requested NXDN Tweaks Enabled" OFF)
if (LZ) if (LZ)
add_definitions(-DLIMAZULUTWEAKS) add_definitions(-DLIMAZULUTWEAKS)
endif () endif ()
#use cmake option -DSID=ON to enable P25p1 Soft ID decoding -- Experimental
option(SID
"Build with P25p1 LSD/Soft ID Enabled" OFF)
if (SID)
add_definitions(-DSOFTID)
endif ()
include(git_revision) include(git_revision)
git_describe(GIT_TAG) git_describe(GIT_TAG)
find_package(LibSndFile REQUIRED) find_package(LibSndFile REQUIRED)
find_package(MBE REQUIRED) find_package(MBE REQUIRED)
find_package(ITPP REQUIRED)
find_package(RTLSDR) find_package(RTLSDR)
find_package(Curses REQUIRED) find_package(Curses REQUIRED)
find_package(PulseAudio REQUIRED) find_package(PulseAudio REQUIRED)
find_package(CODEC2)
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIRS}) include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIR})
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES}) set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${CURSES_LIBRARY})
if(RTLSDR_FOUND) if(RTLSDR_FOUND)
find_package(Threads) find_package(Threads)
@@ -67,12 +58,6 @@ if(RTLSDR_FOUND)
add_definitions(-DUSE_RTLSDR) add_definitions(-DUSE_RTLSDR)
endif(RTLSDR_FOUND) endif(RTLSDR_FOUND)
if(CODEC2_FOUND)
include_directories(SYSTEM ${CODEC2_INCLUDE_DIRS})
list(APPEND LIBS ${CODEC2_LIBRARIES})
add_definitions(-DUSE_CODEC2)
endif(CODEC2_FOUND)
FILE(GLOB SRCS src/*.c src/*.cpp) FILE(GLOB SRCS src/*.c src/*.cpp)
FILE(GLOB HEADERS include/*.h include/*.hpp) FILE(GLOB HEADERS include/*.h include/*.hpp)
@@ -88,11 +73,6 @@ include_directories("${PROJECT_SOURCE_DIR}/include")
ADD_EXECUTABLE(dsd-fme ${SRCS} ${HEADERS}) ADD_EXECUTABLE(dsd-fme ${SRCS} ${HEADERS})
TARGET_LINK_LIBRARIES(dsd-fme ${LIBS}) TARGET_LINK_LIBRARIES(dsd-fme ${LIBS})
target_compile_options(dsd-fme PRIVATE -Wunused-but-set-variable -Wunused-variable -Wunused-parameter
-Wempty-body -Wunused-label $<$<COMPILE_LANGUAGE:C>:-Wpointer-sign>
-Wmisleading-indentation -Wparentheses -Wunused-value -Wreturn-type
-Wtautological-compare)
include(GNUInstallDirs) include(GNUInstallDirs)
install(TARGETS dsd-fme DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS dsd-fme DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@@ -1,17 +1,18 @@
# Digital Speech Decoder - Florida Man Edition # Digital Speech Decoder - Florida Man Edition
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and [Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, YSF, M17, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments. DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and
[Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments.
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, ilyacodes, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody. This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thank you everybody.
![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/audio_work/dsd-fme2.png) ![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/main/dsd-fme2.png)
![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/audio_work/dsd-fme3.png) ![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/main/dsd-fme3.png)
## Information ## Information
See the [examples](https://github.com/lwvmobile/dsd-fme/tree/audio_work/examples "examples") folder for information on [cloning and installing](https://github.com/lwvmobile/dsd-fme/blob/audio_work/examples/Install_Notes.md "cloning and installing"), [example usage](https://github.com/lwvmobile/dsd-fme/blob/audio_work/examples/Example_Usage.md "example usage"), and [trunking examples](https://github.com/lwvmobile/dsd-fme/blob/audio_work/examples/trunking.sh "trunking examples"). See the [examples](https://github.com/lwvmobile/dsd-fme/tree/main/examples "examples") folder for information on [cloning and installing](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Install_Notes.md "cloning and installing"), [example usage](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Example_Usage.md "example usage"), and [trunking examples](https://github.com/lwvmobile/dsd-fme/blob/main/examples/trunking.sh "trunking examples").
## License ## License
Copyright (C) 2010 DSD Author Copyright (C) 2010 DSD Author

View File

@@ -1,31 +0,0 @@
# - Try to find CODEC2
# Once done this will define
#Seems to work on Cygwin as well now -- more testing for may be required
# Not sure what causes the 'Package 'codec2', required by 'virtual:world', not found' message, but seems to work anyways
# -- Checking for module 'codec2'
# -- Package 'codec2', required by 'virtual:world', not found
# -- Found CODEC2: /usr/local/lib/libcodec2.dll.a
if (NOT CODEC2_FOUND)
find_package(PkgConfig)
pkg_check_modules(CODEC2_PKG codec2)
set(CODEC2_DEFINITIONS ${PC_CODEC2_CFLAGS_OTHER})
find_path(CODEC2_INCLUDE_DIR
NAMES codec2/codec2.h codec2.h
HINTS ${CODEC2_PKG_INCLUDE_DIRS})
find_library(CODEC2_LIBRARY
NAMES codec2/codec2 codec2
HINTS ${CODEC2_PKG_LIBRARY_DIRS})
set(CODEC2_LIBRARIES ${CODEC2_LIBRARY})
set(CODEC2_INCLUDE_DIRS ${CODEC2_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CODEC2 DEFAULT_MSG CODEC2_LIBRARY CODEC2_INCLUDE_DIR)
mark_as_advanced(CODEC2_INCLUDE_DIR CODEC2_LIBRARY)
endif (NOT CODEC2_FOUND)

15
cmake/FindITPP.cmake Normal file
View File

@@ -0,0 +1,15 @@
# - Try to find ITPP
# Once done this will define
#
# ITPP_FOUND - System has ITPP
# ITPP_INCLUDE_DIR - The ITPP include directory
# ITPP_LIBRARY - The library needed to use ITPP
#
find_path(ITPP_INCLUDE_DIR itpp/itcomm.h)
set(ITPP_NAMES ${ITPP_NAMES} itpp libitpp libitpp.dll)
FIND_LIBRARY(ITPP_LIBRARY NAMES ${ITPP_NAMES})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ITPP DEFAULT_MSG ITPP_LIBRARY ITPP_INCLUDE_DIR)

View File

@@ -1,4 +1,4 @@
# #
# Internal file for GetGitRevisionDescription.cmake # Internal file for GetGitRevisionDescription.cmake
# #
# Requires CMake 2.6 or newer (uses the 'function' command) # Requires CMake 2.6 or newer (uses the 'function' command)

View File

@@ -1,244 +0,0 @@
#! /bin/bash
#
cdir=$(pwd)
clear
printf "DSD-FME Digital Speech Decoder - Florida Man Edition Portable Cygwin Creator\n"
printf "Only use this in a Cygwin64 Environment. Will not work in a Linux Environment!\n"
read -p "Do you want to make a portable copy? y/N " RELEASE
RELEASE=$(printf "$RELEASE"|tr '[:upper:]' '[:lower:]')
if [ "$RELEASE" = "y" ]; then
printf "Copying files to dsd-fme-portable folder and making zip file. Please wait!\n"
cd ~
#create a dsd-fme-portable folder and copy all the needed items for a portable release version
mkdir dsd-fme-portable
cd dsd-fme-portable
#all of these folders may not be necessary
mkdir sourcecode
mkdir bin
mkdir dev
cd dev
mkdir shm
cd ..
mkdir dsd-fme
mkdir logs
mkdir WAV
mkdir etc
mkdir home
mkdir lib
mkdir tmp
mkdir usr
cd usr
mkdir share
cd share
mkdir terminfo
cd ~
#start copying things
#terminfo is needed for ncurses
cp -r /usr/share/terminfo/* dsd-fme-portable/usr/share/terminfo
#copy required items from the root bin folder to release bin folder
cp /bin/ls.exe dsd-fme-portable/bin/
cp /bin/bash.exe dsd-fme-portable/bin/
cp /bin/ncurses6-config dsd-fme-portable/bin/
cp /bin/ncursesw6-config dsd-fme-portable/bin/
cp /bin/pacat.exe dsd-fme-portable/bin/
cp /bin/pactl.exe dsd-fme-portable/bin/
cp /bin/padsp dsd-fme-portable/bin/
cp /bin/pamon dsd-fme-portable/bin/
cp /bin/paplay dsd-fme-portable/bin/
cp /bin/parec dsd-fme-portable/bin/
cp /bin/parecord dsd-fme-portable/bin/
cp /bin/pulseaudio.exe dsd-fme-portable/bin/
cp /bin/tty.exe dsd-fme-portable/bin/
#start copying things from the etc folder
cp -r /etc/pulse/ dsd-fme-portable/etc/
#start copying things to the lib folder
cp -r /lib/pulseaudio/ dsd-fme-portable/lib/
#start copying things into the dsd-fme folder
cp dsd-fme/build/dsd-fme.exe dsd-fme-portable/dsd-fme/
cp -r dsd-fme/examples dsd-fme-portable/dsd-fme/
cp .bash_profile dsd-fme-portable/dsd-fme/
cp .bashrc dsd-fme-portable/dsd-fme/
cp .inputrc dsd-fme-portable/dsd-fme/
cp .profile dsd-fme-portable/dsd-fme/
#move (cut) the bat files to the portable folder root and delete the other ones so users won't get confused
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/*.bat dsd-fme-portable/
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/example_options.txt dsd-fme-portable/
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/complete_usage_options.txt dsd-fme-portable/
rm -rf dsd-fme-portable/dsd-fme/examples/cygwin_bat
#change .bat files permissions to be read/write/executable by all users (for some reason, this is not preserved)
chmod 777 dsd-fme-portable/*.bat
#copy the license, copyright, source code, and things that should be included in all portable releases
cp dsd-fme/COPYRIGHT dsd-fme-portable/sourcecode/
cp dsd-fme/DSD_Author.pgp dsd-fme-portable/sourcecode/
cp dsd-fme/dsd-fme-qgis-map.qgz dsd-fme-portable/sourcecode/
cp dsd-fme/README.md dsd-fme-portable/sourcecode/
cp dsd-fme/dsd-fme2.png dsd-fme-portable/sourcecode/
cp dsd-fme/dsd-fme3.png dsd-fme-portable/sourcecode/
cp dsd-fme/CMakeLists.txt dsd-fme-portable/sourcecode/
cp -r dsd-fme/src dsd-fme-portable/sourcecode/
cp -r dsd-fme/include dsd-fme-portable/sourcecode/
cp -r dsd-fme/cmake dsd-fme-portable/sourcecode/
cp -r dsd-fme/patch dsd-fme-portable/sourcecode/
#compiled items into release dsd-fme folder
cp codec2/build/src/cygcodec2* dsd-fme-portable/dsd-fme/
cp rtl-sdr/build/src/cyg* dsd-fme-portable/dsd-fme/
cp mbelib/build/cygmbe* dsd-fme-portable/dsd-fme/
#bin items into release dsd-fme folder (uisng * where version numbering may exist, those may be upgraded if hard coded)
cp /bin/bash.exe dsd-fme-portable/dsd-fme/
cp /bin/cut.exe dsd-fme-portable/dsd-fme/
cp /bin/cygao* dsd-fme-portable/dsd-fme/
cp /bin/cygarchive* dsd-fme-portable/dsd-fme/
cp /bin/cygargp* dsd-fme-portable/dsd-fme/
cp /bin/cygassuan* dsd-fme-portable/dsd-fme/
cp /bin/cygasyncns* dsd-fme-portable/dsd-fme/
cp /bin/cygatomic* dsd-fme-portable/dsd-fme/
cp /bin/cygattr* dsd-fme-portable/dsd-fme/
cp /bin/cygavahi-client* dsd-fme-portable/dsd-fme/
cp /bin/cygavahi-common* dsd-fme-portable/dsd-fme/
cp /bin/cygavahi-wrap* dsd-fme-portable/dsd-fme/
cp /bin/cygblkid* dsd-fme-portable/dsd-fme/
cp /bin/cygbrotlicommon* dsd-fme-portable/dsd-fme/
cp /bin/cygbrotlidec* dsd-fme-portable/dsd-fme/
cp /bin/cygbz2* dsd-fme-portable/dsd-fme/
cp /bin/cygcares* dsd-fme-portable/dsd-fme/
cp /bin/cygcheck.exe dsd-fme-portable/dsd-fme/
cp /bin/cygcli.dll dsd-fme-portable/dsd-fme/
cp /bin/cygcom_err* dsd-fme-portable/dsd-fme/
cp /bin/cygcrypt* dsd-fme-portable/dsd-fme/
cp /bin/cygcrypto* dsd-fme-portable/dsd-fme/
cp /bin/cygcurl* dsd-fme-portable/dsd-fme/
cp /bin/cygdb_cxx* dsd-fme-portable/dsd-fme/
cp /bin/cygdb_sql* dsd-fme-portable/dsd-fme/
cp /bin/cygdb* dsd-fme-portable/dsd-fme/
cp /bin/cygdbus* dsd-fme-portable/dsd-fme/
cp /bin/cygedit* dsd-fme-portable/dsd-fme/
cp /bin/cygexpat* dsd-fme-portable/dsd-fme/
cp /bin/cygfdisk* dsd-fme-portable/dsd-fme/
cp /bin/cygffi* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3_threads* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3f_threads* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3f* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3l_threads* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3l* dsd-fme-portable/dsd-fme/
cp /bin/cygfido2* dsd-fme-portable/dsd-fme/
cp /bin/cygFLAC* dsd-fme-portable/dsd-fme/
cp /bin/cygformw* dsd-fme-portable/dsd-fme/
cp /bin/cyggc* dsd-fme-portable/dsd-fme/
cp /bin/cyggdbm_compat* dsd-fme-portable/dsd-fme/
cp /bin/cyggdbm*.dll dsd-fme-portable/dsd-fme/
cp /bin/cyggio* dsd-fme-portable/dsd-fme/
cp /bin/cygglib* dsd-fme-portable/dsd-fme/
cp /bin/cyggmodule* dsd-fme-portable/dsd-fme/
cp /bin/cyggmp* dsd-fme-portable/dsd-fme/
cp /bin/cyggnutls* dsd-fme-portable/dsd-fme/
cp /bin/cyggobject* dsd-fme-portable/dsd-fme/
cp /bin/cyggomp* dsd-fme-portable/dsd-fme/
cp /bin/cyggpg-error* dsd-fme-portable/dsd-fme/
cp /bin/cyggpgme* dsd-fme-portable/dsd-fme/
cp /bin/cyggsm* dsd-fme-portable/dsd-fme/
cp /bin/cyggssapi_krb5* dsd-fme-portable/dsd-fme/
cp /bin/cyggthread* dsd-fme-portable/dsd-fme/
cp /bin/cygguile* dsd-fme-portable/dsd-fme/
cp /bin/cyghistory* dsd-fme-portable/dsd-fme/
cp /bin/cyghogweed* dsd-fme-portable/dsd-fme/
cp /bin/cygICE* dsd-fme-portable/dsd-fme/
cp /bin/cygiconv* dsd-fme-portable/dsd-fme/
cp /bin/cygid3tag* dsd-fme-portable/dsd-fme/
cp /bin/cygidn* dsd-fme-portable/dsd-fme/
cp /bin/cygintl* dsd-fme-portable/dsd-fme/
cp /bin/cygisl* dsd-fme-portable/dsd-fme/
cp /bin/cygjsoncpp* dsd-fme-portable/dsd-fme/
cp /bin/cygk5crypto* dsd-fme-portable/dsd-fme/
cp /bin/cygkrb5* dsd-fme-portable/dsd-fme/
cp /bin/cyglber* dsd-fme-portable/dsd-fme/
cp /bin/cygldap* dsd-fme-portable/dsd-fme/
cp /bin/cygltdl* dsd-fme-portable/dsd-fme/
cp /bin/cyglz* dsd-fme-portable/dsd-fme/
cp /bin/cygma* dsd-fme-portable/dsd-fme/
cp /bin/cygmenuw* dsd-fme-portable/dsd-fme/
cp /bin/cygmeta* dsd-fme-portable/dsd-fme/
cp /bin/cygmp* dsd-fme-portable/dsd-fme/
cp /bin/cygncurses* dsd-fme-portable/dsd-fme/
cp /bin/cygnettle* dsd-fme-portable/dsd-fme/
cp /bin/cygnghttp* dsd-fme-portable/dsd-fme/
cp /bin/cygobjc* dsd-fme-portable/dsd-fme/
cp /bin/cygogg* dsd-fme-portable/dsd-fme/
cp /bin/cygopus* dsd-fme-portable/dsd-fme/
cp /bin/cygorc* dsd-fme-portable/dsd-fme/
cp /bin/cygp11* dsd-fme-portable/dsd-fme/
cp /bin/cygpanel* dsd-fme-portable/dsd-fme/
cp /bin/cygpath.exe dsd-fme-portable/dsd-fme/
cp /bin/cygpcre* dsd-fme-portable/dsd-fme/
cp /bin/cygperl* dsd-fme-portable/dsd-fme/
cp /bin/cygpipe* dsd-fme-portable/dsd-fme/
cp /bin/cygpkg* dsd-fme-portable/dsd-fme/
cp /bin/cygpng* dsd-fme-portable/dsd-fme/
cp /bin/cygpopt* dsd-fme-portable/dsd-fme/
cp /bin/cygprotocol* dsd-fme-portable/dsd-fme/
cp /bin/cygpsl* dsd-fme-portable/dsd-fme/
cp /bin/cygpulse* dsd-fme-portable/dsd-fme/
cp /bin/cygquadmath* dsd-fme-portable/dsd-fme/
cp /bin/cygreadline* dsd-fme-portable/dsd-fme/
cp /bin/cygrhash* dsd-fme-portable/dsd-fme/
cp /bin/cygrunsrv.exe dsd-fme-portable/dsd-fme/
cp /bin/cygsamplerate* dsd-fme-portable/dsd-fme/
cp /bin/cygsasl* dsd-fme-portable/dsd-fme/
cp /bin/cygserver* dsd-fme-portable/dsd-fme/
cp /bin/cygSM* dsd-fme-portable/dsd-fme/
cp /bin/cygsmart* dsd-fme-portable/dsd-fme/
cp /bin/cygsndfile* dsd-fme-portable/dsd-fme/
cp /bin/cygsox* dsd-fme-portable/dsd-fme/
cp /bin/cygspeex* dsd-fme-portable/dsd-fme/
cp /bin/cygsqlite* dsd-fme-portable/dsd-fme/
cp /bin/cygssh* dsd-fme-portable/dsd-fme/
cp /bin/cygssl* dsd-fme-portable/dsd-fme/
cp /bin/cygstart.exe dsd-fme-portable/dsd-fme/
cp /bin/cygstdc* dsd-fme-portable/dsd-fme/
cp /bin/cygtasn* dsd-fme-portable/dsd-fme/
cp /bin/cygtdb* dsd-fme-portable/dsd-fme/
cp /bin/cygticw* dsd-fme-portable/dsd-fme/
cp /bin/cygtwo* dsd-fme-portable/dsd-fme/
cp /bin/cygunistring* dsd-fme-portable/dsd-fme/
cp /bin/cygusb* dsd-fme-portable/dsd-fme/
cp /bin/cyguuid* dsd-fme-portable/dsd-fme/
cp /bin/cyguv* dsd-fme-portable/dsd-fme/
cp /bin/cygvorbis* dsd-fme-portable/dsd-fme/
cp /bin/cygwavpack* dsd-fme-portable/dsd-fme/
cp /bin/cygwin1.dll dsd-fme-portable/dsd-fme/
cp /bin/cygwrap* dsd-fme-portable/dsd-fme/
cp /bin/cygX* dsd-fme-portable/dsd-fme/
cp /bin/cygx* dsd-fme-portable/dsd-fme/
cp /bin/cygz* dsd-fme-portable/dsd-fme/
cp /bin/grep.exe dsd-fme-portable/dsd-fme/
cp /bin/ls.exe dsd-fme-portable/dsd-fme/
cp /bin/mintty.exe dsd-fme-portable/dsd-fme/
cp /bin/pacat.exe dsd-fme-portable/dsd-fme/
cp /bin/pacmd.exe dsd-fme-portable/dsd-fme/
cp /bin/pactl.exe dsd-fme-portable/dsd-fme/
cp /bin/padsp dsd-fme-portable/dsd-fme/
cp /bin/pamon dsd-fme-portable/dsd-fme/
cp /bin/paplay dsd-fme-portable/dsd-fme/
cp /bin/parec dsd-fme-portable/dsd-fme/
cp /bin/pulseaudio.exe dsd-fme-portable/dsd-fme/
cp /bin/tail.exe dsd-fme-portable/dsd-fme/
cp /bin/touch.exe dsd-fme-portable/dsd-fme/
cp /bin/tty.exe dsd-fme-portable/dsd-fme/
zip -r dsd-fme-x86-64-cygwin-portable.zip dsd-fme-portable
fi

View File

@@ -1,14 +0,0 @@
#! /bin/bash
#
clear
printf "DSD-FME Digital Speech Decoder - Florida Man Edition
Automatic Git Pull and Rebuild for Cygwin\n\n"
sleep 1
##Open your clone folder##
git pull
sleep 2
##cd into your build folder##
cd build
cmake ..
make -j $(nproc)
make install

View File

@@ -1,47 +0,0 @@
#! /bin/bash
#
cdir=$(pwd)
clear
printf "Digital Speech Decoder: Florida Man Edition - Auto Installer For Arch Linux\n
This will install the required packages, clone, build, and install DSD-FME only.
This has been tested on Arch 2023.08.01 and Manjaro XFCE 22.1.3 Minimal.\n
MBELib is considered a requirement on this build.
You must view the Patent Notice prior to continuing.
The Patent Notice can be found at the site below.
https://github.com/lwvmobile/mbelib#readme
Please confirm that you have viewed the patent notice by entering y below.\n\n"
read -p "Have you viewed the patent notice? y/N " ANSWER
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
if [ "$ANSWER" = "y" ]; then
sudo pacman -Syu #always run a full update first, partial upgrades aren't supported in Arch -- including downloading dependencies, that may require an updated dependency, and breaks same dependency on another package, a.k.a, dependency hell
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget
git clone https://github.com/lwvmobile/mbelib
cd mbelib
git checkout ambe_tones
mkdir build
cd build
cmake ..
make -j $(nproc)
sudo make install
sudo ldconfig
sudo cp /usr/local/lib/libmbe.so /usr/lib/libmbe.so
cd $cdir
git clone https://github.com/lwvmobile/dsd-fme
### For branch audio_work - git clone --branch audio_work https://github.com/lwvmobile/dsd-fme
cd dsd-fme
#git checkout audio_work
mkdir build
cd build
cmake ..
make -j $(nproc)
sudo make install
sudo ldconfig
printf "Any issues, Please report to either:\nhttps://github.com/lwvmobile/dsd-fme/issues or\nhttps://forums.radioreference.com/threads/dsd-fme.438137/\n\n"
else
printf "Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.\n\n"
fi

View File

@@ -1,77 +0,0 @@
#! /bin/bash
#
cdir=$(pwd)
clear
printf "Digital Speech Decoder: Florida Man Edition - Auto Installer For Cygwin\n
This will install the required packages, clone, build, and install DSD-FME only.
This has been tested on Cygwin x86-64 as of 20250211. This may take a while!\n
MBELib is considered a requirement on this build.
You must view the Patent Notice prior to continuing.
The Patent Notice can be found at the site below.
https://github.com/lwvmobile/mbelib#readme
Please confirm that you have viewed the patent notice by entering y below.\n\n"
read -p "Have you viewed the patent notice? y/N " ANSWER
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
if [ "$ANSWER" = "y" ]; then
#is this needed?
LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH
echo $LD_LIBRARY_PATH
#MBELIB
cd $cdir
printf "Installing mbelib\n Please wait!\n"
git clone https://github.com/lwvmobile/mbelib.git
cd mbelib
git checkout ambe_tones
mkdir build
cd build
cmake ..
make -j $(nproc)
make install
cp cygmbe-1.dll /bin
#CODEC2
cd $cdir
printf "Installing codec2\n Please wait!\n"
git clone https://github.com/drowe67/codec2.git
cd codec2
mkdir build
cd build
cmake ..
make -j $(nproc)
make install
#RTL-SDR
cd $cdir
printf "Installing RTL-SDR\n Please wait!\n"
git clone https://github.com/lwvmobile/rtl-sdr.git
cd rtl-sdr
mkdir build
cd build
cmake ..
make -j $(nproc)
make install
#DSD-FME
cd $cdir
printf "Installing DSD-FME\n Please wait!\n"
git clone https://github.com/lwvmobile/dsd-fme.git
cd dsd-fme
#git checkout aw_dev
mkdir build
cd build
cmake -DCOLORSLOGS=OFF ..
make -j $(nproc)
make install
cd $cdir
#call the cyg_portable script
sh dsd-fme/cyg_portable.sh
printf "Any issues, Please report to either:\nhttps://github.com/lwvmobile/dsd-fme/issues or\nhttps://forums.radioreference.com/threads/dsd-fme.438137/\n\n"
else
printf "Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.\n\n"
fi

View File

@@ -1,17 +1,23 @@
#! /bin/bash #! /bin/bash
clear clear
printf "\nDigital Speech Decoder: Florida Man Edition - Auto Installer\n echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer
This will clone, build, and install DSD-FME only. echo This will clone, build, and install DSD-FME only.
If DSD-FME has never been installed on this machine, echo If you need dependencies installed, please run download-and-install.sh instead.
then you will need to install the dependencies first! echo
Please run download-and-install.sh instead.\n\n"
read -p "Press Enter to continue..." x read -p "Press enter to continue"
git clone https://github.com/lwvmobile/dsd-fme git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme cd dsd-fme
#git checkout audio_work sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo cp tone48.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
sudo chmod 777 /usr/share/tone48.wav
mkdir build mkdir build
cd build cd build
cmake .. cmake ..
make -j $(nproc) make -j `nproc`
sudo make install sudo make install
sudo ldconfig sudo ldconfig

View File

@@ -1,38 +0,0 @@
#! /bin/bash
#
cdir=$(pwd)
clear
printf "Digital Speech Decoder: Florida Man Edition - Auto Installer\n
MBELib is considered a requirement on this build.
You must view the Patent Notice prior to continuing.
The Patent Notice can be found at the site below.
https://github.com/lwvmobile/mbelib#readme
Please confirm that you have viewed the patent notice by entering y below:\n\n"
read -p "Have you viewed the patent notice? y/N " ANSWER
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
if [ "$ANSWER" = "y" ]; then
sudo apt update
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncurses-dev libncurses6 libcodec2-dev
git clone https://github.com/lwvmobile/mbelib
cd mbelib
git checkout ambe_tones
mkdir build
cd build
cmake ..
make -j $(nproc)
sudo make install
sudo ldconfig
cd $cdir
git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme
#git checkout audio_work
mkdir build
cd build
cmake ..
make -j $(nproc)
sudo make install
sudo ldconfig
else
printf "\nSorry, you cannot build DSD-FME without acknowledging the Patent Notice.\nExiting...\n\n"
exit 2
fi

View File

@@ -1,38 +1,67 @@
#! /bin/bash #! /bin/bash
#
cdir=$(pwd)
clear clear
printf "Digital Speech Decoder: Florida Man Edition - Auto Installer\n echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer
MBELib is considered a requirement on this build. echo MBELib is considered a requirement on this build.
You must view the Patent Notice prior to continuing. echo You must view this notice prior to continuing.
The Patent Notice can be found at the site below. echo The Patent Notice can be found at the site below.
https://github.com/lwvmobile/mbelib#readme echo https://github.com/lwvmobile/mbelib#readme
Please confirm that you have viewed the patent notice by entering y below:\n\n" echo Please confirm that you have viewed the patent notice by entering y below.
read -p "Have you viewed the patent notice? y/N " ANSWER echo
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]') echo y/N
if [ "$ANSWER" = "y" ]; then read ANSWER
sudo apt update Y='y'
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncursesw5-dev libcodec2-dev if [[ $Y == $ANSWER ]]; then
git clone https://github.com/lwvmobile/mbelib
cd mbelib sudo apt update
git checkout ambe_tones 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
mkdir build
cd build echo ITPP Manual Build and Install has been temporarily removed from this script due to issues with older versions of Ubuntu.
cmake .. echo Please check to see that libitpp-dev has successfully been downloaded and installed from the repo.
make -j $(nproc) read -p "Press enter to continue"
sudo make install
sudo ldconfig #wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
cd $cdir #tar xjf itpp*
git clone https://github.com/lwvmobile/dsd-fme #if you can't cd into this folder, double check folder name first
cd dsd-fme #cd itpp-4.3.1
#git checkout audio_work #mkdir build
mkdir build #cd build
cd build #cmake ..
cmake .. #make -j `nproc`
make -j $(nproc) #sudo make install
sudo make install #sudo ldconfig
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
sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo cp tone48.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
sudo chmod 777 /usr/share/tone48.wav
mkdir build
cd build
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig
else else
printf "\nSorry, you cannot build DSD-FME without acknowledging the Patent Notice.\nExiting...\n\n" echo
exit 2 echo Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.
fi fi

Binary file not shown.

Before

Width:  |  Height:  |  Size: 725 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 251 KiB

View File

@@ -1,74 +0,0 @@
## Audio Plumbing
While using DSD-FME to demodulate and decode RF Audio, you will find it necessary to properly plumb (or route) audio from each input source and output sink properly. This mini tutorial aims to help you do such when using Pulse Audio.
You will first want to locate and run the virtualsink.sh script file via `sh virtualsink.sh` command. This will create two null-sink, or 'virtual sinks' for us to plumb audio from point A to point B.
Here are screenshots of a proper setup using pavucontrol, or "Pulse Audio Volume Control", having voice input from the encoder listening to your microphone, the RF Output going into the "Virtual_Sink" null-sink, and on the playback side, playing back voice to our speakers, with RF modulated audio being monitored on "Virtual-Sink" null-sink.
![Audio 1](https://github.com/lwvmobile/dsd-fme/blob/aw_dev/examples/pavucontrol_plumbing1.png)
![Audio 2](https://github.com/lwvmobile/dsd-fme/blob/aw_dev/examples/pavucontrol_plumbing2.png)
The general idea is that we want to route RF modulated audio into our Virtual_Sink and have our decoder listen to the same sink. Voice Output should always be to Audio Hardware (speakers, headphones, etc).
Note, that using the virtualsink.sh will create these sinks for us for the duration of your computers boot. If you reboot, they will not persist. Configuration files can be modified to always have these sinks available, or the `virtualsink.sh` script can be executed on login, but you must do so at your own risk and own research, it is out of the scope of this tutorial to create persistent sinks.
In case of the usage of a headless environment and pavucontrol is not viable, you may use alternatives such as pulsemixer which is similar in functionality, just as a CLI tool and not a GUI tool.
Users can now use the `-O` CLI argument to list out all pulse audio input sources and output sinks and pass an optional argument alongside `pulse`, appending the name or index number to select the device each time. This method will work in both Linux environments as well as Cygwin builds (making sure to start the pulseaudio.exe server in the back first with the specified .bat file or command, see below).
For Example:
The Output (truncated) of `dsd-fme -O`
```
=======[ Output Device #1 ]=======
Description: Family 17h (Models 00h-0fh) HD Audio Controller Analog Stereo
Name: alsa_output.pci-0000_0d_00.3.analog-stereo
Index: 1
=======[ Output Device #4 ]=======
Description: Virtual_Sink1
Name: virtual_sink1
Index: 4
=======[ Output Device #5 ]=======
Description: Virtual_Sink2
Name: virtual_sink2
Index: 5
=======[ Input Device #2 ]=======
Description: Family 17h (Models 00h-0fh) HD Audio Controller Analog Stereo
Name: alsa_input.pci-0000_0d_00.3.analog-stereo
Index: 2
=======[ Input Device #5 ]=======
Description: Monitor of Virtual_Sink1
Name: virtual_sink1.monitor
Index: 5
=======[ Input Device #6 ]=======
Description: Monitor of Virtual_Sink2
Name: virtual_sink2.monitor
Index: 6
```
You might use any of the following for input and output options:
```
-i pulse
-i pulse:6
-i pulse:virtual_sink2.monitor
-o pulse
-o pulse:1
-o pulse:alsa_output.pci-0000_0d_00.3.analog-stereo
```
Something to keep in mind is that often times, you only need to set these values once, as a pulse audio server (or plugin) can often times remember these settings, even across reboots, as long as the same hardware is present for future use (real or null-sink).
<!-- TODO: Add This later -->
<!-- Cygwin Note: Running the supplied `0p - start-pulse-audio-backend.bat` file will not only start up the pulse audio server for you, but also setup your null-sinks, and enumerate all devices for you to use, making it an easy selection for your start up .bat files. See any notes in the precomplied .zip file for more information. -->

View File

@@ -1,30 +1,25 @@
### Example Usage and Notes! ### Example Usage and Notes!
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, and P25. To use other decoding methods which cannot be auto detected, please use the following command line switches. `dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, and P25 (1 and 2) . To use other decoding methods which cannot be auto detected, please use the following command line switches. Make sure to route audio into and out of DSD-FME using pavucontrol and virtual sinks as needed.
``` ```
-ft XDMA decoder class (P25p1 and p2, YSF, and DMR Stereo) -ft XDMA decoder class (P25 1 and 2, and DMR Stereo)
-fs DMR Stereo, also may need to use -xr if inverted DMR -fs DMR Stereo, also may need to use -xr if inverted DMR
-fa Legacy Auto (not recommended) -fa Legacy Auto (not recommended)
-fi NXDN48 -fi NXDN48
-fn NXDN96 -fn NXDN96
-fp Provoice -fp ProVoice
-fh EDACS Standard / Network
-fH EDACS Standard / Network with ESK
-fe EDACS Extended Addresssing
-fE EDACS Extended Addressing with ESK
-fm dPMR, also may need to use -xd if inverted dPMR. -fm dPMR, also may need to use -xd if inverted dPMR.
-fr DMR Mono, also may need to use -xr if inverted DMR.
-f1 P25P1 -f1 P25P1
-f2 P25P2 (may need to specify wacn/sys/nac manually if traffic channel) -f2 P25P2 (will need to specify wacn/sys/nac manually)
-fx X2-TDMA -fx X2-TDMA
-fy YSF
-fz M17 Audio
``` ```
## Conventional Frequency Scanner ## New -- Experimental Conventional Frequency "Fast Scanner"
This feature is used to allow DSD-FME to use RIGCTL or RTL input and cycle through frequencies to attain a frame sync. This feature is used to allow DSD-FME to use RIGCTL or RTL input and cycle through frequencies as fast as possible to attain a frame sync.
This works almost identically to how the trunkng methods work, but instead of locking onto a control channel and tuning based on its decoding, it tunes through all loaded frequencies in a CSV file and stops when it finds a frame sync. This method is meant for loading a bunch of conventional non-trunking frequencies that signal only when voice or short data bursts are present (DMR T2, Conventional P25 P1, Conventional NXDN, etc) and decode them until the frame sync stops and then resume its scan. This method uses SDR++ RIGCTL, or RTL internal handling to go through signal. -Y is the scan switch. Note: This method will NOT perform at a level similar to a real hardware scanner, SDR tuning is not nearly as fast or reliable at locking onto signal. This works almost identically to how the trunkng methods work, but instead of locking onto a control channel and tuning based on its decoding, it rapidly tunes through all loaded frequencies in a CSV file and stops when it finds a frame sync. This method is meant for loading a bunch of conventional non-trunking frequencies that signal only when voice or short data bursts are present (DMR T2, Conventional P25 P1, Conventional NXDN, etc) and decode them until the frame sync stops and then resume its scan. This method uses SDR++ RIGCTL, or RTL internal handling to go through signal. -Y is the fast scanner switch.
To scan through a mixture of DMR and P25 Conventional, use: To scan through a mixture of DMR and P25 Conventional, use:
`dsd-fme -i tcp -C channel_map.csv -G group.csv -Y -U 4532 -N 2> log.ans` `dsd-fme -i tcp -C channel_map.csv -G group.csv -Y -U 4532 -N 2> log.ans`
@@ -32,12 +27,12 @@ To scan through a mixture of DMR and P25 Conventional, use:
To scan through NXDN48 only conventional, use: To scan through NXDN48 only conventional, use:
`dsd-fme -fi -i tcp -C channel_map.csv -Y -U 4532 -N 2> log.ans` `dsd-fme -fi -i tcp -C channel_map.csv -Y -U 4532 -N 2> log.ans`
To scan through NXDN96 only conventional, use: To scan through NXDN98 only conventional, use:
`dsd-fme -fn -i tcp -C channel_map.csv -Y -U 4532 -N 2> log.ans` `dsd-fme -fn -i tcp -C channel_map.csv -Y -U 4532 -N 2> log.ans`
The Channel Map setup will be identical to any of the channel maps found for trunking systems, channel numbers do not matter and can be arbitrarily organized, and frequencies can be duplicated to be 'scanned' more frequently in larger CSV files. Scan speed by default is 1 second, and is most stable at that speed. This can be adjusted by using the `-t 2` option, for example, to specify 2 seconds of hang time per frequency. ~~If activity occurs on a frequency, an additional 2 seconds of 'hangtime' will occur on that frequency for any follow up voice activity.~~ Using `-t 0` will forego the one second wait time, and this can work in some situations, but is not advised as it may skip during the middle of transmissions, or skip over them entirely without a framesync. The Channel Map setup will be identical to any of the channel maps found for trunking systems, channel numbers do not matter and can be arbitrarily organized, and frequencies can be duplicated to be 'scanned' more frequently in larger CSV files. Scan speed is purely set by your CPU/GPU, and also by other factors like using SDR++ server to connect to a SDR++ client, or RTL_TCP server to client, etc, so faster computers scan faster, and slower will scan slower, slower scans may require multiple duplicate entries into your scanner csv file if you make a really large one.
Please Note: DMR Simplex may not behave well on scan, I have not been able to test it as such, but given the nature of the on-off timeslot signalling on DMR Simplex, missed frame syncs and skipping/resuming scan may occur. Please Note: DMR Simplex may not behave well on fast scan, I have not been able to test it as such, but given the nature of the on-off timeslot signalling on DMR Simplex, missed frame syncs and skipping/resuming scan may occur.
## Trunking Examples ## Trunking Examples
Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation. Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation.
@@ -49,9 +44,7 @@ and in a second terminal tab, same folder, run
`tail -n 40 -f voice.log` `tail -n 40 -f voice.log`
### Audio Plumbing 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)
Please see [Audio Plumbing](https://github.com/lwvmobile/dsd-fme/blob/aw_dev/examples/Audio_Plumbing.md "Audio Plumbing") for general help with audio plumbing (routing from A to B).
### Input ### ### Input ###
@@ -70,33 +63,26 @@ Please see [Audio Plumbing](https://github.com/lwvmobile/dsd-fme/blob/aw_dev/exa
Wav File Input Note: Due to 96000 rate audio requiring me to double the symbol rate and center, be sure to use the -s 96000 at the very end of the startup command. Also, some NXDN48/96 may have difficulties decoding properly with wav file input. Wav File Input Note: Due to 96000 rate audio requiring me to double the symbol rate and center, be sure to use the -s 96000 at the very end of the startup command. Also, some NXDN48/96 may have difficulties decoding properly with wav file input.
## Example Usage - RTL2832 Dongle Input ## Example Usage - RTL2832 Dongle Input
Short Setup (all default values):
`dsd-fme -i rtl`
Detailed Setup:
`dsd-fme -ft -i rtl:0:154.9875M:26:-2:8:0:6020 ` `dsd-fme -ft -i rtl:0:154.9875M:26:-2:8:0:6020 `
``` ```
RTL-SDR options: RTL-SDR options:
Usage: rtl:dev:freq:gain:ppm:bw:sq:vol WARNING! Old CLI Switch Handling has been depreciated in favor of rtl:<parms>
NOTE: all arguments after rtl are optional now for trunking, but user configuration is recommended Usage: rtl:dev:freq:gain:ppm:bw:sq:udp
dev <num> RTL-SDR Device Index Number dev <num> RTL-SDR Device Index Number
freq <num> RTL-SDR Frequency (851800000 or 851.8M) freq <num> RTL-SDR Frequency (851800000 or 851.8M)
gain <num> RTL-SDR Device Gain (0-49)(default = 0; Hardware AGC recommended) gain <num> RTL-SDR Device Gain (0-49) (default = 26)(0 = Hardware AGC, not recommended)
ppm <num> RTL-SDR PPM Error (default = 0) ppm <num> RTL-SDR PPM Error (default = 0)
bw <num> RTL-SDR Bandwidth kHz (default = 12)(8, 12, 16, 24) bw <num> RTL-SDR VFO Bandwidth kHz (default = 12)(6, 8, 12, 24)
sq <num> RTL-SDR Squelch Level vs RMS Value (Optional) sq <num> RTL-SDR Squelch Level (0 - Open, 25 - Little, 50 - Higher)
vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3) udp <num> RTL-SDR UDP Remote Port (default = 6020)
Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:12:0:6021
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2
``` ```
## Example Usage - Extra Payload/PDU Logging ## Example Usage - Extra Payload/PDU Logging
`dsd-fme -Z -N 2> log.ans` `dsd-fme -Z -N 2>> log.ans`
and in a second terminal tab, same folder, run and in a second terminal tab, same folder, run
@@ -106,11 +92,11 @@ and in a second terminal tab, same folder, run
EDACS Trunking (w/ channel map import) EDACS Trunking (w/ channel map import)
--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (see switches above for STD/NET, EA, and ESK modes) --EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (Tested and Working on EDACS/EDACS-EA with Provoice only, no analog voice monitoring)
`dsd-fme -i tcp -fE -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans` `dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans`
--NXDN48 Trunking (Direct Frequency Assignment) with SDR++ (untested for frequency accuracy) --NXDN48 Trunking (standard band plan) with SDR++ (untested for frequency accuracy)
`dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans` `dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans`
@@ -126,11 +112,11 @@ EDACS Trunking (w/ channel map import)
`dsd-fme -i tcp -T -U 4532 -N 2> log.ans` `dsd-fme -i tcp -T -U 4532 -N 2> log.ans`
--P25 Trunking (QPSK) with P1 Control Channel (Should switch symbol rate and center on Phase 2 audio channels) --P25 Trunking (CQPSK) with P1 Control Channel (Should switch symbol rate and center on Phase 2 audio channels)
`dsd-fme -i tcp -T -U 4532 -N -mq 2> log.ans` `dsd-fme -i tcp -T -U 4532 -N -mq 2> log.ans`
--P25 Trunking Phase 2 TDMA Control Channel systems with QPSK (non Phase 1 systems) --P25 Trunking Phase 2 TDMA Control Channel systems with CQPSK (non Phase 1 systems)
`dsd-fme -i tcp -T -U 4532 -N -f2 -m2 2> log.ans` `dsd-fme -i tcp -T -U 4532 -N -f2 -m2 2> log.ans`
@@ -138,22 +124,20 @@ Trunking Note1: All samples above can also be run with the RTL input method and
`dsd-fme -fp -i rtl:0:851.8M:22:-2:24:0:6020 -T -C channel_map.csv -G group.csv -N 2> log.ans` `dsd-fme -fp -i rtl:0:851.8M:22:-2:24:0:6020 -T -C channel_map.csv -G group.csv -N 2> log.ans`
Trunking Note2: QPSK Phase 1 and Phase 2 Systems may be subceptible to LSM distortion issues, but seem to do okay, but require really good signal. Some CRC issues still occur with Phase 2 TDMA LCCH Mac Signal that can affect reliability, I believe this issue is ultimately caused by the PSK demodulation inside of FME. I also don't believe this will work on 8-level PSK, but I cannot determine that at the moment. Update: I have improved the LCCH Mac Signal decoding my increasing the QPSK decision point buffers to their maximum values. Trunking Note2: CQPSK Phase 1 and Phase 2 Systems are subceptible to LSM distortion issues, but seem to do okay, but require really good signal. Some CRC issues still occur with Phase 2 TDMA LCCH Mac Signal that can affect reliability, I believe this issue is ultimately caused by the PSK demodulation inside of FME. I also don't believe this will work on 8-level PSK, but I cannot determine that at the moment. Update: I have improved the LCCH Mac Signal decoding my increasing the QPSK decision point buffers to their maximum values.
Trunking Note3: DMR Trunking has been coded, and some testing and tweaks have been carried out. Cap+, Con+, and TIII systems seem to do well with trunking now. Placing the frequency for the control channel at channel map 0 in your channel_map.csv file is not required now if using RIGCTL or the RTL Input, both can poll the VFO for the current frequency if it believes its on a control channel, but setting a fake channel number (i.e. 999) first with the CC frequency will result in finding the CC faster on startup if desired. If you need to map out your channels for TIII, you can observe the console output and look for channel numbers. For conveniece I have included the DSDPlus style LSN-esque numbering into the console print so it will make it easier for users from DSDPlus to map frequencies into the channel_map.csv file. Make sure your channel numbers are the lpcn (12-bit logical physical channel number - decimal format) values from the log, and not the LPCN+TS (13-bit DSDPlus LSN) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area. Trunking Note3: DMR Trunking has been coded, and some testing and tweaks have been carried out. Cap+, Con+, and TIII systems seem to do well with trunking now. Placing the frequency for the control channel at channel map 0 in your channel_map.csv file is not required now if using RIGCTL or the RTL Input, both can poll the VFO for the current frequency if it believes its on a control channel, but setting a fake channel number (i.e. 999) first with the CC frequency will result in finding the CC faster on startup if desired. If you need to map out your channels for TIII, you can observe the console output and look for channel numbers. For conveniece I have included the DSDPlus channel numbering (as best as I can figure it, but they seem consistent) into the console print so it will make it easier for users from DSDPlus to map frequencies into the channel_map.csv file. Make sure your channel numbers are the Cd (channel decimal) values from the log, and not the C+ (dsdplus) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area.
``` ```
00:43:00 Sync: +DMR [slot1] slot2 | Color Code=00 | CSBK
Talkgroup Voice Channel Grant (TV_GRANT) - Logical Talkgroup Voice Channel Grant (TV_GRANT) - Logical
LPCN: 0075; TS: 2; LPCN+TS: 0152; Target: 16518173 - Source: 16533625 Ch [036] Cd [0054] C+ [0110] - TS [1] - Target [01900500] - Source [01900505]
``` ```
Use channel 75 in your import file, which would correspond to dsdplus channels 151 and 152 (151 = TS1 / 152 = TS2). Use channel 54 in your import file, which would correspond to dsdplus channels 109 and 110 (109 = TS0 / 110 = TS1).
For Connect Plus, enumerate your list from 1 to the last channel and add the frequency. For Capacity Plus, LSN 1 and 2 will share the same frequency, 3 and 4 will share, 5 and 6 will share, and 7-8 will share, as Capacity Plus counts each 'channel' as two seperate channels (LSN), one for each time slot. Capacity Plus Quirk: DSD-FME makes its best effort to follow the rest channel in the event that the sync is lost for longer than the hangtime, but occassionally, DSD-FME may lose the rest channel and will have to hunt through all frequencies to find it again. For Connect Plus, enumerate your list from 1 to the last channel and add the frequency. For Capacity Plus, Rest Channels 1 and 2 will share the same frequency, 3 and 4 will share, 5 and 6 will share, and 7-8 will share, as Capacity Plus counts each 'channel' as two seperate channels (LSN), one for each time slot. Capacity Plus Quirk: DSD-FME makes its best effort to follow the rest channel in the event that the sync is lost for longer than the hangtime, but occassionally, DSD-FME will lose the rest channel and will have to hunt through all frequencies to find it again.
Trunking Note4: NXDN Trunking v1 will require a channel map. Please see the example folder for an appropriate channel map. NOTICE: NXDN trunking has been fixed on the RTL (rtl_fm) input method and works correctly now using a soft squelch value to determine when to look for frame sync. Tests show this works very well, but squelch adjustments may be required to differentiate the noise floor from the signal. Trunking Note4: NXDN Trunking v1 will require a channel map. Please see the example folder for an appropriate channel map. NOTICE: NXDN trunking will not work properly with RTL input method due to the internal handling of the RTL dongle, when using squelch, it will effectively stop all processing (including trunking/tuning) until signal is regained, but squelch is required on NXDN to prevent false sync patterns.
NXDN Trunking Update: NXDN DFA (Direct Frequency Assignment) has been coded from the v2 documents, so using a channel map may not be required, as long as the DFA is configured with standard values, and not 'system definable' values. This will only work on trunking systems that use DFA (newer systems) in accordance to the v2 CAI document NXDN TS 1-A Version 2.0 September 2016. If the system uses 'system definable' values, then make sure to put the channels in a channel map csv file as as the 16-bit OFN values for proper tuning. NXDN Trunking Update: NXDN DFA (Direct Frequency Assignment) has been coded from the v2 documents, so using a channel map may not be required, as long as the DFA is configured with standard values, and not 'system definable' values. This will only work on trunking systems that use DFA (newer systems) in accordance to the v2 CAI document NXDN TS 1-A Version 2.0 September 2016. If the system uses 'system definable' values, then make sure to put the channels in a channel map csv file as as the 16-bit OFN values for proper tuning.
@@ -161,7 +145,7 @@ NXDN Type-D/IDAS Update: Type-D or IDAS decoding/trunking has been coded now and
Channel Map and Group CSV Note: Leave the top line of the channel_map.csv and group.csv as the label, do not delete the line, if no line is there, dsd_import skips the first line so it will not import the first channel or first group in those files if there is something there that isn't a label. Channel Map and Group CSV Note: Leave the top line of the channel_map.csv and group.csv as the label, do not delete the line, if no line is there, dsd_import skips the first line so it will not import the first channel or first group in those files if there is something there that isn't a label.
Hytera XPT: Code has been added for XPT system slco/flco/csbk decoding and trunking. The setup will be similar to Capacity Plus trunking in the csv file, listing each LSN channel to a frequency (see examples/hytera_xpt_chan.csv). Currently, this set up is working with smaller XPT systems that I have been able to test with. Hytera XPT: Experimental Code has been added for XPT system slco/flco/csbk decoding and trunking. The setup will be similar to Capacity Plus trunking in the csv file, listing each LSN channel to a frequency (see examples/hytera_xpt_chan.csv). Currently, this set up is working with smaller XPT systems that I have been able to test with.
## NCurses Keyboard Shortcuts ## ## NCurses Keyboard Shortcuts ##
@@ -171,210 +155,27 @@ Some Keyboard Shortcuts have been implemented for testing to see how users like
esc or arrow keys - ncurses menu esc or arrow keys - ncurses menu
q - quit q - quit
c - toggle compact mode c - toggle compact mode
h - cycle event history short, long, or off h - toggle call history
z - toggle console payloads z - toggle console payloads
a - toggle call alert beep a - toggle call alert beep
4 - force privacy/scrambler key assertion over enc identifiers (dmr and nxdn) 4 - force dmr privacy key assertion over fid and svc bits
6 - force rc4 key assertion over missing pi header/le enc identifiers (dmr)
i - toggle signal inversion on types that can't auto detect (dmr, dpmr) i - toggle signal inversion on types that can't auto detect (dmr, dpmr)
m - toggle c4fm/qpsk 10/4 (everything but phase 2 signal) m - toggle c4fm/qpsk 10/4 (everything but phase 2 signal)
M - toggle c4fm/qpsk 8/3 (phase 2 tdma control channel) M - toggle c4fm/qpsk 8/3 (phase 2 tdma control channel)
R - start capturing symbol capture bin (date/time name file) R - start capturing symbol capture bin (date/time name file)
r - stop capturing symbol capture bin r - stop capturing symbol capture bin
spacebar - replay last symbol capture bin (captures must be stopped first) spacebar - replay last symbol capture bin (captures must be stopped first)
s - stop playing symbol capture bin or wav input file (lower s) s - stop playing symbol capture bin or wav input file
P - start per call decoded wav files (Capital P) P - start per call decoded wav files
p - stop per call decoded wav files (lower p) p - stop per call decoded wav files
t - toggle trunking (needs either rtl input, or rigctl connection) t - toggle trunking (needs either rtl input, or rigctl connection)
y - toggle scanner (needs either rtl input, or rigctl connection) y - toggle scanner (needs either rtl input, or rigctl connection)
1 - Toggle Voice Synthesis in TDMA Slot 1 or FDMA Conventional Voice 1 - Lockout Tuning/Playback of TG in Slot 1 or Conventional (Current Session Only)
2 - Toggle Voice Synthesis in TDMA Slot 2 2 - Lockout Tuning/Playback of TG in Slot 2 (Current Session Only)
0 - Toggle Audio Smoothing - May produce crackling if enabled on RTL/TCP or wav/bin files
w - Toggle Trunking/Playback White List (Allow A Groups Only) / Black List (Block B or DE groups only) Mode w - Toggle Trunking/Playback White List (Allow A Groups Only) / Black List (Block B or DE groups only) Mode
g - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25, NXDN) g - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25)
u - Toggle Trunking Tuning to Private Calls (DMR T3, Cap+, P25) u - Toggle Trunking Tuning to Private Calls (DMR T3, P25)
d - Toggle Trunking Tuning to Data Calls (DMR T3, NXDN) d - Toggle Trunking Tuning to Data Calls (DMR T3)
e - Toggle Trunking Tuning to Encrypted Calls (P25)
8 - Connect to SDR++ TCP Audio Sink with Defaults/Retry Sink Connection on Disconnect
9 - Connect to SDR++ RIGCTL Server with Defaults/Retry RIGCTL Connection
D - Reset DMR Site Parms/Call Strings, etc.
Z - Simulate NoCarrier/No VC/CC sync (capital Z)
! - Lockout Tuning/Playback of TG in Slot 1 or Conventional -- Current Session Only if no group.csv file specified ```
@ - Lockout Tuning/Playback of TG in Slot 2 -- Current Session Only if no group.csv file specified
k - Hold TG in Slot 1 or Conventional, or clear current hold
l - Hold TG in Slot 2 on TDMA Systems, or clear current hold
C - Drop Call and Return to CC during trunking operation
L - Manual Cycle Forward Channel Tuned when RIGCTL or using RTL input and channel csv loaded
S - Toggle between EDACS Standard/Network and Extended Addressing mode (Capital S)
A - Toggle EDACS ESK Mask (none vs A0)
H - Toggle High Pass Filter on Digital Sythesized Voice
V - Toggle Low Pass Filter on Analog Voice or Raw Signal Monitoring
B - Toggle High Pass Filter on Analog Voice or Raw Signal Monitoring
N - Toggle Passband Filter on Analog Voice or Raw Signal Monitoring
[ - Scroll backwards through Event History
] - Scroll forwards through Event History
\ - Switch between Slot 1 and Slot 2 Event History
v - Cycle RTL Input Volume Multiplier 1X, 2X, 3X (lower v)
```
## Sending Audio to a Icecast 2 Server via FFmpeg (Windows)
Make sure to enable "Stereo Mix" from Volume Control Panel and then disable Windows Sounds.
```
ffmpeg -f dshow -i audio="Stereo Mix (Realtek High Definition Audio)" -c:a aac -b:a 64k -content_type 'audio/aac' -vn -f adts icecast://source:password@192.168.5.251:8844/DSDFME
```
## Sending Audio to a Icecast 2 Server via FFmpeg (Linux w/ Pulse Audio)
`pactl list short sources`
Choose Desired Device to Cast:
```
0 auto_null.monitor module-null-sink.c s16le 2ch 48000Hz IDLE
1 alsa_output.pci-0000_0d_00.3.analog-stereo.monitor module-alsa-card.c s16le 2ch 44100Hz RUNNING
2 alsa_input.pci-0000_0d_00.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz RUNNING
3 virtual_sink.monitor module-null-sink.c s16le 2ch 44100Hz RUNNING
4 virtual_sink2.monitor module-null-sink.c s16le 2ch 44100Hz RUNNING
7 alsa_output.pci-0000_0b_00.1.hdmi-stereo-extra1.monitor module-alsa-card.c s16le 2ch 44100Hz RUNNING
```
Example:
```
francis@12cores:~$ pactl list short sources
0 auto_null.monitor module-null-sink.c s16le 2ch 48000Hz IDLE
francis@12coresx:~$ ffmpeg -f pulse -i 0 -c:a libmp3lame -ab 64k -f mp3 icecast://source:password@XXXXXXXXX:8000
```
Note: Do not run this command for ffmpeg/icecast piped to the main dsd-fme command, make sure these are running in seperate terminals.
## Sending Audio to UDP Target/Port
Audio can be sent to a target address on your local area network with the UDP blaster using the `-o udp:targetaddress:port` option.
Note: Any analog audio monitoring will not function properly over UDP port (rate difference issues)
Example (Short 8k/2):
`dsd-fme -fs -o udp:192.168.7.8:23470`
Receiving End (Short 8k/2):
`socat stdio udp-listen:23470 | play --buffer 640 -q -b 16 -r 8000 -c2 -t s16 -`
Example (Short 8k/1):
`dsd-fme -f1 -o udp:192.168.7.8:23469`
Receiving End (Short 8k/1):
`socat stdio udp-listen:23469 | play --buffer 320 -q -b 16 -r 8000 -c1 -t s16 -`
Example (Float 8k/2):
`dsd-fme -ft -y -o udp:192.168.7.8:23468`
Receiving End (Float 8k/2):
`socat stdio udp-listen:23468 | play --buffer 1280 -q -b 16 -r 8000 -c2 -t f32 -`
Example (Float 8k/1):
`dsd-fme -fi -y -o udp:192.168.7.8:23467`
Receiving End (Float 8k/1):
`socat stdio udp-listen:23467 | play --buffer 640 -q -b 16 -r 8000 -c1 -t f32 -`
Note: --buffer option in play is calculated by (channels * samples * sizeof(samples)),
where samples is 160 for mono, 320 for stereo, and short samples are 2 and float samples are 4.
This is vital to prevent 'lag' or cut-off audio when the UDP audio doesn't fill the default buffer size.
Note2: Be sure to start the receiving end AFTER starting DSD-FME. If DSD-FME is restarted, make sure to restart the receiving end as well, as socat/UDP closes the UDP port listening when DSD-FME closes the UDP socket.
Note3: socat version 1.8 may have a `E xioopen_ipdgram_listen(): unknown address family 0` problem, downgrading to 1.7.4 is recommended if this occurs. `sudo downgrade socat`.
## Record 48k/1 audio with rtl_fm
You can record suitable audio to feed as input to dsd-fme with `-i` using rtl_fm:
`rtl_fm -M nfm -f [FREQ] -s 48k -g 42 -l 20 > input.wav`
You can also combine that with `socat` to send the audio to a remote dsd-fme instance in real time while keeping a local copy on the host where the RTL-SDR dongle is:
`rtl_fm -M fm -f [FREQ] -s 48k -g 42 -l 20 | tee input.wav | socat -u - TCP-LISTEN:1234,forever,reuseaddr`
On the remote side where dsd-fme is, use the TCP input:
`dsd-fme -i tcp:192.168.7.8:1234 -N`
Note: As always, when using rtl_fm, play with `-l` and `-g` to find a good squelch and gain values for your environment - you will know if the squelch is the right value when `input.wav` does not increase in size in the absence of useful signal on the monitored frequency.
## Using the Experimental M17 Encoder (and Decoder)
You can use the experimental M17 Voice Stream and Packet encoder with the following examples.
Encode/Decode Local Feedback Voice Stream (Single Session):
`dsd-fme -fZ -i pulse -o pulse -M M17:3:SP5WWP:BROADCAST:48000`
Using the `-8` option (raw audio monitor) will instead send encoded raw audio to output instead of decoded audio. For example,
To send a voice stream out of stdout into SOCAT TCP to a second/remote DSD-FME session to decode:
Encode (Upper Z):
`dsd-fme -fZ -8 -i pulse -M M17:9:LWVMOBILE:DSD-FME:48000 -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
Decode (Lower z):
`dsd-fme -fz -i tcp:192.168.7.8:7355`
NOTE: When using tcp input for M17 decoding, DSD-FME will keep the TCP connection alive with constant retries, so no need to restart the decoding session on connection failure.
SMS Packet Encoder (Text Only):
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it."`
SMS Packet Encoder (over SOCAT TCP):
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it and SOCAT to send it over TCP." -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
Many combinations of input and output can be used as well, and using features like raw audio saving with `-6 m17str.wav` will save a wav file of your encoded audio to be replayed later with `-i m17str.wav`
Also note, features function similarly, like -Z for payload, and input options include pulse, oss, stdin, rtl, tcp, etc, using those as the mic in for voice encoding. With the right setup of input/output/input using SOCAT TCP pipes, it is conceivable to use DSD-FME to convert analog audio from NFM into M17 audio, or even change another digital format supported by DSD-FME into M17 audio, if desired (purely for research and fun purposes). Ncurses terminal will function with internal encoder/decoder and wav file creation, with toggle encode/tx with `\` key, but will not function with stdout properly.
The `-M` switch is used to specify user M17 variables, the string is in the format of M17:CAN:SRC:DST:INPUT_RATE.
CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.
BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'
Input Rate Default is 48000; Use Multiples of 8000 up to 48000.
Values not entered into the M17: string are set to default values. M17:7:DSD-FME:DSD-FME:48000
Fun Example Transmuting OTA Analog Things into M17:
Start up in sequence.
Terminal #1 (RTL Input):
`dsd-fme -fZ -8 -M M17:1:ANALOG:M17:48000 -i rtl:0:162.425M -o - | socat stdio tcp-listen:7310,forever,reuseaddr`
Terminal #2:
`dsd-fme -fz -i tcp:127.0.0.1:7310`
Fun Example Transmuting OTA Digital Things into M17:
Start up all three of these in sequence.
Terminal #1 (connecting to SDR++):
`dsd-fme -fi -i tcp:127.0.0.1:7355 -o - | socat stdio tcp-listen:7300,forever,reuseaddr`
Terminal #2:
`dsd-fme -fZ -8 -M M17:2:NXDN:M17:8000 -i tcp:127.0.0.1:7300 -o - | socat stdio tcp-listen:7301,forever,reuseaddr`
Terminal #3:
`dsd-fme -fz -i tcp:127.0.0.1:7301`
Note: Transmuting only carries over voice when audio is output, and will not carry any ID values, or any other data payloads.
This mode is only meant for educational or fun purposes, please do not retransmit stuff that doesn't belong to you.
Information on this section will be updated as features are added or modified, etc. If you need help with a particular function and can't find a suitable answer, always open a new issue here, or make contact on the radio reference forum for additional examples, guidance, ideas, help, etc.

View File

@@ -1,80 +1,65 @@
## How to clone and build this branch ## How to clone and build this branch
### Ubuntu 22.04/20.04/LM20/Debian Bullseye: ### 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 23.04 and RPi Bullseye 64-bit has been tested working with the installer script and functions appropriately. Using the included download-and-install.sh should make for a simple and painless clone, build, and install on newer Debian/Ubuntu/Mint/Pi systems. Simply acquire or copy the script, and run it. Update: Ubuntu 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: If you need all dependencies build and installed first (only on Debian/Ubuntu/Mint/Pi), run:
``` ```
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/audio_work/download-and-install.sh wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install.sh
sh download-and-install.sh chmod +x download-and-install.sh
./download-and-install.sh
``` ```
### Debian 12 (SID) / Ubuntu 24.04 LTS (Updated Ncurses Dependencies) 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/audio_work/download-and-install-ubuntu2404lts.sh wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install-nodeps.sh
sh download-and-install-ubuntu2404lts.sh chmod +x download-and-install-nodeps.sh
./download-and-install-nodeps.sh
``` ```
### Arch Based Distros: ### 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.
```
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/audio_work/download-and-install-Arch.sh
sh download-and-install-Arch.sh
```
### Other:
If you have dependencies already installed, please run this instead:
```
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/audio_work/download-and-install-nodeps.sh
sh download-and-install-nodeps.sh
```
## Manual Install ## Manual Install
First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different. First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different.
Debian/Mint/Ubuntu/Pi
``` ```
sudo apt update sudo apt update
#Ubuntu 22.04 lts / Debian 11 and lower sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libcodec2-dev
#Ubuntu 24.04 lts / Debian 12
#sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncurses-dev libncurses6 libcodec2-dev
``` ```
## Headless
Fedora 36/37 -- from https://github.com/lwvmobile/dsd-fme/issues/99
```
sudo dnf update
sudo dnf install libsndfile-devel fftw-devel lapack-devel rtl-sdr-devel pulseaudio-libs-devel libusb-devel cmake git ncurses ncurses-devel gcc wget pavucontrol gcc-c++ codec2-devel
```
Arch -- https://github.com/lwvmobile/dsd-fme/issues/153 and https://github.com/lwvmobile/dsd-fme/issues/153
```
sudo pacman -Syu
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget rtl-sdr
```
## Headless Ubuntu Server/Pi
If running headless, swap out pavucontrol for pulsemixer, and also install pulseaudio as well. Attempting to install pavucontrol in a headless environment may attempt to install a minimal desktop environment. Note: Default behavior of pulseaudio in a headless environment may be to be muted, so check by opening pulsemixer and unmuting and routing audio appropriately. If running headless, swap out pavucontrol for pulsemixer, and also install pulseaudio as well. Attempting to install pavucontrol in a headless environment may attempt to install a minimal desktop environment. Note: Default behavior of pulseaudio in a headless environment may be to be muted, so check by opening pulsemixer and unmuting and routing audio appropriately.
``` ```
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncursesw5-dev pulsemixer pulseaudio libcodec2-dev sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev pulsemixer pulseaudio
``` ```
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") ### 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.2 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 future compatibility issues.
``` ```
git clone https://github.com/lwvmobile/mbelib git clone https://github.com/lwvmobile/mbelib
git checkout ambe_tones
cd mbelib cd mbelib
mkdir build mkdir build
cd build cd build
@@ -86,11 +71,20 @@ cd ..
cd .. cd ..
``` ```
Note: Some environment/distro users (Cygwin, Arch, and others) may need to manually copy any libmbe files found in the build folder to the /usr/lib folder on root.
`sudo cp libmbe* /usr/lib`
Finish by running these steps to clone and build DSD-FME. Finish by running these steps to clone and build DSD-FME.
``` ```
git clone https://github.com/lwvmobile/dsd-fme git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme cd dsd-fme
sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo cp tone48.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
sudo chmod 777 /usr/share/tone48.wav
mkdir build mkdir build
cd build cd build
cmake .. cmake ..
@@ -99,43 +93,9 @@ sudo make install
sudo ldconfig sudo ldconfig
``` ```
Optional 'Virtual Sinks' for routing audio from SDR++ or GQRX, Media Players, etc. into DSD-FME
### LD_PATH 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.
Some environments may require an LD path to be set to /usr/local/lib, similar to:
```
LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH
```
### Windows Cygwin Builds
Cygwin builds now have an experimental semi-automatic installer, to run, follow steps below:
Open Windows PowerShell (not Command Prompt) and copy and paste all of this in all at once.
```
Invoke-WebRequest https://cygwin.com/setup-x86_64.exe -OutFile setup-x86_64.exe
.\setup-x86_64.exe --packages nano,libpulse-devel,libpulse-mainloop-glib0,libpulse-simple0,libpulse0,pulseaudio,pulseaudio-debuginfo,pulseaudio-equalizer,pulseaudio-module-x11,pulseaudio-module-zeroconf,pulseaudio-utils,sox-fmt-pulseaudio,libusb0,libusb1.0,libusb1.0-debuginfo,libusb1.0-devel,libncurses++w10,libncurses-devel,libncursesw10,ncurses,cmake,gcc-core,gcc-debuginfo,gcc-objc,git,make,socat,sox,sox-fmt-ao,zip,unzip,wget,gcc-g++,libsndfile-devel
```
Pick a Mirror. http://www.gtlib.gatech.edu mirror seems relatively fast. Nurse the Cygwin installer by clicking next and waiting for it to finish. Ignore the warning popup telling you to install libusb from sourceforge. Install the [Zadig](https://zadig.akeo.ie/ "Zadig") driver instead, if you haven't already and have an RTL Dongle. After Cygwin finishes installing, the installer sh script will download and run, be patient, it may also take a little while.
Then:
```
C:\cygwin64\bin\mintty.exe /bin/bash -l -c "wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/refs/heads/audio_work/download-and-install-cygwin.sh; sh download-and-install-cygwin.sh; dsd-fme;"
```
After the sh script finishes, dsd-fme should open. If not, then double click on the Cygwin Terminal desktop shortcut, and try running `dsd-fme`. If you chose to create a portable version, you will find the folder and zip file in the `C:\cygwin64\home\username` directory if using the default cygwin64 folder location.
You can also update your versions by using the cyg_rebuild.sh script `sh cyg_rebuild.sh`.
### Virtual Sinks
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired. Note: This setup is completely optional if using TCP Network Sink Audio and/or RTL Input. You may need to manually install either of these programs manually, if desired, or find suitable alternatives for pipewire-pulse such as pwvucontrol, or similar.
``` ```
pactl load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink pactl load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
@@ -155,31 +115,13 @@ sudo make install
sudo ldconfig sudo ldconfig
``` ```
### Applying Patches -- Recommended For Advanced Users Only 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.
More experimental or potentially functionality changing or temperamental features may be included as seperate .patch files inside of the patch folder, and are not included in the source code by default.
If users wish to try or use these patches, then they can apply patches and build/rebuild with the following procedure. Make sure you have changed to the dsd-fme directory first, either by the top
command, or by otherwise opening the terminal to the clone folder.
``` ```
cd dsd-fme sudo cp tone8.wav /usr/share/
git apply patch/patchname.patch sudo cp tone24.wav /usr/share/
cd build sudo cp tone48.wav /usr/share/
make sudo chmod 777 /usr/share/tone8.wav
sudo make install sudo chmod 777 /usr/share/tone24.wav
``` sudo chmod 777 /usr/share/tone48.wav
```
If you later wish to update to the latest version via `git pull`, you may first need to reset any patches, perform the `git pull`, and then reapply patches.
A procedure for this is similar to, but not limited to:
```
cd dsd-fme
git reset --hard
git pull
git apply patch/xyz.patch
cd build
make
sudo make install
```
NOTE: Patches can potential break over time with further changes to code, requiring merge resolution or other manual intervention to re-apply patches.

View File

@@ -1,4 +1,4 @@
LSN(dec),frequency(Hz) (do not delete this line or won't import properly) ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
1,858606250 1,858606250
2,858606250 2,858606250
3,859606250 3,859606250
1 LSN(dec) ChannelNumber(dec) frequency(Hz) (do not delete this line or won't import properly)
2 1 858606250
3 2 858606250
4 3 859606250

View File

@@ -1,14 +0,0 @@
@REM Shutdown pulseaudio and clean out any remenants in /dev/shm if left behind
cd dsd-fme
pactl.exe exit
@REM now navigate to the /dev/shm folder and automatically remove any
@REM remnants from other times pulse wasn't shut down properly
cd ..
cd dev
cd shm
del pulse*
PAUSE

View File

@@ -1,34 +0,0 @@
@REM Set the console / terminal to 90 columns, 50 lines
@REM mode con: cols=90 lines=50
@ECHO OFF
@REM change directory into the dsd-fme folder
cd dsd-fme
@REM Start the Pulse Audio backend with a 10 minute time out
@REM start pulse audio server with 600 second idle time and no CPU limit (prevent closing while idle in menu)
@REM output from pulse server routed to NUL to supress "capabilities dropped, nag messages, etc" messages
pulseaudio.exe --start --no-cpu-limit=TRUE --exit-idle-time=600 2> NUL
@REM End pulse start up
ECHO The Pulse Audio Server should now be active!
@REM Setup Some null-sinks (if desired, remove @REM below)
@REM pactl load-module module-null-sink sink_name=virtual_sink1 sink_properties=device.description=Virtual_Sink1
@REM pactl load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
@REM Setup Default Sink to device 0 (output)
pactl set-default-sink 0
@REM List Avaiable Pulse Audio Sinks and Sources
@ECHO ON
dsd-fme -O
PAUSE

View File

@@ -1,224 +0,0 @@
Build Version: AW 2025-209-g14edf32
MBElib Version: 1.3.4
CODEC2 Support Enabled
Usage: dsd-fme [options] Decoder/Trunking Mode
or: dsd-fme [options] -r <files> Read/Play saved mbe data from file(s)
or: dsd-fme -h Show help
Display Options:
-N Use NCurses Terminal
dsd-fme -N 2> console_log.txt
-Z Log MBE/PDU Payloads to console
Device Options:
-O List All Pulse Audio Input Sources and Output Sinks (devices).
Input/Output options:
-i <device> Audio input device (default is pulse)
/dev/dsp for OSS audio (Depreciated: Will require padsp wrapper in Linux)
pulse for pulse audio signal input
pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O)
rtl for rtl dongle (Default Values -- see below)
rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)
tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)
tcp:192.168.7.5:7355 for custom address and port
filename.bin for OP25/FME capture bin files
filename.wav for 48K/1 wav files (SDR++, GQRX)
filename.wav -s 96000 for 96K/1 wav files (DSDPlus)
(Use single quotes '/directory/audio file.wav' when directories/spaces are present)
-s <rate> Sample Rate of wav input files (48000 or 96000) Mono only!
-o <device> Audio output device (default is pulse)
/dev/dsp for OSS audio (Depreciated: Will require padsp wrapper in Linux)
pulse for pulse audio decoded voice or analog output
pulse:1 or pulse:alsa_output.pci-0000_0d_00.3.analog-stereo for pulse audio decoded voice or analog output on device (see -O)
null for no audio output
udp for UDP socket blaster output (default host 127.0.0.1; default port 23456)
udp:192.168.7.8:23470 for UDP socket blaster output (Target Address and Port
-d <dir> Create mbe data files, use this directory (TDMA version is experimental)
-r <files> Read/Play saved mbe data from file(s)
-g <float> Audio Digital Output Gain (Default: 0 = Auto; )
(Manual: 1 = 2%; 50 = 100%)
-n <float> Audio Analog Output Gain (Default: 0 = Auto; 0-100% )
-6 <file> Output raw audio .wav file (48K/1). (WARNING! Large File Sizes 1 Hour ~= 360 MB)
-7 <dir> Create/Use Custom directory for Per Call decoded .wav file saving.
(Use ./folder for Nested Directory!)
(Use /path/to/folder for hard coded directory!)
(Use Before the -P option!)
-8 Enable Source Audio Monitor
(Set Squelch in RTL, SDR++ or GQRX, etc, if monitoring mixed analog/digital)
-w <file> Output synthesized speech to a single static .wav file. (Do not use with -P Per Call Switch)
-P Enable Per Call WAV file saving. (Do not use with -w filename.wav single wav file switch)
(Per Call works with everything now and doesn't require ncurses terminal!)
-a Enable Call Alert Beep
(Warning! Might be annoying.)
-J <file> Specify Filename for Event Log Output.
-L <file> Specify Filename for LRRP Data Output.
-Q <file> Specify Filename for OK-DMRlib Structured File Output. (placed in DSP folder)
-Q <file> Specify Filename for M17 Float Stream Output. (placed in DSP folder)
-c <file> Output symbol capture to .bin file
-q Reverse Mute - Mute Unencrypted Voice and Unmute Encrypted Voice
-V <num> Enable TDMA Voice Synthesis on Slot 1 (1), Slot 2 (2), or Both (3); Default is 3;
-z Set TDMA Voice Slot Preference when using /dev/dsp audio output (prevent lag and stuttering)
-y Enable Experimental Pulse Audio Float Audio Output
-v <hex> Set Filtering Bitmap Options (Advanced Option)
1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on
RTL-SDR options:
Usage: rtl:dev:freq:gain:ppm:bw:sq:vol
NOTE: all arguments after rtl are optional now for trunking, but user configuration is recommended
dev <num> RTL-SDR Device Index Number or 8 Digit Serial Number, no strings! (default 0)
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
gain <num> RTL-SDR Device Gain (0-49)(default = 0; Hardware AGC recommended)
ppm <num> RTL-SDR PPM Error (default = 0)
bw <num> RTL-SDR Bandwidth kHz (default = 12)(4, 6, 8, 12, 16, 24)
sq <num> RTL-SDR Squelch Level vs RMS Value (Optional)
vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)
Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2
Decoder options:
-fa Auto Detection
-fA Passive Analog Audio Monitor
-ft TDMA Trunking P25p1 Control and Voice, P25p2 Trunked Channels, and DMR
-fs DMR TDMA BS and MS Simplex
-f1 Decode only P25 Phase 1
-f2 Decode only P25 Phase 2 (6000 sps) **
-fd Decode only DSTAR
-fx Decode only X2-TDMA
-fy Decode only YSF
-fz Decode only M17*
-fi Decode only NXDN48* (6.25 kHz) / IDAS*
-fn Decode only NXDN96* (12.5 kHz)
-fp Decode only ProVoice*
-fh Decode only EDACS Standard/ProVoice*
-fH Decode only EDACS Standard/ProVoice with ESK 0xA0*
-fh344 Decode only EDACS Standard/ProVoice and set AFS to 344 or similar custom 11-bit scheme*
-fH434 Decode only EDACS Standard/ProVoice and set AFS to custom 11-bit scheme with ESK 0xA0*
-fe Decode only EDACS EA/ProVoice*
-fE Decode only EDACS EA/ProVoice with ESK 0xA0*
-fm Decode only dPMR*
-l Disable DMR, dPMR, NXDN, M17 input filtering
-u <num> Unvoiced speech quality (default=3)
-xx Expect non-inverted X2-TDMA signal
-xr Expect inverted DMR signal
-xd Expect inverted ICOM dPMR signal
* denotes frame types that cannot be auto-detected.
** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.
Advanced Decoder options:
-X <hex> Manually Set P2 Parameters (WACN, SYSID, CC/NAC)
(-X BEE00ABC123)
-_ <dec> Manually Set NXDN PN95 Dibit Scrambler Seed (1-511)
(-* 261)
-D <dec> Manually Set TIII DMR Location Area n bit len (0-10)(10 max)
(Value defaults to max n bit value for site model size)
(Setting 0 will show full Site ID, no area/subarea)
-ma Auto-select modulation optimizations
-mc Use only C4FM modulation optimizations (default)
-mg Use only GFSK modulation optimizations
-mq Use only QPSK modulation optimizations
-m2 Use P25p2 6000 sps QPSK modulation optimizations
-F Relax P25 Phase 2 MAC_SIGNAL CRC Checksum Pass/Fail
Use this feature to allow MAC_SIGNAL even if CRC errors.
-F Relax DMR RAS/CRC CSBK/DATA Pass/Fail
Enabling on some systems could lead to bad channel assignments/site data decoding if bad or marginal signal
-b <dec> Manually Enter Basic Privacy Key (Decimal Value of Key Number)
(NOTE: This used to be the 'K' option!)
-H <hex> Manually Enter Hytera 10/32/64 Char Basic Privacy Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-H 0B57935150
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)
-1 <hex> Manually Enter RC4 or DES Key Value (DMR, P25, NXDN) (Hex Value)
-2 <hex> Manually Enter and Enforce TYT 16-bit BP Key Value (DMR) (Hex Value)
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) 128 or 256 bit AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-! '736B9A9C5645288B 243AD5CB8701EF8A'
-! '1111111111111111 1111111111111111 1111111111111111 1111111111111111'
-@ <hex> Manually Enter and Enforce Retevis Advanced Privacy (RC2) 128 or 256 bit AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-@ '736B9A9C5645288B 243AD5CB8701EF8A'
-@ '1122334455667788 99AABBCCDDEEFF11 1122334455667788 99AABBCCDDEEFF11'
-5 <hex> Manually Enter and Enforce TYT Enhanced Privacy (AES-128) EP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-5 '736B9A9C5645288B 243AD5CB8701EF8A'
-+ <hex> Manually Enter and Enforce Baofeng Advanced Privacy (PC5-128 or PC5-256) AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC'
-+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC EF01EF01EF01EF01 EF01EF01EF01EF01'
-^ <hex> Manually Enter and Enforce Connect Systems 72-bit (9-byte) Extended Encryption Hex Key (see example below)
-^ 3BBE782C0430008271
-9 <dec> Manually Enter and Enforce Kenwood 15-bit Scrambler Key Value (DMR) (Dec Value)
-A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value)
-S <str> Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits)
For Example, enter 16-bit Keystream 0909 as:
-S 16:0909
For Example, enter 49-bit Keystream as:
-S 49:123456789ABC80
For Example, enter 49-bit Keystream (MBP 70) as:
-S 49:ED0AED4AED4AED4A
For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:
-S 49:11AA22BB33CC44
-k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.
Only supports NXDN, DMR Basic Privacy (decimal value).
(dPMR and Hytera 32/64 char not supported, DMR uses TG value as key id -- EXPERIMENTAL!!).
-K <file> Import Key List from csv file (Hexidecimal Format) -- Capital 'K'.
Use for Hex Value Hytera 10-char BP keys, RC4 10-char, DES 16-char Hex Keys, and AES128/256 32/64-char keys.
-4 Force Privacy Key over Encryption Identifiers (DMR MBP/HBP and NXDN Scrambler)
-0 Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR)
-M <hex> Force Entered Alg ID Value over Missing PI header/LE Encryption Identifiers (DMR)
-3 Disable DMR Late Entry Encryption Identifiers (VC6 Single Burst)
Note: Disable this if false positives on Voice ENC occur.
Trunking Options:
-C <file> Import Channel to Frequency Map (channum, freq) from csv file. (Capital C)
(See channel_map.csv for example)
-G <file> Import Group List Allow/Block and Label from csv file.
(See group.csv for example)
-T Enable Trunking Features (NXDN/P25/EDACS/DMR) with RIGCTL/TCP or RTL Input
-Y Enable Scanning Mode with RIGCTL/TCP or RTL Input
Experimental -- Can only scan for sync with enabled decoders, don't mix NXDN and DMR/P25!
This is not a Trunking Feature, just scans through conventional frequencies fast!
-W Use Imported Group List as a Trunking Allow/White List -- Only Tune with Mode A
-p Disable Tune to Private Calls (DMR TIII, P25, NXDN Type-C and Type-D)
-E Disable Tune to Group Calls (DMR TIII, Con+, Cap+, P25, NXDN Type-C, and Type-D)
-e Enable Tune to Data Calls (DMR TIII, Cap+, NXDN Type-C)
(NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls
-I <dec> Specify TG to Hold During Trunking (DMR, P25, NXDN Type-C Trunking)
-U <port> Enable RIGCTL/TCP; Set TCP Port for RIGCTL. (4532 on SDR++)
-B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF)
P25 - 12000; NXDN48 - 7000; NXDN96: 12000; DMR - 7000-12000; EDACS/PV - 12000-24000;
May vary based on system stregnth, etc.
-t <secs> Set Trunking or Scan Speed VC/sync loss hangtime in seconds. (default = 1 second)
Trunking Example TCP: dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.txt
Trunking Example RTL: dsd-fme -fs -i rtl:0:450M:26:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.txt

View File

@@ -1,97 +0,0 @@
This example options contains numerous options put into the start-dsd-fme.bat file in the option string
feel free to experiment and mix and match example options for your bat files option string.
Trunking Option Examples:
P25 (Phase 1 Control) Trunking with SDR++ Network Sink input, RIGCTL, /dev/dsp output,
per call wav files, ncurses terminal
-ft -i tcp:127.0.0.1:7355 -U 4532 -T -P -N
P25 (Phase 2 Control) Trunking with SDR++ Network Sink input, RIGCTL, /dev/dsp output,
per call wav files, ncurses terminal
-f2 -i tcp:127.0.0.1:7355 -U 4532 -T -P -N
DMR Trunking with RTL input, /dev/dsp output, channel map import,
per call wav files, ncurses terminal, payload dump to console
-fs -i rtl:0:450M:0:-2:12 -T -C examples\dmr_t3_chan.csv -P -N -Z
DMR Trunking with SDR++ input, RIGCTL, /dev/dsp output, RAS enabled CSBK (-F),
channel map import, per call wav files, ncurses terminal, payload dump to console
-fs -F -i tcp -U 4532 -T -C examples\capacity_plus_chan.csv -P -N -Z
NXDN48 v2.0 DFA (Direct Frequency Assignment) Trunking with SDR++ input, RIGCTL, /dev/dsp output,
scrambler key import (decimal), per call wav files, ncurses terminal, payload dump to console
-fi -i tcp -U 4532 -T -P -N -Z -k examples\nxdn_sc_key.csv
NXDN96 Trunking with SDR++ input, RIGCTL, /dev/dsp output,
channel map import, per call wav files, ncurses terminal, payload dump to console
-fn -i tcp -U 4532 -T -C examples\nxdn_chan_map.csv -P -N -Z
EDACS Standard Network Analog and Digital
-fh -i tcp -U 4532 -T -C edacs_channel_map.csv -G group.csv -N
-fh -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N
EDACS Extended Address Analog and Digital (with ESK)
-fE -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N
-fE -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N
Scanning Option Examples:
Scanning will not perform quickly like a real scanner, but can be used with any
supported decoding type and analog as well, and will require a channel_map to scan frequencies
channel numbers do not matter, but use unique ones for each frequeny
-t 0 will scan as fast as possible, but may miss frame sync, -t 1 is stable, but needs 1 second between tuning
P25/DMR/Analog Scanning with RTL input, /dev/dsp output, channel map import,
per call wav files, ncurses terminal, payload dump to console, raw audio signal monitoring
-ft -i rtl:0:450M:0:-2:12 -Y -t 1 -C examples\dmr_t3_chan.csv -P -8 -N -Z
P25/DMR/Analog Scanning with SDR++ input (use squelch), RIGCTL, /dev/dsp output, channel map import,
per call wav files, ncurses terminal, payload dump to console, raw audio signal monitoring
-ft -i tcp:127.0.0.1:7355 -U 4532 -Y -t 1 -C examples\dmr_t3_chan.csv -P -8 -N -Z
NXDN48/Analog Scanning with SDR++ input (use squelch), RIGCTL, /dev/dsp output, channel map import,
per call wav files, ncurses terminal, payload dump to console, raw audio signal monitoring
-fi -i tcp:127.0.0.1:7355 -U 4532 -Y -t 1-C examples\nxdn_chan_map.csv -P -8 -N -Z
Analog Scanning with SDR++ input (use squelch), RIGCTL, /dev/dsp output, channel map import
-fA -i tcp:127.0.0.1:7355 -U 4532 -Y -t 0 -C examples\nxdn_chan_map.csv -8
Encryption Option Examples:
DMR with default /dev/dsp input and output, use basic privacy key 70
-fs -b 70
DMR with default /dev/dsp input and output, load .csv file with decimal basic privacy keys loaded by TG value,
-fs -k examples\multi_key.csv
DMR with default /dev/dsp input and output, use Hytera basic privacy key (10, 32, and 64 hex char versions)
-fs -H 1111111111
-fs -H '2222222222222222 3333333333333333'
-fs -H '2222222222222222 3333333333333333 4444444444444444 5555555555555555'
DMR with default /dev/dsp input and output, use RC4 key 0x7777777777
-fs -1 7777777777
DMR with default /dev/dsp input and output, use RC4 key 0x7777777777,
Force RC4 Key over Missing PI header/LE Encryption Identifiers (-0)
Disable Late Entry Encryption Signalling Identifiers (-3)
-fs -0 -3 -1 7777777777
DMR with default /dev/dsp input and output, load RC4 and 10-char HBP keys from .csv file (hexidecimal format)
-fs -K dsd-fme\examples\multi_key_hex.csv
P25 with default /dev/dsp input and output, use RC4 key 0x7777777777
-ft -1 7777777777
NXDN48 with default /dev/dsp input and output, use Scrambler key 32767
-fi -R 32767
NXDN96 with default /dev/dsp input and output, use Scrambler key 15000
-fn -R 15000
dPMR with default /dev/dsp input and output, use Scrambler key 32767
-fm -R 32767
NXDN48 with default /dev/dsp input and output, load scrambler keys from .csv file (decimal format)
-fi -k dsd-fme\examples\nxdn_sc_key.csv

View File

@@ -1 +0,0 @@
These .bat files are ONLY for use on the portable cygwin builds, do not use these in any other build environments!

View File

@@ -1,74 +0,0 @@
@REM Digital Speech Decoder: Florida Man Edition
@REM DSD-FME Cygwin Windows Portable Builds
@REM
@REM The source code of this software can be downloaded here:
@REM https://github.com/lwvmobile/dsd-fme
@REM
@REM If you paid for, or were otherwise fleeced for this software, DEMAND YOUR MONEY BACK!
@REM
@REM This software is designed to be used with an SDR receiver (like SDR++ or SDR#) with TCP input,
@REM RTL Input, media file playback over virtual cables or null-sinks, or a Discriminator Tap input
@REM
@REM The complete options list can be seen by viewing complete_usage_options.txt
@REM Many examples can be found in example_options.txt
@REM
@REM Enjoy!
@REM Regarding the options string (or any string in .bat files)
@REM If your string may contain spaces, set the first '"' before options, and then an ending '"'
@REM Incorrect: set options="-fs -N -Z"
@REM Correct: set "options=-fs -N -Z"
@REM set options to pass to dsd-fme
set "options= -N -Z "
@REM Set Date Time for log (sourced from: https://stackoverflow.com/questions/1192476/format-date-and-time-in-a-windows-batch-script)
@echo off
REM "%date: =0%" replaces spaces with zeros
set d=%date: =0%
REM "set yyyy=%d:~-4%" pulls the last 4 characters
set yyyy=%d:~-4%
set mm=%d:~4,2%
set dd=%d:~7,2%
set dow=%d:~0,3%
set d=%yyyy%%mm%%dd%
set t=%TIME: =0%
REM "%t::=%" removes semi-colons
REM Instead of above, you could use "%t::=-%" to
REM replace semi-colons with hyphens (or any
REM non-special character)
set t=%t::=%
set t=%t:.=%
set datetimestr=%d%_%t%
@REM @echo Long date time str = %datetimestr%
@REM random number is used so that two log files don't share the same name
set rnd=%RANDOM%
@REM set log file relative filepath
set "clog=.\logs\console_log_%datetimestr%_%rnd%.txt"
set "elog=.\logs\event_log_%datetimestr%_%rnd%.txt"
@REM create the log file now with touch
.\dsd-fme\touch.exe %clog%
.\dsd-fme\touch.exe %elog%
@REM Launch Tail to display the console log and event log in a seperate console windows
start .\dsd-fme\tail.exe -n 40 -f %clog%
start .\dsd-fme\tail.exe -n 40 -f %elog%
@REM start dsd-fme with options and logs
.\dsd-fme\dsd-fme.exe %options% -J %elog% 2> %clog%
echo ----------------------------------------------------------------------------------
echo ----------------------------------------------------------------------------------
echo For any errors, see: %clog%
echo Forward %clog% and Options: "%options%"
echo to developer on Github or Radio Reference for troubleshooting.
echo ----------------------------------------------------------------------------------
echo ----------------------------------------------------------------------------------
@REM Set pause to diplay above message
PAUSE

View File

@@ -1,4 +1,4 @@
LSN(dec),frequency(Hz) (do not delete this line or won't import properly) ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
1,851375000 1,851375000
2,851800000 2,851800000
3,855987500 3,855987500
1 LSN(dec) ChannelNumber(dec) frequency(Hz) (do not delete this line or won't import properly)
2 1 1 851375000
3 2 2 851800000
4 3 3 855987500

View File

@@ -1,7 +0,0 @@
keyid(hex), keyvalue(hex)
0001,0000000000000000,0000000000000000,0000000000000000,0000002823d87923
0003,0101010101010101,0101010101010101
0004,0101010101010101,0101010101010101,0101010101010101,0101010101010101
C197,A753BC945DE5E0F1,EC1970F8565154E6,D9DF2FAC6278FA93,B531D2CC046E93A2
0067,0000000000000000,0000000000000000,0000000000000000,DEADBEEFBAADF00D
0000,0000000000000000,0000000000000000,0000000000000000,0000000000000001
1 keyid(hex), keyvalue(hex)
2 0001,0000000000000000,0000000000000000,0000000000000000,0000002823d87923
3 0003,0101010101010101,0101010101010101
4 0004,0101010101010101,0101010101010101,0101010101010101,0101010101010101
5 C197,A753BC945DE5E0F1,EC1970F8565154E6,D9DF2FAC6278FA93,B531D2CC046E93A2
6 0067,0000000000000000,0000000000000000,0000000000000000,DEADBEEFBAADF00D
7 0000,0000000000000000,0000000000000000,0000000000000000,0000000000000001

View File

@@ -1,4 +1,4 @@
DEC,Mode(A- Allow; B - Block; DE - Digital Enc),Name of Group,Tag (do not delete this line or won't import properly) DEC,Mode(A- Allow, B - Block, DE - Digital Enc),Name of Group,Tag (do not delete this line or won't import properly)
100,B,Example Name,Tag 100,B,Example Name,Tag
1449,A,Fire Dispatch,Fire 1449,A,Fire Dispatch,Fire
929,A,Fire Tac,Fire 929,A,Fire Tac,Fire
1 DEC DEC,Mode(A- Allow, B - Block, DE - Digital Enc),Name of Group,Tag (do not delete this line or won't import properly) Mode(A- Allow; B - Block; DE - Digital Enc) Name of Group Tag (do not delete this line or won't import properly)
2 100 100,B,Example Name,Tag B Example Name Tag
3 1449 1449,A,Fire Dispatch,Fire A Fire Dispatch Fire
4 929 929,A,Fire Tac,Fire A Fire Tac Fire

View File

@@ -1,4 +1,4 @@
LSN(dec),frequency(Hz) (do not delete this line or won't import properly) LSN Number(dec),frequency(Hz) (do not delete this line or won't import properly)
1,858606250 1,858606250
2,858606250 2,858606250
3,859606250 3,859606250
1 LSN(dec) LSN Number(dec) frequency(Hz) (do not delete this line or won't import properly)
2 1 858606250
3 2 858606250
4 3 859606250

View File

@@ -1,4 +1,4 @@
key id or tg id (dec), key number or value (dec) //NXDN Scrambler, BP, 10-char Hytera BP only key id or tg id (dec), key number or value (dec) //NXDN, BP, 10-char Hytera BP only
2,70 2,70
1,123 1,123
100,70 100,70
1 key id or tg id (dec), key number or value (dec) //NXDN Scrambler, BP, 10-char Hytera BP only key id or tg id (dec), key number or value (dec) //NXDN, BP, 10-char Hytera BP only
2 2,70 2,70
3 1,123 1,123
4 100,70 100,70

View File

@@ -1,5 +0,0 @@
key id(hex), key value (hex) //10-char Hytera BP, RC4
1,1234567890
2,ABCDEF1234
C,0B57935150
64,0B57935150
1 key id(hex), key value (hex) //10-char Hytera BP, RC4
2 1,1234567890
3 2,ABCDEF1234
4 C,0B57935150
5 64,0B57935150

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -1,112 +0,0 @@
#!/bin/bash
# Force bash for better compatibility (I think)
#
# Digital Speech Decoder: Florida Man Edition
#
# The source code of this software can be downloaded here:
# https://github.com/lwvmobile/dsd-fme
#
# If you paid for, or were otherwise fleeced for this software, DEMAND YOUR MONEY BACK!
#
# This software is designed to be used with an SDR receiver (like SDR++ or SDR#) with TCP input,
# RTL Input, media file playback over virtual cables or null-sinks, or a Discriminator Tap input
#
# The complete options list can be seen by viewing complete_usage_options.ans
# Many examples can be found in example_options.ans
#
# Enjoy!
###
#### IMPORTANT! Set your dsd-fme options here! ####
# Example : opts=' -ft -i rtl:0:460.350M:0:-1:8 -N -Z '
####
# set options to pass to dsd-fme
opts=' -N -Z '
####
# Initialize variables
xe="0"
gt="0"
xt="0"
ko="0"
# Functions to check for available terminals
chk_xe(){
# Check if x-terminal-emulator is installed
xe=$(which x-terminal-emulator 2>/dev/null)
if [ -z $xe ];then xe="0";else xe="1";fi
}
chk_gt(){
# Check if gnome-terminal is installed
gt=$(which gnome-terminal 2>/dev/null)
if [ -z $gt ];then gt="0";else gt="1";fi
}
chk_ko(){
# Check if Konsole is installed
ko=$(which konsole 2>/dev/null)
if [ -z $ko ];then ko="0";else ko="1";fi
}
chk_xt(){
# Check if xterm is installed
xt=$(which xterm 2>/dev/null)
if [ -z $xt ];then xt="0";else xt="1";fi
}
# Check for and set terminal
chk_xe
if [ $xe = "1" ];then
term=$(which x-terminal-emulator 2>/dev/null)
printf "Using X-Terminal-Emulator!\n"
else
chk_gt
fi
if [ $gt = "1" ];then
term=$(which gnome-terminal 2>/dev/null)
printf "Using Gnome-Terminal!\n"
elif [ $xe = "0" ]&&[ $gt = "0" ];then
chk_xt
fi
if [ $xt = "1" ];then
term=$(which xterm 2>/dev/null)
printf "Using XTerm!\n"
elif [ $xe = "0" ]&&[ $gt = "0" ]&&[ $xt = "0" ];then
chk_ko
fi
if [ $ko = "1" ];then
term=$(which konsole 2>/dev/null)
printf "Using Konsole!\n"
fi
# Bail if no terminal found
if [ "$ko" -eq "0" ]&&[ "$gt" -eq "0" ]&&[ "$xt" -eq "0" ]&&[ "$xe" -eq "0" ];then printf "No known terminals available!\n";exit 1;fi
# Get current directory
wd=$(pwd)
# Set date/time
# dt=$(date +%F_%H%M-%S-%N) #with hyphens
dt=$(date +%Y%m%d_%H%M%S) #just an underscore between date and time
if [ ! -d "logs" ]; then
mkdir logs
fi
# set log file relative filepath
clog="$wd/logs/console_log_$dt.txt"
elog="$wd/logs/event_log_$dt.txt"
# create the log file now with touch
touch $clog
touch $elog
# open new terminal windows to watch live log files
if [ "$term" == "$(which konsole)" ]||[ "$term" == "$(which xterm)" ];then
$term -e "tail -n 40 -f $clog" 2>/dev/null
$term -e "tail -n 40 -f $elog" 2>/dev/null
else
$term --window -e "tail -n 40 -f $clog" 2>/dev/null
$term --window -e "tail -n 40 -f $elog" 2>/dev/null
fi
# start dsd-fme with options and log files
dsd-fme $opts -J $elog 2> $clog
echo -------------------------------------------------------------------------------
echo -------------------------------------------------------------------------------
echo For any errors, see: $clog
echo Forward $clog and
echo Options: \" $opts \"
echo to developer on Github or Radio Reference for troubleshooting.
echo -------------------------------------------------------------------------------
echo -------------------------------------------------------------------------------
# Set pause to diplay above message
read -p "Press ENTER to continue... " x

View File

@@ -1,49 +1,33 @@
#example trunking sh scripts (SDR++ with RIGCTL and RTL Input) #example trunking sh scripts (SDR++ and RTL Input) -T = trunking
#make sure to put a # in front of all lines except the one you wish to use #make sure to put a # in front of all lines except the one you wish to use
#P25 P1 FDMA CC with C4FM #P25 P1 FDMA CC with C4FM
#dsd-fme -i tcp -U 4532 -T -N 2> log.ans #dsd-fme -i tcp -U 4532 -T -N 2> log.ans
#dsd-fme -i rtl:0:850M:0:-2:12 -T -N 2> log.ans #dsd-fme -i rtl:0:850M:26:-2:8:0:6020 -T -N 2> log.ans
#P25 P1 FDMA CC with QPSK (Simulcast) #P25 P1 FDMA CC with QPSK (Simulcast)
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -mq -N 2> log.ans #dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -mq -N 2> log.ans
#dsd-fme -i rtl:0:855.625M:0:-2:12 -T -mq -N 2> log.ans #dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -T -mq -N 2> log.ans
#P25 P2 TDMA CC with C4FM #P25 P2 TDMA CC with QPSK (Simulcast)
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -N 2> log.ans #dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -m2 -N 2> log.ans
#dsd-fme -i rtl:0:855.625M:0:-2:12 -T -f2 -m2 -N 2> log.ans #dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -T -f2 -m2 -N 2> log.ans
#DMR TIII, Con+, Cap+, XPT #DMR TIII, Con+, Cap+, XPT
dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
#dsd-fme -fs -i rtl:0:450M:44:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans #dsd-fme -fs -i rtl:0:450M:26:-2:8:0:6020 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans
#EDACS Standard Network Analog and Digital #EDACS/EDACS-EA Digital Only
#dsd-fme -fh -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans #dsd-fme -fp -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
#dsd-fme -fh -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans #dsd-fme -fp -i rtl:0:850M:26:-2:24:0:6020 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
#EDACS Standard Network Analog and Digital (with ESK) #NXDN48 Trunking with Channel Map (Custom Channels > 800)
#dsd-fme -fH -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans #dsd-fme -fi -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans
#dsd-fme -fH -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans #NOTE! NXDN Trunking will not work correctly with the RTL input method
#EDACS Extended Address Analog and Digital #NXDN96 Trunking with Channel Map (Custom Channels > 800)
#dsd-fme -fe -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans #dsd-fme -fn -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans
#dsd-fme -fe -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans #NOTE! NXDN Trunking will not work correctly with the RTL input method
#EDACS Extended Address Analog and Digital (with ESK)
#dsd-fme -fE -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
#dsd-fme -fE -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
#NXDN48 Type-C or Type-D Trunking with Channel Map
#dsd-fme -fi -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans
#dsd-fme -fi -i rtl:0:855.625M:0:-2:12:100 -T -C nxdn_channel_map.csv -N 2> log.ans
#NXDN48 Type-C Trunking with DFA
#dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans
#dsd-fme -fi -i rtl:0:855.625M:0:-2:12:100 -T -N 2> log.ans
#NXDN96 Trunking with Channel Map
#dsd-fme -fn -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans
#dsd-fme -fn -i rtl -T -C nxdn_channel_map.csv -N 2> log.ans
#Notes: parameters after -i tcp can be omitted if using localhost as the default hostname to connect #Notes: parameters after -i tcp can be omitted if using localhost as the default hostname to connect
#and 7355 as the default port number. -U is the port number for RIGCTL and will mirror the tcp hostname #and 7355 as the default port number. -U is the port number for RIGCTL and will mirror the tcp hostname
@@ -52,6 +36,5 @@ dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
#inside of SDR++ match, i.e., change in SDR++ from 'localhost' to an IP to bind to (machine's IP address) #inside of SDR++ match, i.e., change in SDR++ from 'localhost' to an IP to bind to (machine's IP address)
#Note2: RTL input should only be used on strong signal, the SDR++ VFO will perform much better on marginal signal #Note2: RTL input should only be used on strong signal, the SDR++ VFO will perform much better on marginal signal
#paramters after -i rtl can also be omitted now if using the default configurations
#Having a -G group.csv is purely optional, but useful if you want to block groups, or give groups names #Having a -G group.csv is purely optional, but useful if you want to block groups, or give groups names

64
flake.lock generated
View File

@@ -1,64 +0,0 @@
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1709336216,
"narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1711163522,
"narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"dir": "lib",
"lastModified": 1709237383,
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
"type": "github"
},
"original": {
"dir": "lib",
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,18 +0,0 @@
{
description = "Digital Speech Decoder - Florida Man Edition ";
inputs = {
flake-parts.url = "github:hercules-ci/flake-parts";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
perSystem = { config, pkgs, ... }: {
packages.default = config.packages.dsd-fme;
packages.dsd-fme = pkgs.callPackage ./package.nix { };
};
};
}

View File

@@ -7,6 +7,8 @@
* *
* Code adapted from http://www.aqdi.com/golay.htm * Code adapted from http://www.aqdi.com/golay.htm
* *
* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs
* expected from the P25 transmissions that I have tested. This implementation does work.
*/ */
#include <stdio.h> #include <stdio.h>

View File

@@ -5,6 +5,9 @@
/** /**
* Code taken and adapted from www.eccpage.com/rs.c * Code taken and adapted from www.eccpage.com/rs.c
* Credit goes to Mr Simon Rockliff. * Credit goes to Mr Simon Rockliff.
*
* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs
* expected from the P25 transmissions that I have tested. This implementation does work.
*/ */
/* This program is an encoder/decoder for Reed-Solomon codes. Encoding is in /* This program is an encoder/decoder for Reed-Solomon codes. Encoding is in

View File

@@ -1,71 +0,0 @@
#ifndef BP_H
#define BP_H
static const uint16_t BPK[256] = {
//000
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
//008
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
//016
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
//024
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E,
//032
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
//040
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
//048
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
//056
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36,
//064
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
//072
0x614C, 0x7E4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
//080
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
//088
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57,
//096
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
//104
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
//112
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
//120
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F,
//128
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
//136
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
//144
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
//152
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897,
//160
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
//168
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
//176
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
//184
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF,
//192
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
//200
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
//208
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
//216
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3,
//224
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
//232
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
//240
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
//248
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6
};
#endif

View File

@@ -17,7 +17,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
// This code was originally written by JOnathan Naylor, G4KLX, as part // This code was originally written by JOnathan Naylor, G4KLX, as part
// of the "pcrepeatercontroller" project // of the "pcrepeatercontroller" project
// More info: // More info:
@@ -52,39 +52,39 @@ int traceBack (int * out, int * m_pathMemory0, int * m_pathMemory1, int * m_path
state = S2; // lower path state = S2; // lower path
} else { } else {
state = S0; // upper path state = S0; // upper path
} // end else - if }; // end else - if
out[loop]=0; out[loop]=0;
break; break;
case S1: // if state S1 case S1: // if state S1
if (m_pathMemory1[loop]) { if (m_pathMemory1[loop]) {
state = S2; // lower path state = S2; // lower path
} else { } else {
state = S0; // upper path state = S0; // upper path
} // end else - if }; // end else - if
out[loop]=1; out[loop]=1;
break; break;
case S2: // if state S2 case S2: // if state S2
if (m_pathMemory2[loop]) { if (m_pathMemory2[loop]) {
state = S3; // lower path state = S3; // lower path
} else { } else {
state = S1; // upper path state = S1; // upper path
} // end else - if }; // end else - if
out[loop]=0; out[loop]=0;
break; break;
case S3: // if state S3 case S3: // if state S3
if (m_pathMemory3[loop]) { if (m_pathMemory3[loop]) {
state = S3; // lower path state = S3; // lower path
} else { } else {
state = S1; // upper path state = S1; // upper path
} // end else - if }; // end else - if
out[loop]=1; out[loop]=1;
break; break;
} // end switch }; // end switch
} // end for }; // end for
return(length); return(length);
}; // end function }; // end function
@@ -161,7 +161,7 @@ void viterbiDecode (int n, int *data, int *m_pathMemory0, int *m_pathMemory1, in
}; // end function ViterbiDecode }; // end function ViterbiDecode
// function FECdecoder // function FECdecoder
// returns outlen // returns outlen
int FECdecoder (int * in, int * out) { int FECdecoder (int * in, int * out) {
int outLen; int outLen;

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@
*/ */
/* /*
* dstar interleave experiments * dstar interleave experiments
*/ */
#ifndef _MAIN #ifndef _MAIN

View File

@@ -1,6 +1,6 @@
/* fcs.h */ /* fcs.h */
// Viterbi decoder using Traceback method. // Viterbi decoder using Traceback method.
// Original Source was written by Sho Tamaoki and Tom Wada // Original Source was written by Sho Tamaoki and Tom Wada
// See http://www.lsi.ie.u-ryukyu.ac.jp/~sho/midterm/ // See http://www.lsi.ie.u-ryukyu.ac.jp/~sho/midterm/
@@ -75,7 +75,7 @@ for (loop=0; loop < size; loop++) {
m_crc = (m_crc >> 8) ^ ccittTab[tmp]; m_crc = (m_crc >> 8) ^ ccittTab[tmp];
} // end for }; // end for
// calculate and save crc-value in fields 54 and 55 of dvframe // calculate and save crc-value in fields 54 and 55 of dvframe
m_crc = ~m_crc; m_crc = ~m_crc;
@@ -86,4 +86,4 @@ m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF);
// done // done
return(m_crc); return(m_crc);
}; // end function }; // end function

1448
include/fec.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,12 +5,12 @@
// under the terms of the GNU General Public License as published by // under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3, or (at your option) // the Free Software Foundation; either version 3, or (at your option)
// any later version. // any later version.
// //
// OP25 is distributed in the hope that it will be useful, but WITHOUT // OP25 is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details. // License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with OP25; see the file COPYING. If not, write to the Free // along with OP25; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 51 Franklin Street, Boston, MA // Software Foundation, Inc., 51 Franklin Street, Boston, MA

26
include/p25p1_check_nid.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
#define P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
#ifdef __cplusplus
extern "C" {
#endif
/**
* Checks a NID value, returns the fixed NAC, DUID and also an indication if it failed to decode the NID.
* \param bch_code Input. An array to the 63 bytes, each containing one bit of the NID. This includes the
* NAC (12 bits), DUID (4 bits) and 47 bits of BCH parity.
* \param new_nac Output. An address where to store the calculated NAC value after error correction. Should
* be large enough to accommodate for an integer.
* \param new_duid Output. An address where to store the calculated DUID value after error correction. Should
* be able to accommodate 3 chars.
* \param parity Input. The parity value read.
* \return 0 if there were errors processing the NID.
*/
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity);
#ifdef __cplusplus
}
#endif
#endif // P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a

View File

@@ -1,95 +0,0 @@
#include <stdint.h>
#include <stddef.h>
#ifndef TYT_AP
#define TYT_AP
#define nbround 254
#define n1 264
#define UNUSEDPC4(x) ((void)x)
/* Structure holding all PC4 state variables */
typedef struct {
short bits[49], temp[49];
uint8_t ptconvert;
uint8_t convert[7];
uint8_t perm[16][256];
uint8_t new1[256];
uint8_t array[49];
uint8_t array2[49];
uint8_t decal[nbround];
uint8_t rngxor[nbround][3];
uint8_t rngxor2[nbround][3];
uint8_t rounds;
uint8_t tab[256];
uint8_t inv[256];
uint8_t permut[3][3];
uint64_t bb;
uint64_t x;
uint8_t tot[3];
uint8_t l[2][3], r[2][3];
uint8_t y, totb;
uint32_t result;
uint8_t xyz, count;
uint8_t keys[16];
unsigned char array_arc4[256];
int i_arc4, j_arc4;
int x1, x2, i;
unsigned char h2[n1];
unsigned char h1[n1*3];
} PC4Context;
/* Global context accessible from all .c files */
extern PC4Context ctx;
/* Public API */
void create_keys(PC4Context *ctx, unsigned char key1[], size_t size1);
void pc4encrypt(PC4Context *ctx);
void pc4decrypt(PC4Context *ctx);
void binhex(PC4Context *ctx, short *z, int length);
void hexbin(PC4Context *ctx, short *q, uint8_t w, uint8_t hex);
static void u64_to_bytes_be(uint64_t val, unsigned char *out);
/* Encrypt a 49-bit frame (original flow) */
static void encrypt_frame_49(short frame_bits_in[49]) {
for (int i = 0; i < 49; i++) ctx.bits[i] = frame_bits_in[i];
for (int i = 0; i < 49; i++) ctx.temp[i] = ctx.bits[ctx.array[i]];
for (int i = 0; i < 49; i++) ctx.bits[i] = ctx.temp[i];
ctx.ptconvert = 0;
binhex(&ctx, ctx.bits, 48);
pc4encrypt(&ctx);
for (int q = 0; q < 6; q++) {
uint8_t w = (uint8_t)(q * 8);
hexbin(&ctx, ctx.bits, w, ctx.convert[q]);
}
ctx.bits[48] = (short)(ctx.bits[48] ^ ctx.totb);
for (int i = 0; i < 49; i++) ctx.temp[ctx.array2[i]] = ctx.bits[i];
for (int i = 0; i < 49; i++) ctx.bits[i] = ctx.temp[i];
}
/* Decrypt a 49-bit frame (original flow) */
static void decrypt_frame_49(short frame_bits_in[49]) {
for (int i = 0; i < 49; i++) ctx.bits[i] = frame_bits_in[i];
for (int i = 0; i < 49; i++) ctx.temp[i] = ctx.bits[ctx.array2[i]];
for (int i = 0; i < 49; i++) ctx.bits[i] = ctx.temp[i];
ctx.ptconvert = 0;
binhex(&ctx, ctx.bits, 48);
pc4decrypt(&ctx);
for (int q = 0; q < 6; q++) {
uint8_t w = (uint8_t)(q * 8);
hexbin(&ctx, ctx.bits, w, ctx.convert[q]);
}
ctx.bits[48] = (short)(ctx.bits[48] ^ ctx.totb);
for (int i = 0; i < 49; i++) ctx.temp[ctx.array[i]] = ctx.bits[i];
for (int i = 0; i < 49; i++) ctx.bits[i] = ctx.temp[i];
}
/* Convert 64-bit integer to bytes (big-endian) */
static void u64_to_bytes_be(uint64_t val, unsigned char *out) {
for (int i = 0; i < 8; i++) {
out[i] = (unsigned char)((val >> (56 - 8 * i)) & 0xFF);
}
}
#endif

View File

@@ -1,147 +0,0 @@
#include <stdint.h>
#include <stddef.h>
#ifndef BAOFENG_AP
#define BAOFENG_AP
#define nbround 254
#define n1 264
#define UNUSEDPC5(x) ((void)x)
/* Structure holding all PC5 state variables */
typedef struct {
short bits[49], temp[49];
uint8_t ptconvert;
uint8_t convert[7];
uint8_t perm[16][256];
uint8_t new1[256];
uint8_t array[49];
uint8_t array2[49];
uint8_t decal[nbround];
uint8_t rngxor[nbround][3];
uint8_t rngxor2[nbround][3];
uint8_t rounds;
uint8_t tab[256];
uint8_t inv[256];
uint8_t permut[3][3];
uint64_t bb;
uint64_t x;
uint8_t tot[3];
uint8_t l[2][3], r[2][3];
uint8_t y, totb;
uint32_t result;
uint8_t xyz, count;
uint8_t keys[16];
unsigned char array_arc4[256];
int i_arc4, j_arc4;
int x1, x2, i;
unsigned char h2[n1];
unsigned char h1[n1*3];
uint8_t numbers[25];
} PC5Context;
/* Global context accessible from all .c files */
extern PC5Context ctxpc5;
/* Public API */
void create_keys_pc5(PC5Context *ctxpc5, unsigned char key1[], size_t size1);
void pc5encrypt(PC5Context *ctxpc5);
void pc5decrypt(PC5Context *ctxpc5);
void binhexpc5(PC5Context *ctxpc5, short *z, int length);
void hexbinpc5(PC5Context *ctxpc5, short *q, uint8_t w, uint8_t hex);
static void u64_to_bytes_be_pc5(uint64_t val, unsigned char *out);
/* Encrypt a 49-bit frame using PC5 algorithm (original flow) */
static void encrypt_frame_49_pc5(short frame_bits_in[49]) {
// Only process first 24 bits for encryption, rest are XORed
ctxpc5.ptconvert = 0;
binhexpc5(&ctxpc5, frame_bits_in, 24);
uint8_t convert[6];
// Store first 3 bytes in convert
for (int i = 0; i < 3; i++) convert[i] = ctxpc5.convert[i];
// Split each byte into two 4-bit nibbles
ctxpc5.convert[0] = convert[0] >> 4;
ctxpc5.convert[1] = convert[0] & 0xF;
ctxpc5.convert[2] = convert[1] >> 4;
ctxpc5.convert[3] = convert[1] & 0xF;
ctxpc5.convert[4] = convert[2] >> 4;
ctxpc5.convert[5] = convert[2] & 0xF;
pc5encrypt(&ctxpc5);
// Store encrypted nibbles back in convert
for (int i = 0; i < 6; i++) convert[i] = ctxpc5.convert[i];
// Recombine nibbles into bytes
ctxpc5.convert[0] = (convert[0] << 4) + convert[1];
ctxpc5.convert[1] = (convert[2] << 4) + convert[3];
ctxpc5.convert[2] = (convert[4] << 4) + convert[5];
// Convert back to bits for first 24 bits
for (int q = 0; q < 3; q++) {
uint8_t w = (uint8_t)(q * 8);
hexbinpc5(&ctxpc5, frame_bits_in, w, ctxpc5.convert[q]);
}
// XOR the remaining bits (positions 24 to 48)
for (int i = 24; i < 49; i++) {
frame_bits_in[i] = (short)(frame_bits_in[i] ^ ctxpc5.numbers[i - 24]);
}
for (int i = 0; i < 49; i++) ctxpc5.bits[i] = frame_bits_in[i];
}
/* Decrypt a 49-bit frame using PC5 algorithm (original flow) */
static void decrypt_frame_49_pc5(short frame_bits_in[49]) {
// XOR the remaining bits (positions 24 to 48) first
for (int i = 24; i < 49; i++) {
frame_bits_in[i] = (short)(frame_bits_in[i] ^ ctxpc5.numbers[i - 24]);
}
ctxpc5.ptconvert = 0;
binhexpc5(&ctxpc5, frame_bits_in, 24);
uint8_t convert[6];
// Store first 3 bytes in convert
for (int i = 0; i < 3; i++) convert[i] = ctxpc5.convert[i];
// Split each byte into two 4-bit nibbles
ctxpc5.convert[0] = convert[0] >> 4;
ctxpc5.convert[1] = convert[0] & 0xF;
ctxpc5.convert[2] = convert[1] >> 4;
ctxpc5.convert[3] = convert[1] & 0xF;
ctxpc5.convert[4] = convert[2] >> 4;
ctxpc5.convert[5] = convert[2] & 0xF;
pc5decrypt(&ctxpc5);
// Store decrypted nibbles back in convert
for (int i = 0; i < 6; i++) convert[i] = ctxpc5.convert[i];
// Recombine nibbles into bytes
ctxpc5.convert[0] = (convert[0] << 4) + convert[1];
ctxpc5.convert[1] = (convert[2] << 4) + convert[3];
ctxpc5.convert[2] = (convert[4] << 4) + convert[5];
// Convert back to bits for first 24 bits
for (int q = 0; q < 3; q++) {
uint8_t w = (uint8_t)(q * 8);
hexbinpc5(&ctxpc5, frame_bits_in, w, ctxpc5.convert[q]);
}
for (int i = 0; i < 49; i++) ctxpc5.bits[i] = frame_bits_in[i];
}
/* Convert 64-bit integer to bytes (big-endian) */
static void u64_to_bytes_be_pc5(uint64_t val, unsigned char *out) {
for (int i = 0; i < 8; i++) {
out[i] = (unsigned char)((val >> (56 - 8 * i)) & 0xFF);
}
}
#endif

View File

@@ -1,43 +0,0 @@
#ifndef RETEVIS_AP
#define RETEVIS_AP
#include <stdint.h>
#include "dsd.h"
#define n1 264
typedef struct {
uint8_t array_rc4[256];
int i_rc4, j_rc4;
uint64_t x;
uint8_t xyz, count;
uint64_t bb;
} RC4State;
typedef struct {
int x1, x2;
unsigned char h2[n1];
unsigned char h1[n1 * 3];
} MD2State;
typedef struct {
uint16_t xkey[64];
uint8_t plain[8];
uint8_t cipher[8];
} RC2State;
typedef struct {
RC4State rc4;
MD2State md2;
RC2State rc2;
uint64_t internal_state;
uint64_t internal_zero;
uint8_t keys[16];
} CryptoContext;
/* Public API */
void create_keys_rc2(CryptoContext* ctx, unsigned char key1[], size_t size1);
void encryption_rc2(CryptoContext* ctx, uint8_t s1[49]);
void decrypt_rc2(CryptoContext* ctx, uint8_t bits[49]);
#endif

View File

@@ -1,48 +0,0 @@
#ifndef SYNCTYPE_H
#define SYNCTYPE_H
static const char * SyncTypes[44] = {
"P25p1",
"P25p1",
"X2TDMA DATA",
"X2TDMA DATA",
"X2TDMA VOICE",
"X2TDMA VOICE",
"DSTAR", //voice
"DSTAR", //voice
"M17",
"M17",
"DMR", //10
"DMR",
"DMR",
"DMR",
"EDACS/PV",
"EDACS/PV",
"M17", //M17 Voice Stream
"M17", //M17 Voice Stream
"DSTAR", //header
"DSTAR", //header
"dPMR", //20
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"NXDN",
"NXDN",
"YSF", //30
"YSF", //31
"DMR",
"DMR",
"DMR",
"P25p2",
"P25p2",
"EDACS/PV", //37
"EDACS/PV", //38
"ANALOG", //39 Generic Sync Types
"DIGITAL" //40 Generic Sync Types
};
#endif

View File

@@ -1,8 +1,12 @@
#!/bin/bash sudo cp tone8.wav /usr/share/
# sudo cp tone24.wav /usr/share/
sudo cp tone48.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
sudo chmod 777 /usr/share/tone48.wav
mkdir build mkdir build
cd build cd build
cmake .. cmake ..
make -j $(nproc) make -j `nproc`
sudo make install sudo make install
sudo ldconfig sudo ldconfig

View File

@@ -1,44 +0,0 @@
{ lib
, stdenv
, cmake
, mbelib
, libsndfile
, ncurses
, pulseaudio
, rtl-sdr
, codec2
, portaudioSupport ? true
, portaudio ? null
}:
assert portaudioSupport -> portaudio != null;
stdenv.mkDerivation {
name = "dsd-fme";
src = ./.;
nativeBuildInputs = [ cmake ];
buildInputs = [
mbelib
libsndfile
rtl-sdr
ncurses.dev
pulseaudio.dev
codec2
] ++ lib.optionals portaudioSupport [ portaudio ];
doCheck = true;
meta = with lib; {
description = "Digital Speech Decoder - Florida Man Edition";
longDescription = ''
DSD is able to decode several digital voice formats from discriminator
tap audio and synthesize the decoded speech. Speech synthesis requires
mbelib, which is a separate package.
'';
homepage = "https://github.com/lwvmobile/dsd-fme";
license = licenses.mit;
platforms = platforms.unix;
};
}

View File

@@ -1,330 +0,0 @@
diff --git a/include/dsd.h b/include/dsd.h
index 4a75702..3cf085e 100644
--- a/include/dsd.h
+++ b/include/dsd.h
@@ -317,6 +317,7 @@ typedef struct
long int rtl_rms;
int monitor_input_audio;
int analog_only;
+ int ghosts_only;
int pulse_raw_rate_in;
int pulse_raw_rate_out;
int pulse_digi_rate_in;
@@ -1342,6 +1343,8 @@ 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);
+bool SetModulationWFM(int sockfd, int bandwidth);
+void death (dsd_opts * opts, dsd_state * state);
//commands below unique to GQRX only, not usable on SDR++
bool GetSignalLevel(int sockfd, double *dBFS);
bool GetSquelchLevel(int sockfd, double *dBFS);
diff --git a/src/3.c b/src/3.c
index e104993..b9033bd 100644
--- a/src/3.c
+++ b/src/3.c
@@ -591,4 +591,176 @@ long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as
rms = (long int)sqrt((p-err) / len);
if (rms < 0) rms = 150;
return rms;
-}
\ No newline at end of file
+}
+
+
+//Ghosts
+void death (dsd_opts * opts, dsd_state * state)
+{
+ int i, x, result = 0;
+ short ghosts[960];
+ short spooks[960];
+ short demons[960];
+ short sample = 0;
+
+ memset (ghosts, 0, 960*sizeof(short));
+ memset (spooks, 0, 960*sizeof(short));
+ memset (demons, 0, 960*sizeof(short));
+
+ while (!exitflag) //while the software is running
+ {
+ //death on speaks at 48k/1 short output
+ if (opts->audio_in_type == 0)
+ {
+ for (i = 0; i < 960; i++)
+ {
+ pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
+ ghosts[i] = sample;
+ }
+
+ for (i = 0; i < 960; i++)
+ {
+ pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
+ spooks[i] = sample;
+ }
+
+ for (i = 0; i < 960; i++)
+ {
+ pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
+ demons[i] = sample;
+ }
+ }
+
+ if (opts->audio_in_type == 8)
+ {
+ for (i = 0; i < 960; i++)
+ {
+ result = sf_read_short(opts->tcp_file_in, &sample, 1);
+ if (result == 0)
+ {
+ sf_close(opts->tcp_file_in);
+ fprintf (stderr, "Connection to the Underworld Severed.\n");
+ fprintf (stderr, "Closing Death Speech Decoder.\n");
+ cleanupAndExit(opts, state);
+ }
+ ghosts[i] = sample;
+ }
+
+ for (i = 0; i < 960; i++)
+ {
+ result = sf_read_short(opts->tcp_file_in, &sample, 1);
+ if (result == 0)
+ {
+ sf_close(opts->tcp_file_in);
+ fprintf (stderr, "Connection to the Underworld Severed.\n");
+ fprintf (stderr, "Closing Death Speech Decoder.\n");
+ cleanupAndExit(opts, state);
+ }
+ spooks[i] = sample;
+ }
+
+ for (i = 0; i < 960; i++)
+ {
+ result = sf_read_short(opts->tcp_file_in, &sample, 1);
+ if (result == 0)
+ {
+ sf_close(opts->tcp_file_in);
+ fprintf (stderr, "Connection to the Underworld Severed.\n");
+ fprintf (stderr, "Closing Death Speech Decoder.\n");
+ cleanupAndExit(opts, state);
+ }
+ demons[i] = sample;
+ }
+ }
+
+ //RTL Input
+ #ifdef USE_RTLSDR
+ if (opts->audio_in_type == 3)
+ {
+ for (i = 0; i < 960; i++)
+ {
+ get_rtlsdr_sample(&sample, opts, state);
+ ghosts[i] = sample;
+ }
+
+ for (i = 0; i < 960; i++)
+ {
+ get_rtlsdr_sample(&sample, opts, state);
+ spooks[i] = sample;
+ }
+
+ for (i = 0; i < 960; i++)
+ {
+ get_rtlsdr_sample(&sample, opts, state);
+ demons[i] = sample;
+ }
+ }
+ #endif
+
+ //The Dead Don't Like OSS input yet
+
+ //Listen to the Howls of the Deceased
+ if (opts->audio_out_type == 0 && opts->slot1_on == 1)
+ {
+ pa_simple_write(opts->pulse_raw_dev_out, ghosts, 960*2, NULL);
+ pa_simple_write(opts->pulse_raw_dev_out, spooks, 960*2, NULL);
+ pa_simple_write(opts->pulse_raw_dev_out, demons, 960*2, NULL);
+ }
+
+ if (opts->audio_out_type == 8) //UDP Audio
+ {
+ udp_socket_blasterA (opts, state, 960*2, ghosts);
+ udp_socket_blasterA (opts, state, 960*2, spooks);
+ udp_socket_blasterA (opts, state, 960*2, demons);
+ }
+
+ //added a condition check so that if OSS output and 8K, switches to 48K when opening OSS
+ if (opts->audio_out_type == 5 && opts->floating_point == 0 && opts->slot1_on == 1)
+ {
+ write (opts->audio_out_fd, ghosts, 960*2);
+ write (opts->audio_out_fd, spooks, 960*2);
+ write (opts->audio_out_fd, demons, 960*2);
+ }
+
+ //STDOUT -- I don't see the harm of adding this here, will be fine for analog only or digital only (non-mixed analog and digital)
+ if (opts->audio_out_type == 1 && opts->floating_point == 0 && opts->slot1_on == 1)
+ {
+ write (opts->audio_out_fd, ghosts, 960*2);
+ write (opts->audio_out_fd, spooks, 960*2);
+ write (opts->audio_out_fd, demons, 960*2);
+ }
+
+ //Undulate the Crystal Ball
+ if (x >= opts->trunk_hangtime)
+ {
+ long int spook_channel = state->trunk_lcn_freq[0];
+
+ //rigctl
+ if (opts->use_rigctl == 1)
+ {
+ if (opts->setmod_bw != 0 ) SetModulationWFM(opts->rigctl_sockfd, opts->setmod_bw);
+ SetFreq(opts->rigctl_sockfd, spook_channel);
+ }
+
+ //rtl
+ if (opts->audio_in_type == 3)
+ {
+ #ifdef USE_RTLSDR
+ rtl_dev_tune(opts, spook_channel);
+ #endif
+ }
+
+ //
+ state->trunk_lcn_freq[0] += 200000;
+
+ if (state->trunk_lcn_freq[0] > 106100000)
+ state->trunk_lcn_freq[0] = 87900000;
+
+ x = 0;
+
+ }
+
+ x++;
+
+ } //end while loop
+} //end ghosts
\ No newline at end of file
diff --git a/src/dsd_main.c b/src/dsd_main.c
index ca878e9..f14995d 100644
--- a/src/dsd_main.c
+++ b/src/dsd_main.c
@@ -696,6 +696,7 @@ initOpts (dsd_opts * opts)
opts->monitor_input_audio = 0; //enable with -8
opts->analog_only = 0; //only turned on with -fA
+ opts->ghosts_only = 0; //ghostbox mode with -fG
opts->inverted_p2 = 0;
opts->p2counter = 0;
@@ -1358,6 +1359,7 @@ usage ()
printf ("Decoder options:\n");
printf (" -fa Auto Detection\n");
printf (" -fA Passive Analog Audio Monitor\n");
+ printf (" -fG Ghosts, Demons, Specters, Ghouls, and Skeletons (Ghost Box)\n");
printf (" -ft TDMA Trunking P25p1 Control and Voice, P25p2 Trunked Channels, and DMR\n");
printf (" -fs DMR TDMA BS and MS Simplex\n");
printf (" -f1 Decode only P25 Phase 1\n");
@@ -2172,6 +2174,35 @@ main (int argc, char **argv)
sprintf (opts.output_name, "Analog Monitor");
fprintf (stderr,"Only Monitoring Passive Analog Signal\n");
}
+ else if (optarg[0] == 'G') //listen to spooky scary skeletons
+ {
+ opts.frame_dstar = 0;
+ opts.frame_x2tdma = 0;
+ opts.frame_p25p1 = 0;
+ opts.frame_p25p2 = 0;
+ opts.frame_nxdn48 = 0;
+ opts.frame_nxdn96 = 0;
+ opts.frame_dmr = 0;
+ opts.frame_dpmr = 0;
+ opts.frame_provoice = 0;
+ opts.frame_ysf = 0;
+ opts.frame_m17 = 0;
+ opts.pulse_digi_rate_out = 8000;
+ opts.pulse_digi_out_channels = 1;
+ opts.dmr_stereo = 0;
+ state.dmr_stereo = 0;
+ opts.dmr_mono = 0;
+ state.rf_mod = 0;
+ opts.monitor_input_audio = 1;
+ opts.ghosts_only = 1;
+ state.trunk_lcn_freq[0] = 87900000; //everybody knows ghosts live on the Broadcast FM band
+ opts.setmod_bw = 150000; //Ghosts annunciate clearly with about 150,000 PB
+ opts.trunk_hangtime = 0;
+ opts.rtlsdr_center_freq = 850000000;
+ opts.rtl_bandwidth = 48;
+ sprintf (opts.output_name, "Ghost Box");
+ fprintf (stderr,"Sounds of the Dead.\n");
+ }
else if (optarg[0] == 'd')
{
opts.frame_dstar = 1;
@@ -3462,6 +3493,22 @@ main (int argc, char **argv)
processM17IPF(&opts, &state);
}
+ else if (opts.ghosts_only == 1)
+ {
+ #ifdef USE_RTLSDR
+ if(opts.audio_in_type == 3)
+ open_rtlsdr_stream(&opts);
+ #endif
+
+ if (opts.audio_in_type == 0)
+ openPulseInput(&opts);
+
+ if (opts.audio_out_type == 0)
+ openPulseOutput(&opts);
+
+ death(&opts, &state);
+ }
+
else
{
diff --git a/src/dsd_rigctl.c b/src/dsd_rigctl.c
index 820ac7e..f12bacc 100644
--- a/src/dsd_rigctl.c
+++ b/src/dsd_rigctl.c
@@ -163,6 +163,28 @@ bool SetModulation(int sockfd, int bandwidth)
return true;
}
+bool SetModulationWFM(int sockfd, int bandwidth)
+{
+ char buf[BUFSIZE];
+ //the bandwidth is now a user/system based configurable variable
+ sprintf (buf, "M WFM %d\n", bandwidth); //SDR++ has changed the token from FM to NFM, even if Ryzerth fixes it later, users may still have an older version
+ Send(sockfd, buf);
+ Recv(sockfd, buf);
+
+ //if it fails the first time, send the other token instead
+ if (strcmp(buf, "RPRT 1\n") == 0 ) //sdr++ has a linebreak here, is that in all versions of the protocol?
+ {
+ sprintf (buf, "M WFM %d\n", bandwidth); //anything not SDR++
+ Send(sockfd, buf);
+ Recv(sockfd, buf);
+ }
+
+ if (strcmp(buf, "RPRT 1\n") == 0 )
+ return false;
+
+ return true;
+}
+
bool GetSignalLevel(int sockfd, double *dBFS)
{
char buf[BUFSIZE];
diff --git a/src/rtl_sdr_fm.cpp b/src/rtl_sdr_fm.cpp
index ad04667..dd6566c 100644
--- a/src/rtl_sdr_fm.cpp
+++ b/src/rtl_sdr_fm.cpp
@@ -1034,7 +1034,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
//init with low pass if decoding P25 or EDACS/Provoice
if (opts->frame_p25p1 == 1 || opts->frame_p25p2 == 1 || opts->frame_provoice == 1)
demod_init_ro2(&demod);
- else if (opts->analog_only == 1 || opts->m17encoder == 1)
+ else if (opts->analog_only == 1 || opts->m17encoder == 1 || opts->ghosts_only == 1)
demod_init_analog(&demod);
else demod_init(&demod);
output_init(&output);

View File

@@ -1,257 +0,0 @@
diff --git a/include/dsd.h b/include/dsd.h
index 4a75702..355837f 100644
--- a/include/dsd.h
+++ b/include/dsd.h
@@ -357,6 +357,8 @@ typedef struct
int frame_m17;
int inverted_m17;
+ int frame_typeii;
+
FILE *symbolfile;
int call_alert;
@@ -989,6 +991,10 @@ typedef struct
#define INV_DPMR_FRAME_SYNC_3 "311313111133"
#define INV_DPMR_FRAME_SYNC_4 "111333331133131131111313"
+//Motorola Type II Sync Pattern 0xAC binary 1010 1100
+#define TYPE_II_SYNC "13131133" //baesd on what getDibit returns
+#define TYPE_II_SINV "31313311" //baesd on what getDibit returns
+
/*
* function prototypes
*/
@@ -1357,6 +1363,9 @@ void edacs(dsd_opts * opts, dsd_state * state);
unsigned long long int edacs_bch (unsigned long long int message);
void eot_cc(dsd_opts * opts, dsd_state * state); //end of TX return to CC
+//Type II
+void type_ii(dsd_opts * opts, dsd_state * state);
+
//Generic Tuning Functions
void return_to_cc (dsd_opts * opts, dsd_state * state);
diff --git a/src/dsd_dibit.c b/src/dsd_dibit.c
index 5424f7a..9b403cf 100644
--- a/src/dsd_dibit.c
+++ b/src/dsd_dibit.c
@@ -222,13 +222,14 @@ invert_dibit(int dibit)
int digitize (dsd_opts* opts, dsd_state* state, int symbol)
{
// determine dibit state
- if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37))
+ if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37) || (state->synctype == 40))
{
// 6 +D-STAR
// 14 +ProVoice
// 18 +D-STAR_HD
// 37 +EDACS
+ // 41 +TypeII
if (symbol > state->center)
{
@@ -243,12 +244,13 @@ int digitize (dsd_opts* opts, dsd_state* state, int symbol)
return (1); // +3
}
}
- else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19) || (state->synctype == 38))
+ else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19) || (state->synctype == 38) || (state->synctype == 41))
{
// 7 -D-STAR
// 15 -ProVoice
// 19 -D-STAR_HD
// 38 -EDACS
+ // 41 -TypeII
if (symbol > state->center)
{
diff --git a/src/dsd_frame.c b/src/dsd_frame.c
index 577169c..58fa07c 100644
--- a/src/dsd_frame.c
+++ b/src/dsd_frame.c
@@ -269,6 +269,12 @@ processFrame (dsd_opts * opts, dsd_state * state)
processYSF(opts, state);
return;
}
+ //TypeII
+ else if (state->synctype == 40 || state->synctype == 41)
+ {
+ type_ii(opts, state);
+ return;
+ }
//M17
else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) ||
(state->synctype == 76) || (state->synctype == 77) || (state->synctype == 86) || (state->synctype == 87) ||
diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c
index e10f3cc..7f5fbf5 100644
--- a/src/dsd_frame_sync.c
+++ b/src/dsd_frame_sync.c
@@ -117,6 +117,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
* 36 = -P25 P2
* 37 = +EDACS
* 38 = -EDACS
+ * 40 = +Type2
+ * 41 = -Type2
*/
@@ -205,7 +207,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
{
t_max = 12; //based on Frame_Sync_2 pattern
}
- else if (opts->frame_m17 == 1)
+ else if (opts->frame_m17 == 1 || opts->frame_typeii == 1)
{
t_max = 8;
}
@@ -581,6 +583,40 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
//end YSF sync
+ //Motorola Type II
+ strncpy(synctest8, (synctest_p - 7), 8);
+ if (opts->frame_typeii == 1)
+ {
+ if (strcmp(synctest8, TYPE_II_SYNC) == 0)
+ {
+ if (state->lastsynctype == 40)
+ {
+ printFrameSync (opts, state, "+Type II", synctest_pos + 1, modulation);
+ state->carrier = 1;
+ state->offset = synctest_pos;
+ state->max = ((state->max) + lmax) / 2;
+ state->min = ((state->min) + lmin) / 2;
+ state->lastsynctype = 40;
+ return (40);
+ }
+ else state->lastsynctype = 40;
+ }
+ else if (strcmp(synctest8, TYPE_II_SINV) == 0)
+ {
+ if (state->lastsynctype == 41)
+ {
+ printFrameSync (opts, state, "-Type II", synctest_pos + 1, modulation);
+ state->carrier = 1;
+ state->offset = synctest_pos;
+ state->max = ((state->max) + lmax) / 2;
+ state->min = ((state->min) + lmin) / 2;
+ state->lastsynctype = 41;
+ return (41);
+ }
+ else state->lastsynctype = 41;
+ }
+ }
+
//M17 Sync -- Just STR and LSF for now
strncpy(synctest16, (synctest_p - 15), 16);
strncpy(synctest8, (synctest_p - 7), 8);
diff --git a/src/dsd_main.c b/src/dsd_main.c
index ca878e9..c5a3e95 100644
--- a/src/dsd_main.c
+++ b/src/dsd_main.c
@@ -622,6 +622,7 @@ initOpts (dsd_opts * opts)
opts->frame_provoice = 0;
opts->frame_ysf = 1;
opts->frame_m17 = 0;
+ opts->frame_typeii = 0;
opts->mod_c4fm = 1;
opts->mod_qpsk = 0;
opts->mod_gfsk = 0;
@@ -1377,6 +1378,7 @@ usage ()
printf (" -fe Decode only EDACS EA/ProVoice*\n");
printf (" -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n");
printf (" -fm Decode only dPMR*\n");
+ printf (" -fT Decode only Type II (Smartnet/Smartzone)*\n");
printf (" -l Disable DMR, dPMR, NXDN, M17 input filtering\n");
printf (" -u <num> Unvoiced speech quality (default=3)\n");
printf (" -xx Expect non-inverted X2-TDMA signal\n");
@@ -2431,6 +2433,39 @@ main (int argc, char **argv)
opts.rtl_bandwidth = 24;
// opts.rtl_gain_value = 36;
}
+ else if (optarg[0] == 'T') //Motorola Type II
+ {
+ opts.frame_dstar = 0;
+ opts.frame_x2tdma = 0;
+ opts.frame_p25p1 = 0;
+ opts.frame_p25p2 = 0;
+ opts.frame_nxdn48 = 0;
+ opts.frame_nxdn96 = 0;
+ opts.frame_dmr = 0;
+ opts.frame_dpmr = 0;
+ opts.frame_provoice = 0;
+ opts.frame_ysf = 0;
+ opts.frame_m17 = 0;
+ opts.frame_typeii = 1;
+ ////to maintain 10/4 sample per symbol rate, we need to run at 36000
+ //to hit TypeII's 3600 bps rating. BW values in rtl_fm are also modified
+ //similarly, but testing will need to be done, this MAY NOT be viable in DSD-FME.
+ opts.pulse_digi_rate_in = 36000;
+ opts.wav_sample_rate = 36000;
+ opts.mod_c4fm = 1;
+ opts.mod_qpsk = 0;
+ opts.mod_gfsk = 0; //might still be better as gfsk
+ state.rf_mod = 2;
+ opts.pulse_digi_rate_out = 8000;
+ opts.pulse_digi_out_channels = 1;
+ opts.dmr_stereo = 0;
+ opts.dmr_mono = 0;
+ state.dmr_stereo = 0;
+ // opts.setmod_bw = 12500;
+ sprintf (opts.output_name, "Type II");
+ // fprintf (stderr,"Setting symbol rate to 4800 / second\n");
+ fprintf (stderr,"Decoding Motorola Type II frames.\n");
+ }
else if (optarg[0] == '1')
{
opts.frame_dstar = 0;
diff --git a/src/rtl_sdr_fm.cpp b/src/rtl_sdr_fm.cpp
index ad04667..954c6dc 100644
--- a/src/rtl_sdr_fm.cpp
+++ b/src/rtl_sdr_fm.cpp
@@ -1025,6 +1025,7 @@ void rtlsdr_sighandler()
void open_rtlsdr_stream(dsd_opts *opts)
{
int r;
+ if (opts->frame_typeii == 1) bandwidth_divisor = 36000;
rtl_bandwidth = opts->rtl_bandwidth * 1000; //reverted back to straight value
bandwidth_multiplier = (bandwidth_divisor / rtl_bandwidth);
volume_multiplier = 1; //moved to external handling to be more dynamic
diff --git a/src/typeII.c b/src/typeII.c
new file mode 100644
index 0000000..eb0cd56
--- /dev/null
+++ b/src/typeII.c
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------------
+ * typeII.c
+ * Motorola Type II Decoder (WIP)
+ *
+ *
+ *
+ *
+ * LWVMOBILE
+ * 2024-03 DSD-FME Florida Man Edition
+ *-----------------------------------------------------------------------------*/
+
+#include "dsd.h"
+
+//Type II
+void type_ii(dsd_opts * opts, dsd_state * state)
+{
+ int i;
+ uint8_t t2[80]; memset(t2, 0, sizeof(t2));
+
+ for (i = 0; i < 76; i++)
+ t2[i] = getDibit(opts, state);
+
+ //debug dump collected bits
+ fprintf (stderr, "Frame:");
+ for (i = 0; i < 10; i++)
+ fprintf (stderr, " %02X", (uint8_t)ConvertBitIntoBytes(&t2[i*8], 8));
+
+ fprintf (stderr, "\n");
+}
\ No newline at end of file

View File

@@ -1,8 +1,8 @@
#! /bin/bash #! /bin/bash
#
clear clear
printf "DSD-FME Digital Speech Decoder - Florida Man Edition echo DSD-FME Digital Speech Decoder - Florida Man Edition
Automatic Git Pull and Rebuild\n\n" echo Automatic Git Pull and Rebuild
echo
sleep 1 sleep 1
##Open your clone folder## ##Open your clone folder##
git pull git pull
@@ -10,6 +10,7 @@ sleep 2
##cd into your build folder## ##cd into your build folder##
cd build cd build
cmake .. cmake ..
make -j $(nproc) make -j `nproc`
sudo make install sudo make install
sudo ldconfig sudo ldconfig

View File

@@ -1,922 +0,0 @@
/*-------------------------------------------------------------------------------
* aes.c Tinier AES
* Easy To Use (and configurable) AES Output and Utility Bit/Byte Functions
*
* Modified Tiny AES code for more variable nk/nr/nb values, all in one file
* https://github.com/kokke/tiny-AES-c
*-----------------------------------------------------------------------------*/
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include "dsd.h"
#define AES_BLOCKLEN 16
unsigned Nb = 4;
unsigned Nk = 8;
unsigned Nr = 14;
struct AES_ctx
{
uint8_t RoundKey[240];
uint8_t Iv[16];
};
//internal function prototypes
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
typedef uint8_t state_t[4][4];
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
#define getSBoxValue(num) (sbox[(num)])
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];
}
if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
if (Nk == 8) //only run if using AES256 (Nk == 8)
{
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
}
j = i * 4; k=(i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
//input bit array, return output as up to a 64-bit value
uint64_t convert_bits_into_output_ta(uint8_t * input, int len)
{
int i;
uint64_t output = 0;
for(i = 0; i < len; i++)
{
output <<= 1;
output |= (uint64_t)(input[i] & 1);
}
return output;
}
//take x amount of bits and pack into len amount of bytes (symmetrical)
void pack_bit_array_into_byte_array_ta (uint8_t * input, uint8_t * output, int len)
{
int i;
for (i = 0; i < len; i++)
output[i] = (uint8_t)convert_bits_into_output_ta(&input[i*8], 8);
}
//take len amount of bytes and unpack back into a bit array
void unpack_byte_array_into_bit_array_ta (uint8_t * input, uint8_t * output, int len)
{
int i = 0, k = 0;
for (i = 0; i < len; i++)
{
output[k++] = (input[i] >> 7) & 1;
output[k++] = (input[i] >> 6) & 1;
output[k++] = (input[i] >> 5) & 1;
output[k++] = (input[i] >> 4) & 1;
output[k++] = (input[i] >> 3) & 1;
output[k++] = (input[i] >> 2) & 1;
output[k++] = (input[i] >> 1) & 1;
output[k++] = (input[i] >> 0) & 1;
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#define getSBoxInvert(num) (rsbox[(num)])
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
// Cipher is the main function that encrypts the PlainText,
// or produces a keystream, depending on application.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without MixColumns()
for (round = 1; ; ++round)
{
SubBytes(state);
ShiftRows(state);
if (round == Nr) {
break;
}
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// Add round key to last round
AddRoundKey(Nr, state, RoundKey);
}
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without InvMixColumn()
for (round = (Nr - 1); ; --round)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
if (round == 0) {
break;
}
InvMixColumns(state);
}
}
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i)
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
uint8_t buffer[AES_BLOCKLEN];
size_t i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer,ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
//byte-wise output of AES OFB Keystream
//input iv is a 16-byte uint8_t array of initialization vector
//input key is up to 32-byte uint8_t array of key value
//input type is the type/key len of AES required (0-128, 1-192, 2-256)
//input nblocks is the number of rounds of 16-byte keystream output blocks requried
//output is a uint8_t bytewise array, each round filled with 16-bytes from aes keystream output
void aes_ofb_keystream_output (uint8_t * iv, uint8_t * key, uint8_t * output, int type, int nblocks)
{
int i;
uint8_t input_register[16]; //OFB Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//load first round of input_register with received IV (OFB First Input Register)
memcpy (input_register, iv, 16*sizeof(uint8_t) );
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//execute the cipher function, and copy ciphered input_register to output for required number of rounds
for (i = 0; i < nblocks; i++)
{
Cipher((state_t*)input_register, ctx.RoundKey); //input_register is returned as output, and is put back in as object feedback
memcpy (output+(i*16), input_register, 16*sizeof(uint8_t) ); //copy ciphered input_register to output
}
}
//byte-wise AES CFB (Cipher Feedback) Payload operating in 128-bit block mode
//input in is a uint8_t bytewise array,is the input to be ciphered (encrypted or decrypted)
//input iv is a 16-byte uint8_t array of initialization vector
//input key is up to 32-byte uint8_t array of key value
//input type is the type/key len of AES required (0-128, 1-192, 2-256)
//input nblocks is the number of rounds of 16-byte payload blocks requried (last block will need padding if not flush)
//output out is a uint8_t bytewise array, each round filled with 16-bytes of cfb ciphered output (encrypted or decrypted)
//de is a bit-flag signalling to run Cipher (encrypt) on 1, or InvCipher (decrypt) on 0
void aes_cfb_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in, uint8_t * out, int type, int nblocks, int de)
{
int i, j;
uint8_t input_register[16]; //Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//load first round of input_register with received IV (CFB First Input Register)
memcpy (input_register, iv, 16*sizeof(uint8_t) );
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//execute the cipher function, and copy ciphered input_register to output for required number of rounds
for (i = 0; i < nblocks; i++)
{
//the cipher is always run in the foward, or encryption mode
Cipher((state_t*)input_register, ctx.RoundKey);
//xor the current input 'in' to the current state of the input_register for cipher feedback
for (j = 0; j < 16; j++)
input_register[j] ^= in[j+(i*16)];
//copy ciphered/xor'd input_register to output 'out'
memcpy (out+(i*16), input_register, 16*sizeof(uint8_t) );
//if running in decryption mode, we feed in the next round of input
if (!de)
memcpy(input_register, in+(i*16), 16*sizeof(uint8_t));
}
}
//byte-wise AES CBC (Cipher Block Chaining) //Yeah, I know this already exists, but wanted a custom convenience wrapper version
//input in is a uint8_t bytewise array,is the input to be ciphered (encrypted or decrypted)
//input iv is a 16-byte uint8_t array of initialization vector
//input key is up to 32-byte uint8_t array of key value
//input type is the type/key len of AES required (0-128, 1-192, 2-256)
//input nblocks is the number of rounds of 16-byte payload blocks requried (last block will need padding if not flush)
//output out is a uint8_t bytewise array, each round filled with 16-bytes of cfb ciphered output (encrypted or decrypted)
//de is a bit-flag signalling to run Cipher (encrypt) on 1, or InvCipher (decrypt) on 0
void aes_cbc_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in, uint8_t * out, int type, int nblocks, int de)
{
int i, j;
uint8_t input_register[16]; //Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//load first round of input_register accordingly
if (de)
memcpy (input_register, iv, 16*sizeof(uint8_t) );
else memcpy (input_register, in, 16*sizeof(uint8_t) );
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//
for (i = 0; i < nblocks; i++)
{
//run encryption or decryption depending on de value
if (de) //encrypt
{
//xor the current input 'in' pt to the current state of the input_register for cbc feedback
for (j = 0; j < 16; j++)
input_register[j] ^= in[j+(i*16)];
Cipher((state_t*)input_register, ctx.RoundKey);
//copy ciphered input_register to output 'out'
memcpy (out+(i*16), input_register, 16*sizeof(uint8_t) );
}
else //decrypt
{
InvCipher((state_t*)input_register, ctx.RoundKey);
//copy ciphered input_register to output 'out'
memcpy (out+(i*16), input_register, 16*sizeof(uint8_t) );
//xor the current output by IV, or by last received CT
if (i == 0)
{
for (j = 0; j < 16; j++)
out[j] ^= iv[j];
}
else
{
for (j = 0; j < 16; j++)
out[j+(i*16)] ^= in[j+((i-1)*16)];
}
//copy in next segment for input_register (if not last)
if (i < nblocks)
memcpy(input_register, in+((i+1)*16), 16*sizeof(uint8_t) );
}
}
}
//byte-wise AES CBC_MAC (Cipher Block Chaining Message Authentication) //This is slightly different than above, no IV is present,
//but if iv is desireable, it will need to be pre-XOR'd with the first plaintext input block by the calling function
//input in is a uint8_t bytewise array, is the input to be ciphered.
//input key is up to 32-byte uint8_t array of key value
//input type is the type/key len of AES required (0-128, 1-192, 2-256)
//input nblocks is the number of rounds of 16-byte payload blocks requried (last block will need padding if not flush)
//output out is a uint8_t bytewise array, with only the final round output as the MAC octets
//NOTE: When doing a cbc_mac, you should only run it in the forward (encryption) mode to get the mac bytes
void aes_cbc_mac_generator (uint8_t * key, uint8_t * in, uint8_t * out, int type, int nblocks)
{
int i, j;
uint8_t input_register[16]; //Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//
for (i = 0; i < nblocks; i++)
{
//xor the current input 'in' pt to the current state of the input_register for cbc feedback
//if this is the first iteration, this will load the first round plain text instead
for (j = 0; j < 16; j++)
input_register[j] ^= in[j+((i+0)*16)];
Cipher((state_t*)input_register, ctx.RoundKey);
//debug, load out all intermediate output register values
// memcpy (out+(i*16), input_register, 16*sizeof(uint8_t) );
}
//copy final ciphered input_register to output 'out', user will determine how many bytes of output they want for MAC
memcpy (out, input_register, 16*sizeof(uint8_t) );
}
//byte-wise output of AES ECB Ciphering/Deciphering
//input is uint8_t byte-wise (16-bytes) data to be ciphered or deciphered
//input key is up to 32-byte uint8_t array of key value
//input type is the type/len of AES required (0-128, 1-192, 2-256)
//output is a uint8_t bytewise array of ciphered or deciphered input
//de is a bit-flag signalling to run Cipher (encrypt) on 1, or InvCipher (decrypt) on 0
void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de)
{
uint8_t input_register[16]; //ECB Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//load input_register with received input (ECB Payload)
memcpy (input_register, input, 16*sizeof(uint8_t) );
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//run encryption or decryption depending on de value
if (de) //encrypt
Cipher((state_t*)input_register, ctx.RoundKey);
else //decrypt
InvCipher((state_t*)input_register, ctx.RoundKey);
//copy ciphered/deciphered input_register to output
memcpy (output, input_register, 16*sizeof(uint8_t) );
}
//symmetrical ctr mode payload encryption and decryption
void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type)
{
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//init and set the iv and key variables
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
memset (ctx.Iv, 0, 16*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
memcpy (ctx.Iv, iv, AES_BLOCKLEN);
//pack input bit-wise payload to byte array
uint8_t payload_bytes[16];
memset (payload_bytes, 0, sizeof(payload_bytes));
pack_bit_array_into_byte_array_ta (payload, payload_bytes, 16);
//pass to internal CTR handler for payload
AES_CTR_xcrypt_buffer(&ctx, payload_bytes, 16);
//unpack output bytes back to bits
unpack_byte_array_into_bit_array_ta(payload_bytes, payload, 16);
}
//symmetrical ctr mode payload encryption and decryption
void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type)
{
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//init and set the iv and key variables
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
memset (ctx.Iv, 0, 16*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
memcpy (ctx.Iv, iv, AES_BLOCKLEN);
//pass to internal CTR handler for payload
AES_CTR_xcrypt_buffer(&ctx, payload, 16);
}

View File

@@ -1,832 +0,0 @@
/*-------------------------------------------------------------------------------
* des.c Crypthings
* DES Alg
*-----------------------------------------------------------------------------*/
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "dsd.h"
//NOTE: The SLUT boxes are S boxes with additional calculations so we don't have
//to manually sort out row and column values and can cut down on operations
uint8_t SLUT1[64] = {
0xE0,0x00,0x40,0xF0,0xD0,0x70,0x10,0x40,
0x20,0xE0,0xF0,0x20,0xB0,0xD0,0x80,0x10,
0x30,0xA0,0xA0,0x60,0x60,0xC0,0xC0,0xB0,
0x50,0x90,0x90,0x50,0x00,0x30,0x70,0x80,
0x40,0xF0,0x10,0xC0,0xE0,0x80,0x80,0x20,
0xD0,0x40,0x60,0x90,0x20,0x10,0xB0,0x70,
0xF0,0x50,0xC0,0xB0,0x90,0x30,0x70,0xE0,
0x30,0xA0,0xA0,0x00,0x50,0x60,0x00,0xD0
};
uint8_t SLUT2[64] = {
0x0F,0x03,0x01,0x0D,0x08,0x04,0x0E,0x07,
0x06,0x0F,0x0B,0x02,0x03,0x08,0x04,0x0E,
0x09,0x0C,0x07,0x00,0x02,0x01,0x0D,0x0A,
0x0C,0x06,0x00,0x09,0x05,0x0B,0x0A,0x05,
0x00,0x0D,0x0E,0x08,0x07,0x0A,0x0B,0x01,
0x0A,0x03,0x04,0x0F,0x0D,0x04,0x01,0x02,
0x05,0x0B,0x08,0x06,0x0C,0x07,0x06,0x0C,
0x09,0x00,0x03,0x05,0x02,0x0E,0x0F,0x09
};
uint8_t SLUT3[64] = {
0xA0,0xD0,0x00,0x70,0x90,0x00,0xE0,0x90,
0x60,0x30,0x30,0x40,0xF0,0x60,0x50,0xA0,
0x10,0x20,0xD0,0x80,0xC0,0x50,0x70,0xE0,
0xB0,0xC0,0x40,0xB0,0x20,0xF0,0x80,0x10,
0xD0,0x10,0x60,0xA0,0x40,0xD0,0x90,0x00,
0x80,0x60,0xF0,0x90,0x30,0x80,0x00,0x70,
0xB0,0x40,0x10,0xF0,0x20,0xE0,0xC0,0x30,
0x50,0xB0,0xA0,0x50,0xE0,0x20,0x70,0xC0
};
uint8_t SLUT4[64] = {
0x07,0x0D,0x0D,0x08,0x0E,0x0B,0x03,0x05,
0x00,0x06,0x06,0x0F,0x09,0x00,0x0A,0x03,
0x01,0x04,0x02,0x07,0x08,0x02,0x05,0x0C,
0x0B,0x01,0x0C,0x0A,0x04,0x0E,0x0F,0x09,
0x0A,0x03,0x06,0x0F,0x09,0x00,0x00,0x06,
0x0C,0x0A,0x0B,0x01,0x07,0x0D,0x0D,0x08,
0x0F,0x09,0x01,0x04,0x03,0x05,0x0E,0x0B,
0x05,0x0C,0x02,0x07,0x08,0x02,0x04,0x0E
};
uint8_t SLUT5[64] = {
0x20,0xE0,0xC0,0xB0,0x40,0x20,0x10,0xC0,
0x70,0x40,0xA0,0x70,0xB0,0xD0,0x60,0x10,
0x80,0x50,0x50,0x00,0x30,0xF0,0xF0,0xA0,
0xD0,0x30,0x00,0x90,0xE0,0x80,0x90,0x60,
0x40,0xB0,0x20,0x80,0x10,0xC0,0xB0,0x70,
0xA0,0x10,0xD0,0xE0,0x70,0x20,0x80,0xD0,
0xF0,0x60,0x90,0xF0,0xC0,0x00,0x50,0x90,
0x60,0xA0,0x30,0x40,0x00,0x50,0xE0,0x30
};
uint8_t SLUT6[64] = {
0x0C,0x0A,0x01,0x0F,0x0A,0x04,0x0F,0x02,
0x09,0x07,0x02,0x0C,0x06,0x09,0x08,0x05,
0x00,0x06,0x0D,0x01,0x03,0x0D,0x04,0x0E,
0x0E,0x00,0x07,0x0B,0x05,0x03,0x0B,0x08,
0x09,0x04,0x0E,0x03,0x0F,0x02,0x05,0x0C,
0x02,0x09,0x08,0x05,0x0C,0x0F,0x03,0x0A,
0x07,0x0B,0x00,0x0E,0x04,0x01,0x0A,0x07,
0x01,0x06,0x0D,0x00,0x0B,0x08,0x06,0x0D
};
uint8_t SLUT7[64] = {
0x40,0xD0,0xB0,0x00,0x20,0xB0,0xE0,0x70,
0xF0,0x40,0x00,0x90,0x80,0x10,0xD0,0xA0,
0x30,0xE0,0xC0,0x30,0x90,0x50,0x70,0xC0,
0x50,0x20,0xA0,0xF0,0x60,0x80,0x10,0x60,
0x10,0x60,0x40,0xB0,0xB0,0xD0,0xD0,0x80,
0xC0,0x10,0x30,0x40,0x70,0xA0,0xE0,0x70,
0xA0,0x90,0xF0,0x50,0x60,0x00,0x80,0xF0,
0x00,0xE0,0x50,0x20,0x90,0x30,0x20,0xC0
};
uint8_t SLUT8[64] = {
0x0D,0x01,0x02,0x0F,0x08,0x0D,0x04,0x08,
0x06,0x0A,0x0F,0x03,0x0B,0x07,0x01,0x04,
0x0A,0x0C,0x09,0x05,0x03,0x06,0x0E,0x0B,
0x05,0x00,0x00,0x0E,0x0C,0x09,0x07,0x02,
0x07,0x02,0x0B,0x01,0x04,0x0E,0x01,0x07,
0x09,0x04,0x0C,0x0A,0x0E,0x08,0x02,0x0D,
0x00,0x0F,0x06,0x0C,0x0A,0x09,0x0D,0x00,
0x0F,0x03,0x03,0x05,0x05,0x06,0x08,0x0B
};
//initial permutation IP (on input)
uint8_t initial_register_permutation[64] = {
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7};
//D-Box Expansion (sometimes noted as E BIT-SELECTION TABLE)
uint8_t message_expansion[48] = {
32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1};
// (sometimes noted as simply 'P')
uint8_t right_half_permutation[32] = {
16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25};
//inverse initial PI, a.k.a. final permutation (on output)
uint8_t final_register_permutation[64] = {
40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25};
//initial key permutation
uint8_t pc1_key_permutation[56] = {
57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4};
//sub c and d key permutation
uint8_t pc2_key_permutation[48] = {
//c bits
14,17,11,24,1,5,
3,28,15,6,21,10,
23,19,12,4,26,8,
16,7,27,20,13,2,
//d bits (with -28 offset to simply/only have one permute function)
13,24,3,9,19,27,
2,12,23,17,5,20,
16,21,11,28,6,25,
18,14,22,8,1,4
};
uint8_t key_shift_sizes[17] = {
1,
1, 1, 2, 2,
2, 2, 2, 2,
1, 2, 2, 2,
2, 2, 2, 1};
uint8_t key_shift_bytes[17] = {
0x80,
0x80, 0x80, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0,
0x80, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0x80};
uint8_t key_shift_x[4] = {8,8,8,4};
uint8_t key_shift_y[4] = {1,2,3,0};
//input is byte-wise array, output is byte-wise array
void permute(uint8_t * input, uint8_t * output, uint8_t * table, uint8_t start, uint8_t end)
{
for (uint8_t i = start; i < end; i++)
{
uint8_t tdiv = (table[i] - 1) / 8;
uint8_t tmod = (table[i] - 1) % 8;
uint8_t idiv = i/8;
uint8_t imod = i%8;
//need a way to simplify or get rid of all these shifts
uint8_t bit = (((input[tdiv] & (0x80 >> tmod)) << tmod) >> imod);
output[idiv] |= bit;
}
}
void rotate_sub_keys(uint8_t * Kc, uint8_t * Kd, uint8_t shift_size, uint8_t shift_byte)
{
uint8_t sb1[4]; memset(sb1, 0, sizeof(sb1));
uint8_t sb2[4]; memset(sb2, 0, sizeof(sb2));
for (uint8_t i = 0; i < 4; i++)
{
sb1[i] = shift_byte & Kc[i];
sb2[i] = shift_byte & Kd[i];
}
for (uint8_t i = 0; i < 4; i++)
{
uint8_t x = key_shift_x[i];
uint8_t y = key_shift_y[i];
Kc[i] <<= shift_size;
Kd[i] <<= shift_size;
Kc[i] |= (sb1[y] >> (x - shift_size));
Kd[i] |= (sb2[y] >> (x - shift_size));
}
}
void des_cipher (uint8_t * main_key, uint8_t * input_register, uint8_t * output_register, uint8_t de)
{
//starting and ending permutations
uint8_t initial_permutation[8]; memset(initial_permutation, 0, sizeof(initial_permutation));
uint8_t pre_end_permutation[8]; memset(pre_end_permutation, 0, sizeof(pre_end_permutation));
//intermediate left, right, and expansion/selection arrays
uint8_t left[4]; memset(left, 0, sizeof(left));
uint8_t right[4]; memset(right, 0, sizeof(right));
uint8_t rperm[4]; memset(rperm, 0, sizeof(rperm));
uint8_t exp[6]; memset(exp, 0, sizeof(exp));
uint8_t sel[4]; memset(sel, 0, sizeof(sel));
//key sets
uint8_t Ks[16][7]; memset(Ks, 0, sizeof(Ks));
uint8_t Kc[4]; memset(Kc, 0, sizeof(Kc));
uint8_t Kd[4]; memset(Kd, 0, sizeof(Kd));
//initial Ks permutation and shuffle to Kc and Kd
permute (main_key, Ks[0], pc1_key_permutation, 0, 56);
for (uint8_t i = 0; i < 3; i++)
{
Kc[i] = Ks[0][i];
}
Kc[3] = Ks[0][3] & 0xF0;
for (uint8_t i = 0; i < 3; i++)
{
Kd[i] = (Ks[0][i+3] & 0x0F) << 4;
Kd[i] |= (Ks[0][i+4] & 0xF0) >> 4;
}
Kd[3] = (Ks[0][6] & 0x0F) << 4;
//reset Ks
memset(Ks, 0, sizeof(Ks));
//create the 16 Ks rounds
for (uint8_t i = 0; i < 16; i++)
{
uint8_t shift_size = key_shift_sizes[i+1];
uint8_t shift_byte = key_shift_bytes[i+1];
rotate_sub_keys(Kc, Kd, shift_size, shift_byte);
permute (Kc, Ks[i], pc2_key_permutation, 0, 24);
permute (Kd, Ks[i], pc2_key_permutation, 24, 48);
}
//permute the input_register with the ip (initial_register_permutation) table
permute (input_register, initial_permutation, initial_register_permutation, 0, 64);
//copy the initial permutation to left and right half arrays
memcpy (left, initial_permutation+0, sizeof(left));
memcpy (right, initial_permutation+4, sizeof(right));
//16 fiestel rounds
for (uint8_t f = 0; f < 16; f++)
{
//reset rperm and exp arrays
memset(rperm, 0, sizeof(rperm));
memset(exp, 0, sizeof(exp));
//expand right half from 32-bit to 48-bit
permute (right, exp, message_expansion, 0, 48);
//apply current iteration keyset to the expanded array (encryption cycles Ks forward, decryption goes in reverse)
if (de == 1)
{
for (uint8_t i = 0; i < 6; i++)
exp[i] ^= Ks[f][i];
}
else
{
for (uint8_t i = 0; i < 6; i++)
exp[i] ^= Ks[15-f][i];
}
//extract 6 bits for each sbox swap from the expanded array
uint8_t e1 = exp[0] >> 2;
uint8_t e2 = ((exp[0] & 0x3) << 4) | (exp[1] >> 4);
uint8_t e3 = ((exp[1] & 0xF) << 2) | (exp[2] >> 6);
uint8_t e4 = exp[2] & 0x3F;
uint8_t e5 = exp[3] >> 2;
uint8_t e6 = ((exp[3] & 0x3) << 4) | (exp[4] >> 4);
uint8_t e7 = ((exp[4] & 0xF) << 2) | (exp[5] >> 6);
uint8_t e8 = exp[5] & 0x3F;
//select 32-bits of each 48-bit expansion via modified S Box Look Up Tables
//swap the 6 bits for 4 bits via SLUT tables and OR them together into a selection array
sel[0] = SLUT1[e1] | SLUT2[e2];
sel[1] = SLUT3[e3] | SLUT4[e4];
sel[2] = SLUT5[e5] | SLUT6[e6];
sel[3] = SLUT7[e7] | SLUT8[e8];
//permute the selected 32-bits into a new right half
permute (sel, rperm, right_half_permutation, 0, 32);
//xor new right half against left half
for (uint8_t i = 0; i < 4; i++)
rperm[i] ^= left[i];
//swap old right half to left, set new right half to rperm
memcpy (left, right, sizeof(left));
memcpy (right, rperm, sizeof(right));
} //end fiestel rounds f
//combine right and left into the pre_end_permutation array
memcpy (pre_end_permutation+0, right, sizeof(right));
memcpy (pre_end_permutation+4, left, sizeof(left));
//carry out the final permuation to get the completed output_register for this cipher iteration
permute (pre_end_permutation, output_register, final_register_permutation, 0, 64);
}
void des56_ofb_keystream_output (uint8_t * main_key, uint8_t * iv, uint8_t * ks_bytes, uint8_t de, int16_t nblocks)
{
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, iv, sizeof(input_register));
//execute the des_cipher in output feedback mode
for (int16_t i = 0; i < nblocks; i++)
{
//de should be 1 here for encryption mode
des_cipher(main_key, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input register
memcpy(ks_bytes+(i*8), output_register, sizeof(output_register)); //copy output_register to ks_bytes + iteration offset of times 8
memset(output_register, 0, sizeof(output_register)); //reset output register
}
}
//the des_cipher function is already ECB mode, this convenience wrapper is just available as a reminder to illustrate that point
void des56_ecb_payload_crypt (uint8_t * main_key, uint8_t * input_register, uint8_t * output_register, uint8_t de)
{
//de 1 for encryption mode, 0 for decryption mode
des_cipher(main_key, input_register, output_register, de);
}
//TDEA, or triple data encryption algorithm, or triple DES, in electronic codebook mode
void tdea_ecb_payload_crypt (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * input, uint8_t * output, uint8_t de)
{
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the input to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, input, sizeof(input_register));
//For TDEA, the cipher alternates between encryption and decryption to the payload
//so, for example, K1 is run as de=1, K2 is run as de=0, and K3 is run as de=1,
//K1 and K3 will always use the same mode and K2 will be the opposite
//NOTE: If running ECB mode in decryption, make sure to send the keys in reverse order
//so that its K3, K2, and K1 for decryption, and K1, K2, K3 for encryption
//K1
des_cipher(K1, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
de = (de ^ 1) & 1; //flip the de bit
des_cipher(K2, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
de = (de ^ 1) & 1; //flip the de bit back
des_cipher(K3, input_register, output_register, de);
//copy payload out
memcpy(output, output_register, sizeof(output_register)); //copy output_register to output
}
//TDEA, or triple data encryption algorithm, or triple DES, in cipher block chain mode (64-bit)
//same as usual inputs (byte wise), also, if needing DES56, just feed the same key into K1, K2, and K3
//if running in decryption mode, this will reverse the key bundle order from (K1, K2, K3) to (K3, K2, K1) on this end
void tdea_cbc_payload_crypt (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * iv, uint8_t * in, uint8_t * out, int16_t nblocks, uint8_t de)
{
int16_t i, j;
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//load first round of input_register accordingly
if (de)
memcpy (input_register, iv, sizeof(input_register)); //load the IV as first input_register if encrypting
else memcpy (input_register, in, sizeof(input_register)); //load first cipher text as input_register is decrypting
//run payload for number of payload nblocks required
for (i = 0; i < nblocks; i++)
{
if (de)
{
//xor the current input 'in' pt to the current state of the input_register for cbc feedback
for (j = 0; j < 8; j++)
input_register[j] ^= in[j+(i*8)];
//K1
des_cipher(K1, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
de = (de ^ 1) & 1; //flip the de bit
des_cipher(K2, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
de = (de ^ 1) & 1; //flip the de bit back
des_cipher(K3, input_register, output_register, de);
memcpy (input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
//copy ciphered output_register to output 'out'
memcpy (out+(i*8), output_register, sizeof(output_register));
memset(output_register, 0, sizeof(output_register)); //reset output register
}
else
{
//K3
des_cipher(K3, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
de = (de ^ 1) & 1; //flip the de bit
des_cipher(K2, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K1
de = (de ^ 1) & 1; //flip the de bit back
des_cipher(K1, input_register, output_register, de);
memcpy (input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
//copy ciphered input_register to output 'out'
memcpy (out+(i*8), output_register, sizeof(output_register));
//xor the current output by IV, or by last received CT, depending on round
if (i == 0)
{
for (j = 0; j < 8; j++)
out[j] ^= iv[j];
}
else
{
for (j = 0; j < 8; j++)
out[j+(i*8)] ^= in[j+((i-1)*8)];
}
//copy in next segment for input_register (if not last)
if (i < nblocks)
memcpy(input_register, in+((i+1)*8), sizeof(input_register));
memset(output_register, 0, sizeof(output_register)); //reset output register
}
}
}
//TDEA, or triple data encryption algorithm, or triple DES, in cipher block chain mode (64-bit)
//same as usual inputs (byte wise), also, if needing DES56, just feed the same key into K1, K2, and K3
//only the last output_register is returned for the variable len MAC bytes, always run in encryption mode
//but if iv is desireable, it will need to be pre-XOR'd with the first plaintext input block by the calling function
void tdea_cbc_mac_generator (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * in, uint8_t * out, int16_t nblocks)
{
int16_t i, j;
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//run payload for number of payload nblocks required
for (i = 0; i < nblocks; i++)
{
//the cipher is always run in the foward, or encryption mode (1,0,1)
//xor the current input 'in' pt to the current state of the input_register for cbc feedback
for (j = 0; j < 8; j++)
input_register[j] ^= in[j+(i*8)];
//K1
des_cipher(K1, input_register, output_register, 1);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
des_cipher(K2, input_register, output_register, 0);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
des_cipher(K3, input_register, output_register, 1);
memcpy (input_register, output_register, sizeof(output_register));
//copy ciphered output_register to output 'out'
//Since we only want the last output_register,
//this will overwrite the out until completion
memcpy (out, output_register, sizeof(output_register));
memset(output_register, 0, sizeof(output_register)); //reset output register
}
}
//TDEA, or triple data encryption algorithm, or triple DES, in cihper feedback mode (64-bit)
//same as usual inputs (byte wise), also, if needing DES56, just feed the same key into K1, K2, and K3
void tdea_cfb_payload_crypt (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * iv, uint8_t * in, uint8_t * out, int16_t nblocks, uint8_t de)
{
int16_t i, j;
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, iv, sizeof(input_register));
//execute the des_cipher in output feedback mode 3 times using each key and transferring output to input each time
for (i = 0; i < nblocks; i++)
{
//the cipher is always run in the foward, or encryption mode (1,0,1)
//K1
des_cipher(K1, input_register, output_register, 1);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
des_cipher(K2, input_register, output_register, 0);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
des_cipher(K3, input_register, output_register, 1);
//xor the current input 'in' to the current state of the input_register for cipher feedback
for (j = 0; j < 8; j++)
output_register[j] ^= in[j+(i*8)];
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
//copy keystream out and reset
memcpy(out+(i*8), output_register, sizeof(output_register)); //copy output_register to ks_bytes + iteration offset of times 8
memset(output_register, 0, sizeof(output_register)); //reset output register
//if running in decryption mode, we feed in the next round of input
if (!de)
memcpy(input_register, in+(i*8), sizeof(input_register));
}
}
//TDEA, or triple data encryption algorithm, or triple DES, in IV counter mode (tested, working)
void tdea_ctr_payload_crypt (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * iv, uint8_t * input, uint8_t * output, int16_t nblocks)
{
int16_t i, j;
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (ctr mode will manipulate the IV, since it needs to keep a rolling counter
memcpy(input_register, iv, sizeof(input_register));
//execute the des_cipher in counter mode 3 times using each key and transferring output to input each time
//then the IV is iterated and fed back into the input_register to start the next nblocks loop
for (i = 0; i < nblocks; i++)
{
//CTR mode cipher should always run in the forward (encryption) mode
//K1
des_cipher(K1, input_register, output_register, 1);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
des_cipher(K2, input_register, output_register, 0);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
des_cipher(K3, input_register, output_register, 1);
//set output at current pointer to the xor of input current pointer and the output_register
for (j = 0; j < 8; j++)
output[j+(i*8)] = input[j+(i*8)] ^ output_register[j];
memset(output_register, 0, sizeof(output_register)); //reset output register
//increment the IV, and handle roll over (uint8_t will rollover to 0 after 0xFF)
for (j = 7; j >= 0; j--)
{
iv[j]++;
if (iv[j] == 0)
continue;
else break;
}
//debug IV iteration
// fprintf (stderr, "\n IV: ");
// for (j = 0; j < 8; j++)
// fprintf (stderr, "%02X", iv[j]);
//feed the new IV into the input register
memcpy (input_register, iv, sizeof(input_register));
}
}
//TDEA, or triple data encryption algorithm, or triple DES, in output feedback mode
void tdea_tofb_keystream_output (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * iv, uint8_t * ks_bytes, uint8_t de, int16_t nblocks)
{
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, iv, sizeof(input_register));
//execute the des_cipher in output feedback mode 3 times using each key and transferring output to input each time
for (int16_t i = 0; i < nblocks; i++)
{
//K1
des_cipher(K1, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
de = (de ^ 1) & 1; //flip the de bit
des_cipher(K2, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
de = (de ^ 1) & 1; //flip the de bit back
des_cipher(K3, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
//copy keystream out and reset
memcpy(ks_bytes+(i*8), output_register, sizeof(output_register)); //copy output_register to ks_bytes + iteration offset of times 8
memset(output_register, 0, sizeof(output_register)); //reset output register
}
}
//a linear feedback shift register with maximal taps on 64-bit values that can be run to any specified len,
//its input is a byte array of up to 8 bytes, and its output is same array packed with new LFSR value in it.
uint64_t lfsr_64_to_len_ca(uint8_t * iv, int16_t len)
{
uint64_t lfsr = 0, bit = 0;
lfsr = ((uint64_t)iv[0] << 56ULL) + ((uint64_t)iv[1] << 48ULL) + ((uint64_t)iv[2] << 40ULL) + ((uint64_t)iv[3] << 32ULL) +
((uint64_t)iv[4] << 24ULL) + ((uint64_t)iv[5] << 16ULL) + ((uint64_t)iv[6] << 8ULL) + ((uint64_t)iv[7] << 0ULL);
memset (iv, 0, 8*sizeof(uint8_t));
for(int16_t cnt = 0; cnt < len; cnt++)
{
//63,61,45,37,27,14
// Polynomial is C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
lfsr = (lfsr << 1) | bit;
}
for (int16_t i = 0; i < 8; i++)
iv[i] = (lfsr >> (56-(i*8))) & 0xFF;
// fprintf (stderr, "\n IV(%02d): ", len);
// for (int16_t i = 0; i < 8; i++)
// fprintf (stderr, "%02X", iv[i]);
return bit;
}
void des56_ca_keystream_output (uint8_t * main_key, uint8_t * iv, uint8_t * ks_bytes, uint8_t de, int16_t ff, int16_t nbits)
{
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, iv, sizeof(input_register));
//fast forward the current input_register state
lfsr_64_to_len_ca(input_register, ff);
//execute the des_cipher in (CA) mode with 1-bit output
for (int16_t i = 0; i < nbits; i++)
{
//de should be 1 here for encryption mode
des_cipher(main_key, input_register, output_register, de);
//keystream accumulation, shift current byte and append
//single bit from current output register's most significant bit
ks_bytes[i/8] <<= 1;
ks_bytes[i/8] |= ((output_register[0] >> 7) & 1);
//advance the input_register with the lfsr 1 time
lfsr_64_to_len_ca(input_register, 1);
//reset output register
memset(output_register, 0, sizeof(output_register));
}
// fprintf (stderr, "\n IR: ");
// for (int16_t i = 0; i < 8; i++)
// fprintf (stderr, "%02X", input_register[i]);
}
//transitional function mainly to load key and iv into an array
void des_multi_keystream_output (unsigned long long int mi, unsigned long long int key_ulli, uint8_t * output, int type, int len)
{
int i = 0;
uint8_t iv[8]; memset (iv, 0, sizeof(iv));
uint8_t key[8]; memset(key, 0, sizeof(key));
//convert unsinged long long int values into array values
for (i = 7; i >= 0; i--)
iv[7-i] = (mi >> (8*i)) & 0xFF;
for (i = 7; i >= 0; i--)
key[7-i] = (key_ulli >> (8*i)) & 0xFF;
//debug
// fprintf (stderr, "\n IV: ");
// for (i = 0; i < 8; i++)
// fprintf (stderr, "%02X", iv[i]);
// fprintf (stderr, "\n Key: ");
// for (i = 0; i < 8; i++)
// fprintf (stderr, "%02X", key[i]);
//types: 1 = DES56 OFB; 2 = DES56 CA (XL)
if (type == 2)
{
if (len == 0)
des56_ca_keystream_output(key, iv, output, 1, 110+696, 1704);
else des56_ca_keystream_output(key, iv, output, 1, 110+000, 1704);
}
else des56_ofb_keystream_output(key, iv, output, 1, len);
}
//transitional function mainly to load iv into an array
void tdea_multi_keystream_output (unsigned long long int mi, uint8_t * key, uint8_t * output, int type, int len)
{
int i = 0;
uint8_t iv[8]; memset (iv, 0, sizeof(iv));
//convert unsinged long long int values into array values
for (i = 7; i >= 0; i--)
iv[7-i] = (mi >> (8*i)) & 0xFF;
//debug
// fprintf (stderr, "\n IV: ");
// for (i = 0; i < 8; i++)
// fprintf (stderr, "%02X", iv[i]);
//type 1 = TDEA TOFB //TODO: Add more types like 2DES?
if (type == 1)
tdea_tofb_keystream_output(key, key+8, key+16, iv, output, 1, len);
else tdea_tofb_keystream_output(key, key+8, key+16, iv, output, 1, len);
}

View File

@@ -1,126 +0,0 @@
#include "dsd.h"
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input)
{
/*
SLOT 1 Protected LC FLCO=0x00 FID=0x20 <--this link appears to indicate scrambler usage from Kenwood on DMR
DMR PDU Payload [80][20][40][00][00][01][00][00][01] SB: 00000000000 - 000;
SLOT 1 TGT=1 SRC=1 FLCO=0x00 FID=0x00 SVC=0x00 Group Call <--different call, no scrambler from same Kenwood Radio
DMR PDU Payload [00][00][00][00][00][01][00][00][01]
For This, we could possible transition this to not be enforced
since we may have a positive indicator in link control,
but needs further samples and validation
*/
int lfsr = 0, bit = 0;
sscanf (input, "%d", &lfsr);
fprintf (stderr,"DMR Kenwood 15-bit Scrambler Key %05d with Forced Application\n", lfsr);
for (int i = 0; i < 882; i++)
{
state->static_ks_bits[0][i] = lfsr & 0x1;
state->static_ks_bits[1][i] = lfsr & 0x1;
bit = ( (lfsr >> 1) ^ (lfsr >> 0) ) & 1;
lfsr = ( (lfsr >> 1 ) | (bit << 14) );
}
state->ken_sc = 1;
}
void anytone_bp_keystream_creation(dsd_state * state, char * input)
{
uint16_t key = 0;
uint16_t kperm = 0;
sscanf (input, "%hX", &key);
key &= 0xFFFF; //truncate to 16-bits
//calculate key permutation using simple operations
uint8_t nib1, nib2, nib3, nib4;
//nib 1 and 3 are simple inversions
nib1 = ~(key >> 12) & 0xF;
nib3 = ~(key >> 4) & 0xF;
//nib 2 and 4 are +8 and mod 16 (& 0xF)
nib2 = (((key >> 8) & 0xF) + 8) % 16;
nib4 = (((key >> 0) & 0xF) + 8) % 16;
//debug
// fprintf (stderr, "{%01X, %01X, %01X, %01X}", nib1, nib2, nib3, nib4);
kperm = nib1;
kperm <<= 4;
kperm |= nib2;
kperm <<= 4;
kperm |= nib3;
kperm <<= 4;
kperm |= nib4;
//load bits into static keystream
for (int i = 0; i < 16; i++)
{
state->static_ks_bits[0][i] = (kperm >> (15-i)) & 1;
state->static_ks_bits[1][i] = (kperm >> (15-i)) & 1;
}
fprintf (stderr,"DMR Anytone Basic 16-bit Key 0x%04X with Forced Application\n", key);
state->any_bp = 1;
}
void straight_mod_xor_keystream_creation(dsd_state * state, char * input)
{
uint16_t len = 0;
char * curr;
curr = strtok(input, ":"); //should be len (mod) of key (decimal)
if (curr != NULL)
sscanf (curr, "%hd", &len);
else goto END_KS;
//len sanity check, can't be greater than 882
if (len > 882)
len = 882;
curr = strtok(NULL, ":"); //should be key in hex
if (curr != NULL)
{
//continue
}
else goto END_KS;
uint8_t ks_bytes[112];
memset (ks_bytes, 0, sizeof(ks_bytes));
parse_raw_user_string(curr, ks_bytes);
uint8_t ks_bits[896];
memset(ks_bits, 0, sizeof(ks_bits));
uint16_t unpack_len = len / 8;
if (len % 8)
unpack_len++;
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, unpack_len);
for (uint16_t i = 0; i < len; i++)
{
state->static_ks_bits[0][i] = ks_bits[i];
state->static_ks_bits[1][i] = ks_bits[i];
}
fprintf (stderr,"AMBE Straight XOR %d-bit Keystream: ", len);
for (uint16_t i = 0; i < unpack_len; i++)
fprintf (stderr, "%02X", ks_bytes[i]);
fprintf (stderr, " with Forced Application \n");
state->straight_ks = 1;
state->straight_mod = (int)len;
END_KS:
if (curr == NULL)
fprintf (stderr, "Straight KS String Malformed! No KS Created!\n");
}

View File

@@ -1,417 +0,0 @@
#include "dsd.h"
#include "pc4.h"
/* === Global MD2II buffers === */
int x1, x2, i, n1global;
unsigned char h2[n1];
unsigned char h1[n1*3];
/* === MD2II functions === */
void MD2II_init()
{
x1 = 0;
x2 = 0;
for (i = 0; i < n1global; i++)
h2[i] = 0;
for (i = 0; i < n1global; i++)
h1[i] = 0;
}
void MD2II_hashing(unsigned char t1[], size_t b6)
{
static unsigned char s4[256] =
{13,199,11,67,237,193,164,77,115,184,141,222,73,
38,147,36,150,87,21,104,12,61,156,101,111,145,
119,22,207,35,198,37,171,167,80,30,219,28,213,
121,86,29,214,242,6,4,89,162,110,175,19,157,
3,88,234,94,144,118,159,239,100,17,182,173,238,
68,16,79,132,54,163,52,9,58,57,55,229,192,
170,226,56,231,187,158,70,224,233,245,26,47,32,
44,247,8,251,20,197,185,109,153,204,218,93,178,
212,137,84,174,24,120,130,149,72,180,181,208,255,
189,152,18,143,176,60,249,27,227,128,139,243,253,
59,123,172,108,211,96,138,10,215,42,225,40,81,
65,90,25,98,126,154,64,124,116,122,5,1,168,
83,190,131,191,244,240,235,177,155,228,125,66,43,
201,248,220,129,188,230,62,75,71,78,34,31,216,
254,136,91,114,106,46,217,196,92,151,209,133,51,
236,33,252,127,179,69,7,183,105,146,97,39,15,
205,112,200,166,223,45,48,246,186,41,148,140,107,
76,85,95,194,142,50,49,134,23,135,169,221,210,
203,63,165,82,161,202,53,14,206,232,103,102,195,
117,250,99,0,74,160,241,2,113};
int b1, b2, b3, b4 = 0, b5;
while (b6) {
for (; b6 && x2 < n1global; b6--, x2++) {
b5 = t1[b4++];
h1[x2 + n1global] = b5;
h1[x2 + (n1global*2)] = b5 ^ h1[x2];
x1 = h2[x2] ^= s4[b5 ^ x1];
}
if (x2 == n1global) {
b2 = 0;
x2 = 0;
for (b3 = 0; b3 < (n1global+2); b3++) {
for (b1 = 0; b1 < (n1global*3); b1++)
b2 = h1[b1] ^= s4[b2];
b2 = (b2 + b3) % 256;
}
}
}
}
void MD2II_end(unsigned char h4[n1])
{
unsigned char h3[n1];
int i, n4;
n4 = n1global - x2;
for (i = 0; i < n4; i++) h3[i] = n4;
MD2II_hashing(h3, n4);
MD2II_hashing(h2, n1global);
for (i = 0; i < n1global; i++) h4[i] = h1[i];
}
void print_hex(const unsigned char *buf, int len)
{
for (int i = 0; i < len; i++)
printf("%02X ", buf[i]);
printf("\n");
}
static inline uint64_t rol48(uint64_t x, int n)
{
return ((x << n) | (x >> 47)) & 0xffffffffffff;
}
/* === Keystream (A5-like) === */
static int threshold(uint64_t r1, uint64_t r2, uint64_t r3)
{
int total = (((r1 >> 31) & 1) == 1) +
(((r2 >> 31) & 1) == 1) +
(((r3 >> 31) & 1) == 1);
return (total > 1) ? 0 : 1;
}
static uint64_t clock_r1(int ctl, uint64_t r1)
{
ctl ^= ((r1 >> 31) & 1);
if (ctl) {
int taps[] = {0,3,5,9,10,11,12,17,18,28,33,34,35,36,37,39,42,43,44,46,47,49,50,57,60,61,62,63};
uint64_t fb = 0;
for(int i=0;i<sizeof(taps)/sizeof(int);i++) fb ^= (r1>>taps[i])&1ULL;
r1 = (r1<<1)&0xFFFFFFFFFFFFFFFFULL;
if(fb&1ULL) r1 ^= 1ULL;
}
return r1;
}
static uint64_t clock_r2(int ctl, uint64_t r2)
{
ctl ^= ((r2 >> 31) & 1);
if (ctl) {
int taps[] = {0,3,5,8,9,10,12,13,15,17,19,20,21,22,24,27,30,31,33,34,35,36,37,40,41,42,51,52,55,56,59,60,62,63};
uint64_t fb = 0;
for(int i=0;i<sizeof(taps)/sizeof(int);i++) fb ^= (r2>>taps[i])&1ULL;
r2 = (r2<<1)&0xFFFFFFFFFFFFFFFFULL;
if(fb&1ULL) r2 ^= 1ULL;
}
return r2;
}
static uint64_t clock_r3(int ctl, uint64_t r3)
{
ctl ^= ((r3 >> 31) & 1);
if (ctl) {
int taps[] = {1,2,4,5,6,7,8,9,10,14,15,16,17,18,22,23,25,26,27,28,29,31,32,34,35,36,38,41,42,43,44,45,47,48,49,50,51,54,55,59,61,63};
uint64_t fb = 0;
for(int i=0;i<sizeof(taps)/sizeof(int);i++) fb ^= (r3>>taps[i])&1ULL;
r3 = (r3<<1)&0xFFFFFFFFFFFFFFFFULL;
if(fb&1ULL) r3 ^= 1ULL;
}
return r3;
}
void keystream37(unsigned char *key, uint64_t frame, unsigned char *output)
{
uint64_t r1=0,r2=0,r3=0;
for(int i=0;i<8;i++) r1=(r1<<8)|key[i];
for(int i=0;i<8;i++) r2=(r2<<8)|key[i+8];
for(int i=0;i<8;i++) r3=(r3<<8)|key[i+16];
for(int i=0;i<64;i++){
int ctl=threshold(r1,r2,r3);
r1=clock_r1(ctl,r1);
r2=clock_r2(ctl,r2);
r3=clock_r3(ctl,r3);
if(frame&1ULL){ r1^=1ULL; r2^=1ULL; r3^=1ULL;}
frame>>=1;
}
for(int i=0;i<384;i++){
int ctl=threshold(r1,r2,r3);
r1=clock_r1(ctl,r1);
r2=clock_r2(ctl,r2);
r3=clock_r3(ctl,r3);
}
unsigned char *ptr=output;
unsigned char byte=0;
int bits=0;
for(int i=0;i<1008;i++){
int ctl=threshold(r1,r2,r3);
r1=clock_r1(ctl,r1);
r2=clock_r2(ctl,r2);
r3=clock_r3(ctl,r3);
int bit=((r1>>63)^(r2>>63)^(r3>>63))&1U;
byte=(byte<<1)|bit;
bits++;
if(bits==8){*ptr++=byte; bits=0; byte=0;}
}
if(bits) *ptr=byte;
}
//dsd-fme -fs -Z -i '/SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/cap-clear.wav/cap-33.wav' -H '3333333333333333 3333333333333333 3333333333333333 3333333333333333'
void kirisun_uni_keystream_creation(dsd_state *state)
{
uint8_t slot = 0;
if (state->currentslot == 0)
slot = 0;
else slot = 1;
uint32_t lfsr = 0;
if (state->currentslot == 0)
lfsr = (uint32_t)state->payload_mi;
else lfsr = (uint32_t)state->payload_miR;
unsigned char userkeyhexa[32]; memset(userkeyhexa, 0, sizeof(userkeyhexa));
unsigned char realkey[32]; memset(realkey, 0, sizeof(realkey));
unsigned char lfsrhexa[4]; memset(lfsrhexa, 0, sizeof(lfsrhexa));
uint8_t ks_bytes[126]; memset(ks_bytes, 0, sizeof(ks_bytes));
lfsrhexa[0] = (lfsr >> 24) & 0xFF;
lfsrhexa[1] = (lfsr >> 16) & 0xFF;
lfsrhexa[2] = (lfsr >> 8) & 0xFF;
lfsrhexa[3] = (lfsr >> 0) & 0xFF;
uint64_t K1 = state->A1[slot];
uint64_t K2 = state->A2[slot];
uint64_t K3 = state->A3[slot];
uint64_t K4 = state->A4[slot];
if (K1 != 0 && K2 != 0 && K3 != 0 && K4 != 0)
state->aes_key_loaded[slot] = 1;
else state->aes_key_loaded[slot] = 0;
if (state->aes_key_loaded[slot] == 1)
{
u64_to_bytes_be(K1, &userkeyhexa[0]);
u64_to_bytes_be(K2, &userkeyhexa[8]);
u64_to_bytes_be(K3, &userkeyhexa[16]);
u64_to_bytes_be(K4, &userkeyhexa[24]);
for(int i=0;i<32;i++) realkey[i]=userkeyhexa[i];
/* --- MD2II first pass --- */
n1global=32;
unsigned char h4[n1]; memset(h4, 0, sizeof(h4));
MD2II_init();
MD2II_hashing(realkey,32);
MD2II_end(h4);
for(int i=0;i<32;i++) realkey[i]=h4[i];
// printf("=== realkey final (32 bytes) ===\n");
// print_hex(realkey,32);
/* --- MD2II with n1global=8, hash lfsr byte by byte --- */
unsigned char hashtag[1];
n1global=8;
MD2II_init();
for(int i=0;i<4;i++)
{
hashtag[0] = (lfsr >> (24-8*i)) & 0xFF;
MD2II_hashing(hashtag,1);
}
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("h4[0..7]: ");
// print_hex(h4,8);
uint64_t internal_state=0;
for(int i=0;i<8;i++)
internal_state=(internal_state<<8)+(h4[i]);
// printf("internal_state=%016llX\n",(unsigned long long)internal_state);
/* --- MD2II with n1global=24 --- */
n1global=24;
MD2II_init();
for(int i=0;i<4;i++)
{
hashtag[0] = (lfsr >> (24-8*i)) & 0xFF;
MD2II_hashing(hashtag,1);
}
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("realkey (24 bytes):\n");
// print_hex(h4,24);
/* --- keystream output --- */
unsigned char output[126]; memset(output, 0, sizeof(output));
keystream37(h4, internal_state, output);
// printf("keystream output (126 bytes):\n");
// print_hex(output,126);
//store ks_bytes to storage
for (int i = 0; i < 126; i++)
ks_bytes[i] = (uint8_t)output[i];
if (slot == 0)
memcpy(state->ks_octetL, ks_bytes, sizeof(ks_bytes));
else memcpy(state->ks_octetR, ks_bytes, sizeof(ks_bytes));
//debug ks_bytes
// fprintf (stderr, "KS: ");
// for (int i = 0; i < 126; i++)
// {
// if ((i != 0) && ((i%16)==0))
// fprintf (stderr, "\n ");
// fprintf (stderr, "%02X ", ks_bytes[i]);
// }
// fprintf (stderr, "\n");
}
}
//dsd-fme -fs -Z -i '/SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/better_samples_tg1/kirisun_advanced_tg1.wav' -H 'DC1A7E9F9BF312DB F45010CEC5F7A53A C407D0BFA803617B E426A7254DA9390D'
void kirisun_adv_keystream_creation(dsd_state *state)
{
uint8_t slot = 0;
if (state->currentslot == 0)
slot = 0;
else slot = 1;
uint32_t lfsr = 0;
if (state->currentslot == 0)
lfsr = (uint32_t)state->payload_mi;
else lfsr = (uint32_t)state->payload_miR;
unsigned char userkeyhexa[32]; memset(userkeyhexa, 0, sizeof(userkeyhexa));
unsigned char realkey[32]; memset(realkey, 0, sizeof(realkey));
unsigned char lfsrhexa[4]; memset(lfsrhexa, 0, sizeof(lfsrhexa));
uint8_t ks_bytes[126]; memset(ks_bytes, 0, sizeof(ks_bytes));
lfsrhexa[0] = (lfsr >> 24) & 0xFF;
lfsrhexa[1] = (lfsr >> 16) & 0xFF;
lfsrhexa[2] = (lfsr >> 8) & 0xFF;
lfsrhexa[3] = (lfsr >> 0) & 0xFF;
uint64_t K1 = state->A1[slot];
uint64_t K2 = state->A2[slot];
uint64_t K3 = state->A3[slot];
uint64_t K4 = state->A4[slot];
if (K1 != 0 && K2 != 0 && K3 != 0 && K4 != 0)
state->aes_key_loaded[slot] = 1;
else state->aes_key_loaded[slot] = 0;
if (state->aes_key_loaded[slot] == 1)
{
u64_to_bytes_be(K1, &userkeyhexa[0]);
u64_to_bytes_be(K2, &userkeyhexa[8]);
u64_to_bytes_be(K3, &userkeyhexa[16]);
u64_to_bytes_be(K4, &userkeyhexa[24]);
//init vital portions of PC4Context
ctx.x1 = 0;
ctx.x2 = 0;
for (ctx.i = 0; ctx.i < n1; ctx.i++) ctx.h2[ctx.i] = 0;
for (ctx.i = 0; ctx.i < n1; ctx.i++) ctx.h1[ctx.i] = 0;
ctx.rounds = nbround;
//create keys
create_keys(&ctx, userkeyhexa, 32);
/* --- MD2II first pass --- */
n1global=32;
unsigned char h4[n1]; memset(h4, 0, sizeof(h4));
MD2II_init();
MD2II_hashing(userkeyhexa,32);
MD2II_end(h4);
for(int i=0;i<32;i++) realkey[i]=h4[i];
// printf("=== realkey final (32 bytes) ===\n");
// print_hex(realkey,32);
n1global=32;
memset(h4, 0, sizeof(h4));
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("h4[0..7]: ");
// print_hex(h4,8);
uint64_t internal_state=0;
for(int i=0;i<6;i++)
internal_state=(internal_state<<8)+(h4[i]);
int k = 0;
for (int i = 0; i < 18; i++)
{
//debug internal_state
// fprintf (stderr, "ST: %012lX; \n", internal_state);
ctx.convert[0]=(internal_state>>40)&0xff;
ctx.convert[1]=(internal_state>>32)&0xff;
ctx.convert[2]=(internal_state>>24)&0xff;
ctx.convert[3]=(internal_state>>16)&0xff;
ctx.convert[4]=(internal_state>>8)&0xff;
ctx.convert[5]=internal_state&0xff;
pc4encrypt(&ctx);
internal_state=0;
for (int i=0;i<6;i++)
internal_state=(internal_state<<8)+(ctx.convert[i]&0xff);
internal_state=rol48(internal_state,1);
//keystream
for (int i=0;i<6;i++)
ks_bytes[k++] = ctx.convert[i];
k++; //skip 7th octet
}
//store ks_bytes to storage
if (slot == 0)
memcpy(state->ks_octetL, ks_bytes, sizeof(ks_bytes));
else memcpy(state->ks_octetR, ks_bytes, sizeof(ks_bytes));
//debug ks_bytes
// fprintf (stderr, "KS: ");
// for (int i = 0; i < 126; i++)
// {
// if ((i != 0) && ((i%16)==0))
// fprintf (stderr, "\n ");
// fprintf (stderr, "%02X ", ks_bytes[i]);
// }
// fprintf (stderr, "\n");
}
}

View File

@@ -1,392 +0,0 @@
#include "pc4.h"
/* Global PC4 context instance */
PC4Context ctx;
/* ---------------------------------
Internal utility functions
----------------------------------- */
/* Rotate right */
static uint32_t ror(uint32_t x, int shift, int bits) {
uint32_t m0 = (1u << (bits - shift)) - 1u;
uint32_t m1 = (1u << shift) - 1u;
return ((x >> shift) & m0) | ((x & m1) << (bits - shift));
}
/* Rotate left */
static uint32_t rol(uint32_t x, int shift, int bits) {
uint32_t m0 = (1u << (bits - shift)) - 1u;
uint32_t m1 = (1u << shift) - 1u;
return ((x & m0) << shift) | ((x >> (bits - shift)) & m1);
}
/* SplitMix64 random number generator */
static uint64_t next_rng(PC4Context *ctx) {
uint64_t z = (ctx->x += 0x9e3779b97f4a7c15ULL);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
return z ^ (z >> 31);
}
/* ARC4 initialization */
static void arc4_init(PC4Context *ctx, unsigned char key[]) {
int tmp;
for (ctx->i_arc4 = 0; ctx->i_arc4 < 256; ctx->i_arc4++)
ctx->array_arc4[ctx->i_arc4] = (unsigned char)ctx->i_arc4;
ctx->j_arc4 = 0;
for (ctx->i_arc4 = 0; ctx->i_arc4 < 256; ctx->i_arc4++) {
ctx->j_arc4 = (ctx->j_arc4 + ctx->array_arc4[ctx->i_arc4] + key[ctx->i_arc4 % 256]) % 256;
tmp = ctx->array_arc4[ctx->i_arc4];
ctx->array_arc4[ctx->i_arc4] = ctx->array_arc4[ctx->j_arc4];
ctx->array_arc4[ctx->j_arc4] = tmp;
}
ctx->i_arc4 = 0;
ctx->j_arc4 = 0;
}
/* ARC4 output combined with SplitMix64 stream */
static unsigned char arc4_output(PC4Context *ctx) {
uint8_t rndbyte, decal;
int tmp, t;
ctx->i_arc4 = (ctx->i_arc4 + 1) % 256;
ctx->j_arc4 = (ctx->j_arc4 + ctx->array_arc4[ctx->i_arc4]) % 256;
tmp = ctx->array_arc4[ctx->i_arc4];
ctx->array_arc4[ctx->i_arc4] = ctx->array_arc4[ctx->j_arc4];
ctx->array_arc4[ctx->j_arc4] = tmp;
t = (ctx->array_arc4[ctx->i_arc4] + ctx->array_arc4[ctx->j_arc4]) % 256;
if (ctx->xyz == 0) ctx->bb = next_rng(ctx);
decal = (uint8_t)(56 - (8 * ctx->xyz));
rndbyte = (uint8_t)((ctx->bb >> decal) & 0xffu);
ctx->xyz++;
if (ctx->xyz == 8) ctx->xyz = 0;
if (ctx->count == 0) {
rndbyte = (uint8_t)(rndbyte ^ ctx->array_arc4[t]);
ctx->count = 1;
} else {
rndbyte = (uint8_t)(rndbyte + ctx->array_arc4[t]);
ctx->count = 0;
}
return rndbyte;
}
/* Initialize MD2-II state */
static void md2_init(PC4Context *ctx) {
ctx->x1 = 0;
ctx->x2 = 0;
for (ctx->i = 0; ctx->i < n1; ctx->i++) ctx->h2[ctx->i] = 0;
for (ctx->i = 0; ctx->i < n1; ctx->i++) ctx->h1[ctx->i] = 0;
}
/* MD2-II hashing */
static void md2_hashing(PC4Context *ctx, unsigned char t1[], size_t b6) {
static const unsigned char s4[256] = {
13,199,11,67,237,193,164,77,115,184,141,222,73,38,147,36,150,87,21,104,12,61,156,101,111,145,
119,22,207,35,198,37,171,167,80,30,219,28,213,121,86,29,214,242,6,4,89,162,110,175,19,157,3,
88,234,94,144,118,159,239,100,17,182,173,238,68,16,79,132,54,163,52,9,58,57,55,229,192,170,226,
56,231,187,158,70,224,233,245,26,47,32,44,247,8,251,20,197,185,109,153,204,218,93,178,212,137,84,
174,24,120,130,149,72,180,181,208,255,189,152,18,143,176,60,249,27,227,128,139,243,253,59,123,172,
108,211,96,138,10,215,42,225,40,81,65,90,25,98,126,154,64,124,116,122,5,1,168,83,190,131,191,244,
240,235,177,155,228,125,66,43,201,248,220,129,188,230,62,75,71,78,34,31,216,254,136,91,114,106,46,
217,196,92,151,209,133,51,236,33,252,127,179,69,7,183,105,146,97,39,15,205,112,200,166,223,45,48,
246,186,41,148,140,107,76,85,95,194,142,50,49,134,23,135,169,221,210,203,63,165,82,161,202,53,14,
206,232,103,102,195,117,250,99,0,74,160,241,2,113};
int b1, b2, b3, b4, b5;
b4 = 0;
while (b6) {
for (; b6 && ctx->x2 < n1; b6--, ctx->x2++) {
b5 = t1[b4++];
ctx->h1[ctx->x2 + n1] = (unsigned char)b5;
ctx->h1[ctx->x2 + (n1 * 2)] = (unsigned char)(b5 ^ ctx->h1[ctx->x2]);
ctx->x1 = ctx->h2[ctx->x2] ^= s4[b5 ^ ctx->x1];
}
if (ctx->x2 == n1) {
b2 = 0;
ctx->x2 = 0;
for (b3 = 0; b3 < (n1 + 2); b3++) {
for (b1 = 0; b1 < (n1 * 3); b1++)
b2 = ctx->h1[b1] ^= s4[b2];
b2 = (b2 + b3) % 256;
}
}
}
}
/* Finalize MD2-II */
static void md2_end(PC4Context *ctx, unsigned char h4[n1]) {
unsigned char h3[n1];
int i, n4 = n1 - ctx->x2;
for (i = 0; i < n4; i++) h3[i] = (unsigned char)n4;
md2_hashing(ctx, h3, (size_t)n4);
md2_hashing(ctx, ctx->h2, sizeof(ctx->h2));
for (i = 0; i < n1; i++) h4[i] = ctx->h1[i];
}
/* Generate a random index */
static int mixy(PC4Context *ctx, int nn2) {
return arc4_output(ctx) % nn2;
}
/* Fisher-Yates shuffle */
static void mixer(PC4Context *ctx, uint8_t *mixu, int nn) {
int ii, jj, tmmp;
for (ii = nn - 1; ii > 0; ii--) {
jj = mixy(ctx, ii + 1);
tmmp = mixu[jj];
mixu[jj] = mixu[ii];
mixu[ii] = (uint8_t)tmmp;
}
}
/* Key schedule and S-box generation */
void create_keys(PC4Context *ctx, unsigned char key1[], size_t size1) {
int i, w, k;
unsigned char h4[n1];
md2_init(ctx);
md2_hashing(ctx, key1, size1);
md2_end(ctx, h4);
for (i = 0; i < 16; i++) ctx->keys[i] = h4[i];
arc4_init(ctx, h4);
ctx->x = 0;
for (i = 0; i < 8; i++) ctx->x = (ctx->x << 8) + (uint64_t)(h4[256 + i] & 0xffu);
ctx->xyz = 0;
ctx->count = 0;
for (i = 0; i < 20000; i++) (void)arc4_output(ctx);
uint8_t numbers[256];
for (w = 0; w < 16; w++) {
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 256; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 256);
for (i = 0; i < 256; i++) ctx->perm[w][i] = numbers[i];
}
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 256; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 256);
for (i = 0; i < 256; i++) ctx->new1[i] = numbers[i];
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 49; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 49);
for (i = 0; i < 49; i++) ctx->array[i] = numbers[i];
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < nbround; i++) ctx->decal[i] = (uint8_t)((arc4_output(ctx) % 23) + 1);
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (w = 0; w < 3; w++)
for (i = 0; i < nbround; i++)
ctx->rngxor[i][w] = arc4_output(ctx);
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 49; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 49);
for (i = 0; i < 49; i++) ctx->array2[i] = numbers[i];
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 256; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 256);
for (i = 0; i < 256; i++) {
ctx->tab[i] = numbers[i];
ctx->inv[ctx->tab[i]] = (unsigned char)i;
}
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (w = 0; w < 3; w++) {
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 3; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 3);
for (i = 0; i < 3; i++) ctx->permut[w][i] = numbers[i];
}
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (w = 0; w < 3; w++)
for (i = 0; i < nbround; i++)
ctx->rngxor2[i][w] = arc4_output(ctx);
}
/* Compute round transformation */
static void compute(PC4Context *ctx, uint8_t *tab1, uint8_t round) {
ctx->tot[0] = (uint8_t)((ctx->perm[round][tab1[ctx->permut[0][0]]] +
ctx->perm[round][tab1[ctx->permut[0][1]]]) ^
ctx->perm[round][tab1[ctx->permut[0][2]]]);
ctx->tot[0] = (uint8_t)(ctx->tot[0] + ctx->new1[ctx->tot[0]]);
ctx->tot[1] = (uint8_t)((ctx->perm[round][tab1[ctx->permut[1][0]]] +
ctx->perm[round][tab1[ctx->permut[1][1]]]) ^
ctx->perm[round][tab1[ctx->permut[1][2]]]);
ctx->tot[1] = (uint8_t)(ctx->tot[1] + ctx->new1[ctx->tot[1]]);
ctx->tot[2] = (uint8_t)((ctx->perm[round][tab1[ctx->permut[2][0]]] +
ctx->perm[round][tab1[ctx->permut[2][1]]]) ^
ctx->perm[round][tab1[ctx->permut[2][2]]]);
ctx->tot[2] = (uint8_t)(ctx->tot[2] + ctx->new1[ctx->tot[2]]);
}
/* Convert bits to bytes */
void binhex(PC4Context *ctx, short *z, int length) {
short *b = (short *)z;
uint8_t i, j;
for (i = 0; i < length; i = j) {
uint8_t a = 0;
for (j = i; j < i + 8; ++j) {
a |= (uint8_t)(b[((short)(7 - (j % 8)) + j) - (j % 8)] << (j - i));
}
ctx->convert[ctx->ptconvert] = a;
ctx->ptconvert++;
}
}
/* Convert byte to bits */
void hexbin(PC4Context *ctx, short *q, uint8_t w, uint8_t hex) { // warning: unused parameter ‘ctx’ [-Wunused-parameter]
short *bits = (short *)q;
for (uint8_t i = 0; i < 8; ++i) {
bits[(short)(7 + w) - i] = (short)((hex >> i) & 1u);
}
UNUSEDPC4(ctx); //fix above warning
}
/* Encrypt one block */
void pc4encrypt(PC4Context *ctx) {
int i;
ctx->totb = 0;
for (i = 0; i < 3; i++) {
ctx->l[0][i] = ctx->convert[i];
ctx->r[0][i] = ctx->convert[i + 3];
}
for (i = 1; i <= ctx->rounds; i++) {
ctx->totb ^= ctx->r[(i - 1) % 2][0];
ctx->totb ^= ctx->r[(i - 1) % 2][1];
ctx->totb ^= ctx->r[(i - 1) % 2][2];
ctx->r[(i - 1) % 2][0] += (uint8_t)(~ctx->rngxor2[ctx->rounds - i][0]);
ctx->r[(i - 1) % 2][1] ^= (uint8_t)(~ctx->rngxor2[ctx->rounds - i][1]);
ctx->r[(i - 1) % 2][2] += (uint8_t)(~ctx->rngxor2[ctx->rounds - i][2]);
ctx->result = 0;
ctx->result += ((uint32_t)ctx->r[(i - 1) % 2][0] << 16);
ctx->result += ((uint32_t)ctx->r[(i - 1) % 2][1] << 8);
ctx->result += ctx->r[(i - 1) % 2][2];
ctx->result = rol(ctx->result, ctx->decal[i - 1], 24);
ctx->r[(i - 1) % 2][0] = (uint8_t)(ctx->result >> 16);
ctx->r[(i - 1) % 2][1] = (uint8_t)((ctx->result >> 8) & 0xffu);
ctx->r[(i - 1) % 2][2] = (uint8_t)(ctx->result & 0xffu);
ctx->r[(i - 1) % 2][0] = ctx->tab[ctx->r[(i - 1) % 2][0]];
ctx->r[(i - 1) % 2][0] ^= ctx->rngxor[i - 1][0];
ctx->r[(i - 1) % 2][1] = ctx->inv[ctx->r[(i - 1) % 2][1]];
ctx->r[(i - 1) % 2][1] -= ctx->rngxor[i - 1][1];
ctx->r[(i - 1) % 2][2] = ctx->tab[ctx->r[(i - 1) % 2][2]];
ctx->r[(i - 1) % 2][2] ^= ctx->rngxor[i - 1][2];
compute(ctx, ctx->r[(i - 1) % 2], (uint8_t)((i - 1) % 16));
ctx->l[i % 2][0] = ctx->r[(i - 1) % 2][0];
ctx->r[i % 2][0] = ctx->l[(i - 1) % 2][0] - ctx->tot[0];
ctx->l[i % 2][1] = ctx->r[(i - 1) % 2][1];
ctx->r[i % 2][1] = ctx->l[(i - 1) % 2][1] ^ ctx->tot[1];
ctx->l[i % 2][2] = ctx->r[(i - 1) % 2][2];
ctx->r[i % 2][2] = ctx->l[(i - 1) % 2][2] - ctx->tot[2];
}
for (i = 0; i < 3; i++) {
ctx->convert[i + 3] = ctx->l[(ctx->rounds - 1) % 2][i];
ctx->convert[i] = ctx->r[(ctx->rounds - 1) % 2][i];
}
ctx->totb %= 2;
}
/* Decrypt one block */
void pc4decrypt(PC4Context *ctx) {
int i;
ctx->totb = 0;
for (i = 0; i < 3; i++) {
ctx->l[0][i] = ctx->convert[i];
ctx->r[0][i] = ctx->convert[i + 3];
}
ctx->y = (uint8_t)((ctx->rounds - 1) % 16);
if (ctx->y == 0) ctx->y = 16;
for (i = 1; i <= ctx->rounds; i++) {
ctx->y--;
compute(ctx, ctx->r[(i - 1) % 2], ctx->y);
if (ctx->y == 0) ctx->y = 16;
ctx->result = 0;
ctx->l[(i - 1) % 2][0] ^= ctx->rngxor[ctx->rounds - i][0];
ctx->l[(i - 1) % 2][0] = ctx->inv[ctx->l[(i - 1) % 2][0]];
ctx->l[(i - 1) % 2][1] += ctx->rngxor[ctx->rounds - i][1];
ctx->l[(i - 1) % 2][1] = ctx->tab[ctx->l[(i - 1) % 2][1]];
ctx->l[(i - 1) % 2][2] ^= ctx->rngxor[ctx->rounds - i][2];
ctx->l[(i - 1) % 2][2] = ctx->inv[ctx->l[(i - 1) % 2][2]];
ctx->result += ((uint32_t)ctx->l[(i - 1) % 2][0] << 16);
ctx->result += ((uint32_t)ctx->l[(i - 1) % 2][1] << 8);
ctx->result += ctx->l[(i - 1) % 2][2];
ctx->result = ror(ctx->result, ctx->decal[ctx->rounds - i], 24);
ctx->l[(i - 1) % 2][0] = (uint8_t)(ctx->result >> 16);
ctx->l[(i - 1) % 2][1] = (uint8_t)((ctx->result >> 8) & 0xffu);
ctx->l[(i - 1) % 2][2] = (uint8_t)(ctx->result & 0xffu);
ctx->l[(i - 1) % 2][0] -= (uint8_t)(~ctx->rngxor2[i - 1][0]);
ctx->l[(i - 1) % 2][1] ^= (uint8_t)(~ctx->rngxor2[i - 1][1]);
ctx->l[(i - 1) % 2][2] -= (uint8_t)(~ctx->rngxor2[i - 1][2]);
ctx->totb ^= ctx->l[(i - 1) % 2][0];
ctx->totb ^= ctx->l[(i - 1) % 2][1];
ctx->totb ^= ctx->l[(i - 1) % 2][2];
ctx->l[i % 2][0] = ctx->r[(i - 1) % 2][0];
ctx->r[i % 2][0] = ctx->l[(i - 1) % 2][0] + ctx->tot[0];
ctx->l[i % 2][1] = ctx->r[(i - 1) % 2][1];
ctx->r[i % 2][1] = ctx->l[(i - 1) % 2][1] ^ ctx->tot[1];
ctx->l[i % 2][2] = ctx->r[(i - 1) % 2][2];
ctx->r[i % 2][2] = ctx->l[(i - 1) % 2][2] + ctx->tot[2];
}
for (i = 0; i < 3; i++) {
ctx->convert[i + 3] = ctx->l[(ctx->rounds - 1) % 2][i];
ctx->convert[i] = ctx->r[(ctx->rounds - 1) % 2][i];
}
ctx->totb %= 2;
}

View File

@@ -1,468 +0,0 @@
#include "pc5.h"
#include "dsd.h"
#include <ctype.h>
/* Global PC5 context instance */
PC5Context ctxpc5;
/* ---------------------------------
Internal utility functions
----------------------------------- */
/* Rotate right */
static uint32_t ror(uint32_t x, int shift, int bits) {
uint32_t m0 = (1u << (bits - shift)) - 1u;
uint32_t m1 = (1u << shift) - 1u;
return ((x >> shift) & m0) | ((x & m1) << (bits - shift));
}
/* Rotate left */
static uint32_t rol(uint32_t x, int shift, int bits) {
uint32_t m0 = (1u << (bits - shift)) - 1u;
uint32_t m1 = (1u << shift) - 1u;
return ((x & m0) << shift) | ((x >> (bits - shift)) & m1);
}
/* SplitMix64 random number generator */
static uint64_t next_rng(PC5Context *ctxpc5) {
uint64_t z = (ctxpc5->x += 0x9e3779b97f4a7c15ULL);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
return z ^ (z >> 31);
}
/* ARC4 initialization */
static void arc4_init(PC5Context *ctxpc5, unsigned char key[]) {
int tmp;
for (ctxpc5->i_arc4 = 0; ctxpc5->i_arc4 < 256; ctxpc5->i_arc4++)
ctxpc5->array_arc4[ctxpc5->i_arc4] = (unsigned char)ctxpc5->i_arc4;
ctxpc5->j_arc4 = 0;
for (ctxpc5->i_arc4 = 0; ctxpc5->i_arc4 < 256; ctxpc5->i_arc4++) {
ctxpc5->j_arc4 = (ctxpc5->j_arc4 + ctxpc5->array_arc4[ctxpc5->i_arc4] + key[ctxpc5->i_arc4 % 256]) % 256;
tmp = ctxpc5->array_arc4[ctxpc5->i_arc4];
ctxpc5->array_arc4[ctxpc5->i_arc4] = ctxpc5->array_arc4[ctxpc5->j_arc4];
ctxpc5->array_arc4[ctxpc5->j_arc4] = tmp;
}
ctxpc5->i_arc4 = 0;
ctxpc5->j_arc4 = 0;
}
/* ARC4 output combined with SplitMix64 stream */
static unsigned char arc4_output(PC5Context *ctxpc5) {
uint8_t rndbyte, decal;
int tmp, t;
ctxpc5->i_arc4 = (ctxpc5->i_arc4 + 1) % 256;
ctxpc5->j_arc4 = (ctxpc5->j_arc4 + ctxpc5->array_arc4[ctxpc5->i_arc4]) % 256;
tmp = ctxpc5->array_arc4[ctxpc5->i_arc4];
ctxpc5->array_arc4[ctxpc5->i_arc4] = ctxpc5->array_arc4[ctxpc5->j_arc4];
ctxpc5->array_arc4[ctxpc5->j_arc4] = tmp;
t = (ctxpc5->array_arc4[ctxpc5->i_arc4] + ctxpc5->array_arc4[ctxpc5->j_arc4]) % 256;
if (ctxpc5->xyz == 0) ctxpc5->bb = next_rng(ctxpc5);
decal = (uint8_t)(56 - (8 * ctxpc5->xyz));
rndbyte = (uint8_t)((ctxpc5->bb >> decal) & 0xffu);
ctxpc5->xyz++;
if (ctxpc5->xyz == 8) ctxpc5->xyz = 0;
if (ctxpc5->count == 0) {
rndbyte = (uint8_t)(rndbyte ^ ctxpc5->array_arc4[t]);
ctxpc5->count = 1;
} else {
rndbyte = (uint8_t)(rndbyte + ctxpc5->array_arc4[t]);
ctxpc5->count = 0;
}
return rndbyte;
}
/* Initialize MD2-II state */
static void md2_init(PC5Context *ctxpc5) {
ctxpc5->x1 = 0;
ctxpc5->x2 = 0;
for (ctxpc5->i = 0; ctxpc5->i < n1; ctxpc5->i++) ctxpc5->h2[ctxpc5->i] = 0;
for (ctxpc5->i = 0; ctxpc5->i < n1; ctxpc5->i++) ctxpc5->h1[ctxpc5->i] = 0;
}
/* MD2-II hashing */
static void md2_hashing(PC5Context *ctxpc5, unsigned char t1[], size_t b6) {
static const unsigned char s4[256] = {
13,199,11,67,237,193,164,77,115,184,141,222,73,38,147,36,150,87,21,104,12,61,156,101,111,145,
119,22,207,35,198,37,171,167,80,30,219,28,213,121,86,29,214,242,6,4,89,162,110,175,19,157,3,
88,234,94,144,118,159,239,100,17,182,173,238,68,16,79,132,54,163,52,9,58,57,55,229,192,170,226,
56,231,187,158,70,224,233,245,26,47,32,44,247,8,251,20,197,185,109,153,204,218,93,178,212,137,84,
174,24,120,130,149,72,180,181,208,255,189,152,18,143,176,60,249,27,227,128,139,243,253,59,123,172,
108,211,96,138,10,215,42,225,40,81,65,90,25,98,126,154,64,124,116,122,5,1,168,83,190,131,191,244,
240,235,177,155,228,125,66,43,201,248,220,129,188,230,62,75,71,78,34,31,216,254,136,91,114,106,46,
217,196,92,151,209,133,51,236,33,252,127,179,69,7,183,105,146,97,39,15,205,112,200,166,223,45,48,
246,186,41,148,140,107,76,85,95,194,142,50,49,134,23,135,169,221,210,203,63,165,82,161,202,53,14,
206,232,103,102,195,117,250,99,0,74,160,241,2,113};
int b1, b2, b3, b4, b5;
b4 = 0;
while (b6) {
for (; b6 && ctxpc5->x2 < n1; b6--, ctxpc5->x2++) {
b5 = t1[b4++];
ctxpc5->h1[ctxpc5->x2 + n1] = (unsigned char)b5;
ctxpc5->h1[ctxpc5->x2 + (n1 * 2)] = (unsigned char)(b5 ^ ctxpc5->h1[ctxpc5->x2]);
ctxpc5->x1 = ctxpc5->h2[ctxpc5->x2] ^= s4[b5 ^ ctxpc5->x1];
}
if (ctxpc5->x2 == n1) {
b2 = 0;
ctxpc5->x2 = 0;
for (b3 = 0; b3 < (n1 + 2); b3++) {
for (b1 = 0; b1 < (n1 * 3); b1++)
b2 = ctxpc5->h1[b1] ^= s4[b2];
b2 = (b2 + b3) % 256;
}
}
}
}
/* Finalize MD2-II */
static void md2_end(PC5Context *ctxpc5, unsigned char h4[n1]) {
unsigned char h3[n1];
int i, n4 = n1 - ctxpc5->x2;
for (i = 0; i < n4; i++) h3[i] = (unsigned char)n4;
md2_hashing(ctxpc5, h3, (size_t)n4);
md2_hashing(ctxpc5, ctxpc5->h2, sizeof(ctxpc5->h2));
for (i = 0; i < n1; i++) h4[i] = ctxpc5->h1[i];
}
/* Generate a random index */
static int mixy(PC5Context *ctxpc5, int nn2) {
return arc4_output(ctxpc5) % nn2;
}
/* Fisher-Yates shuffle */
static void mixer(PC5Context *ctxpc5, uint8_t *mixu, int nn) {
int ii, jj, tmmp;
for (ii = nn - 1; ii > 0; ii--) {
jj = mixy(ctxpc5, ii + 1);
tmmp = mixu[jj];
mixu[jj] = mixu[ii];
mixu[ii] = (uint8_t)tmmp;
}
}
/* Key schedule and S-box generation */
void create_keys_pc5(PC5Context *ctxpc5, unsigned char key1[], size_t size1) {
int i, w, k;
unsigned char h4[n1];
md2_init(ctxpc5);
md2_hashing(ctxpc5, key1, size1);
md2_end(ctxpc5, h4);
for (i = 0; i < 16; i++) ctxpc5->keys[i] = h4[i];
arc4_init(ctxpc5, h4);
ctxpc5->x = 0;
for (i = 0; i < 8; i++) ctxpc5->x = (ctxpc5->x << 8) + (uint64_t)(h4[256 + i] & 0xffu);
ctxpc5->xyz = 0;
ctxpc5->count = 0;
for (i = 0; i < 23000; i++) (void)arc4_output(ctxpc5);
uint8_t numbers[256];
for (w = 0; w < 253; w++) {
k = arc4_output(ctxpc5) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i;
mixer(ctxpc5, numbers, 16);
for (i = 0; i < 16; i++) ctxpc5->perm[i][w] = numbers[i];
}
k = arc4_output(ctxpc5) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i;
mixer(ctxpc5, numbers, 16);
for (i = 0; i < 16; i++) ctxpc5->new1[i] = numbers[i];
k = arc4_output(ctxpc5) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
for (i = 0; i < nbround; i++) ctxpc5->decal[i] = (uint8_t)((arc4_output(ctxpc5) % 11) + 1);
k = arc4_output(ctxpc5) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
for (w = 0; w < 3; w++)
for (i = 0; i < nbround; i++)
ctxpc5->rngxor[i][w] = arc4_output(ctxpc5) % 16;
k = arc4_output(ctxpc5) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
for (i = 0; i < 16; i++) numbers[i] = (uint8_t)i;
mixer(ctxpc5, numbers, 16);
for (i = 0; i < 16; i++) {
ctxpc5->tab[i] = numbers[i];
ctxpc5->inv[ctxpc5->tab[i]] = (unsigned char)i;
}
k = arc4_output(ctxpc5) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
for (w = 0; w < 3; w++) {
k = arc4_output(ctxpc5) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
for (i = 0; i < 3; i++) numbers[i] = (uint8_t)i;
mixer(ctxpc5, numbers, 3);
for (i = 0; i < 3; i++) ctxpc5->permut[w][i] = numbers[i];
}
k = arc4_output(ctxpc5) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctxpc5);
for (w = 0; w < 3; w++)
for (i = 0; i < nbround; i++)
ctxpc5->rngxor2[i][w] = arc4_output(ctxpc5) % 16;
k = arc4_output(ctxpc5) + 256;
for (w = 0; w < 25; w++) ctxpc5->numbers[w]=arc4_output(ctxpc5) % 2;
}
static void compute_pc5(PC5Context *ctxpc5, uint8_t *tab1, uint8_t round) {
ctxpc5->tot[0] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[0][0]]][round] +
ctxpc5->perm[tab1[ctxpc5->permut[0][1]]][round]) ^
ctxpc5->perm[tab1[ctxpc5->permut[0][2]]][round]);
ctxpc5->tot[0] = (uint8_t)((ctxpc5->tot[0] + ctxpc5->new1[ctxpc5->tot[0]]) % 16);
ctxpc5->tot[1] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[1][0]]][round] +
ctxpc5->perm[tab1[ctxpc5->permut[1][1]]][round]) ^
ctxpc5->perm[tab1[ctxpc5->permut[1][2]]][round]);
ctxpc5->tot[1] = (uint8_t)((ctxpc5->tot[1] + ctxpc5->new1[ctxpc5->tot[1]]) % 16);
ctxpc5->tot[2] = (uint8_t)((ctxpc5->perm[tab1[ctxpc5->permut[2][0]]][round] +
ctxpc5->perm[tab1[ctxpc5->permut[2][1]]][round]) ^
ctxpc5->perm[tab1[ctxpc5->permut[2][2]]][round]);
ctxpc5->tot[2] = (uint8_t)((ctxpc5->tot[2] + ctxpc5->new1[ctxpc5->tot[2]]) % 16);
}
/* Convert bits to bytes */
void binhexpc5(PC5Context *ctxpc5, short *z, int length) {
short *b = (short *)z;
uint8_t i, j;
for (i = 0; i < length; i = j) {
uint8_t a = 0;
for (j = i; j < i + 8; ++j) {
a |= (uint8_t)(b[((short)(7 - (j % 8)) + j) - (j % 8)] << (j - i));
}
ctxpc5->convert[ctxpc5->ptconvert] = a;
ctxpc5->ptconvert++;
}
}
/* Convert byte to bits */
void hexbinpc5(PC5Context *ctxpc5, short *q, uint8_t w, uint8_t hex) { // warning: unused parameter ‘ctxpc5’ [-Wunused-parameter]
UNUSED(ctxpc5);
short *bits = (short *)q;
for (uint8_t i = 0; i < 8; ++i) {
bits[(short)(7 + w) - i] = (short)((hex >> i) & 1u);
}
// UNUSEDPC4(ctxpc5); //fix above warning
}
/* Encrypt one block */
void pc5encrypt(PC5Context *ctxpc5) {
int i;
int g;
for (i = 0; i < 3; i++) {
ctxpc5->l[0][i] = ctxpc5->convert[i];
ctxpc5->r[0][i] = ctxpc5->convert[i + 3];
}
for (i = 1; i <= ctxpc5->rounds; i++) {
ctxpc5->r[(i - 1) % 2][0] = (ctxpc5->r[(i - 1) % 2][0] + ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][0])) % 16;
ctxpc5->r[(i - 1) % 2][1] = (ctxpc5->r[(i - 1) % 2][1] ^ ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][1])) % 16;
ctxpc5->r[(i - 1) % 2][2] = (ctxpc5->r[(i - 1) % 2][2] + ((uint8_t)~ctxpc5->rngxor2[ctxpc5->rounds - i][2])) % 16;
ctxpc5->result = 0;
ctxpc5->result += ((uint32_t)ctxpc5->r[(i - 1) % 2][0] << 8);
ctxpc5->result += ((uint32_t)ctxpc5->r[(i - 1) % 2][1] << 4);
ctxpc5->result += ctxpc5->r[(i - 1) % 2][2];
ctxpc5->result = rol(ctxpc5->result, ctxpc5->decal[i - 1], 12);
ctxpc5->r[(i - 1) % 2][0] = (uint8_t)(ctxpc5->result >> 8);
ctxpc5->r[(i - 1) % 2][1] = (uint8_t)((ctxpc5->result >> 4) & 0xfu);
ctxpc5->r[(i - 1) % 2][2] = (uint8_t)(ctxpc5->result & 0xfu);
ctxpc5->r[(i - 1) % 2][0] = ctxpc5->tab[ctxpc5->r[(i - 1) % 2][0]];
ctxpc5->r[(i - 1) % 2][0] = (ctxpc5->r[(i - 1) % 2][0] ^ ctxpc5->rngxor[i - 1][0]) % 16;
ctxpc5->r[(i - 1) % 2][1] = ctxpc5->inv[ctxpc5->r[(i - 1) % 2][1]];
if ((ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) < 0) {
g = (ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) + 16;
} else {
g = (ctxpc5->r[(i - 1) % 2][1] - ctxpc5->rngxor[i - 1][1]) % 16;
}
ctxpc5->r[(i - 1) % 2][1] = g;
ctxpc5->r[(i - 1) % 2][2] = ctxpc5->tab[ctxpc5->r[(i - 1) % 2][2]];
ctxpc5->r[(i - 1) % 2][2] = (ctxpc5->r[(i - 1) % 2][2] ^ ctxpc5->rngxor[i - 1][2]) % 16;
compute_pc5(ctxpc5, ctxpc5->r[(i - 1) % 2], (uint8_t)((i - 1) % 253));
ctxpc5->l[i % 2][0] = ctxpc5->r[(i - 1) % 2][0];
if ((ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0]) < 0) {
g = (ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0]) + 16;
} else {
g = ctxpc5->l[(i - 1) % 2][0] - ctxpc5->tot[0];
}
ctxpc5->r[i % 2][0] = g;
ctxpc5->l[i % 2][1] = ctxpc5->r[(i - 1) % 2][1];
ctxpc5->r[i % 2][1] = (ctxpc5->l[(i - 1) % 2][1] ^ ctxpc5->tot[1]) % 16;
ctxpc5->l[i % 2][2] = ctxpc5->r[(i - 1) % 2][2];
if ((ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2]) < 0) {
g = (ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2]) + 16;
} else {
g = ctxpc5->l[(i - 1) % 2][2] - ctxpc5->tot[2];
}
ctxpc5->r[i % 2][2] = g;
}
for (i = 0; i < 3; i++) {
ctxpc5->convert[i + 3] = ctxpc5->l[(ctxpc5->rounds - 1) % 2][i];
ctxpc5->convert[i] = ctxpc5->r[(ctxpc5->rounds - 1) % 2][i];
}
}
/* Decrypt one block */
void pc5decrypt(PC5Context *ctxpc5) {
int i;
for (i = 0; i < 3; i++) {
ctxpc5->l[0][i] = ctxpc5->convert[i];
ctxpc5->r[0][i] = ctxpc5->convert[i + 3];
}
ctxpc5->y = (uint8_t)((ctxpc5->rounds - 1) % 253);
if (ctxpc5->y == 0) ctxpc5->y = 253;
for (i = 1; i <= ctxpc5->rounds; i++) {
ctxpc5->y--;
compute_pc5(ctxpc5, ctxpc5->r[(i - 1) % 2], ctxpc5->y);
if (ctxpc5->y == 0) ctxpc5->y = 253;
ctxpc5->result = 0;
ctxpc5->l[(i - 1) % 2][0] ^= ctxpc5->rngxor[ctxpc5->rounds - i][0];
ctxpc5->l[(i - 1) % 2][0] %= 16;
ctxpc5->l[(i - 1) % 2][0] = ctxpc5->inv[ctxpc5->l[(i - 1) % 2][0]];
ctxpc5->l[(i - 1) % 2][1] += ctxpc5->rngxor[ctxpc5->rounds - i][1];
ctxpc5->l[(i - 1) % 2][1] %= 16;
ctxpc5->l[(i - 1) % 2][1] = ctxpc5->tab[ctxpc5->l[(i - 1) % 2][1]];
ctxpc5->l[(i - 1) % 2][2] ^= ctxpc5->rngxor[ctxpc5->rounds - i][2];
ctxpc5->l[(i - 1) % 2][2] %= 16;
ctxpc5->l[(i - 1) % 2][2] = ctxpc5->inv[ctxpc5->l[(i - 1) % 2][2]];
ctxpc5->result += ((uint32_t)ctxpc5->l[(i - 1) % 2][0] << 8);
ctxpc5->result += ((uint32_t)ctxpc5->l[(i - 1) % 2][1] << 4);
ctxpc5->result += ctxpc5->l[(i - 1) % 2][2];
ctxpc5->result = ror(ctxpc5->result, ctxpc5->decal[ctxpc5->rounds - i], 12);
ctxpc5->l[(i - 1) % 2][0] = (uint8_t)(ctxpc5->result >> 8);
ctxpc5->l[(i - 1) % 2][1] = (uint8_t)((ctxpc5->result >> 4) & 0xfu);
ctxpc5->l[(i - 1) % 2][2] = (uint8_t)(ctxpc5->result & 0xfu);
int temp;
temp = ctxpc5->l[(i - 1) % 2][0] - ((uint8_t)(~ctxpc5->rngxor2[i - 1][0]) % 16);
if (temp < 0) temp += 16;
ctxpc5->l[(i - 1) % 2][0] = temp % 16;
ctxpc5->l[(i - 1) % 2][1] ^= (uint8_t)(~ctxpc5->rngxor2[i - 1][1]) % 16;
ctxpc5->l[(i - 1) % 2][1] %= 16;
temp = ctxpc5->l[(i - 1) % 2][2] - ((uint8_t)(~ctxpc5->rngxor2[i - 1][2]) % 16);
if (temp < 0) temp += 16;
ctxpc5->l[(i - 1) % 2][2] = temp % 16;
ctxpc5->l[i % 2][0] = ctxpc5->r[(i - 1) % 2][0];
ctxpc5->r[i % 2][0] = (ctxpc5->l[(i - 1) % 2][0] + ctxpc5->tot[0]) % 16;
ctxpc5->l[i % 2][1] = ctxpc5->r[(i - 1) % 2][1];
ctxpc5->r[i % 2][1] = (ctxpc5->l[(i - 1) % 2][1] ^ ctxpc5->tot[1]) % 16;
ctxpc5->l[i % 2][2] = ctxpc5->r[(i - 1) % 2][2];
ctxpc5->r[i % 2][2] = (ctxpc5->l[(i - 1) % 2][2] + ctxpc5->tot[2]) % 16;
}
for (i = 0; i < 3; i++) {
ctxpc5->convert[i + 3] = ctxpc5->l[(ctxpc5->rounds - 1) % 2][i];
ctxpc5->convert[i] = ctxpc5->r[(ctxpc5->rounds - 1) % 2][i];
}
}
/* Key creation for BAOFENG AP */
void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input) {
char buf[1024];
strncpy(buf, input, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
// Count hexadecimal characters (without spaces)
int hex_char_count = 0;
for (int i = 0; buf[i] != '\0'; i++) {
if (isxdigit(buf[i])) {
hex_char_count++;
}
}
if (hex_char_count == 32) { // 128 bits (32 hex characters)
unsigned char key1[16] = {0};
unsigned char key2[16] = {0};
char *pEnd;
uint64_t K1 = strtoull(buf, &pEnd, 16);
uint64_t K2 = strtoull(pEnd, &pEnd, 16);
u64_to_bytes_be_pc5(K1, &key1[0]);
u64_to_bytes_be_pc5(K2, &key1[8]);
// Reverse key for PC5 (only for 128-bit)
for (int i=0;i<16;i++) key2[i] = key1[15-i];
/* Create key schedule */
create_keys_pc5(&ctxpc5, key2, sizeof(key2));
ctxpc5.rounds = nbround;
fprintf(stderr, "DMR BAOFENG AP (PC5) 128-bit Key %016llX%016llX with Forced Application\n",
(unsigned long long)K1, (unsigned long long)K2);
state->baofeng_ap = 1;
} else if (hex_char_count == 64) { // 256 bits (64 hex characters)
// Remove spaces and store as ASCII string (64 bytes)
char ascii_key[65] = {0}; // 64 characters + null terminator
int j = 0;
for (int i = 0; buf[i] != '\0' && j < 64; i++) {
if (isxdigit(buf[i])) {
ascii_key[j++] = buf[i];
}
}
ascii_key[64] = '\0';
/* Create key schedule */
create_keys_pc5(&ctxpc5, (unsigned char*)ascii_key, 64);
ctxpc5.rounds = nbround;
fprintf(stderr, "DMR BAOFENG AP (PC5) 256-bit Key: %s with Forced Application\n", ascii_key);
state->baofeng_ap = 1;
} else {
fprintf(stderr, "ERROR: Invalid key length. Expected 32 chars (128-bit) or 64 chars (256-bit), got %d hex chars\n", hex_char_count);
return;
}
}

View File

@@ -1,404 +0,0 @@
#include "rc2.h"
#include "dsd.h"
static inline uint64_t rol64(uint64_t x, int n) {
return ((x << n) | (x >> (63 - n) >> 1)) & 0xffffffffffffffff;
}
void swapbit(uint64_t* internalstate, uint8_t bit) {
unsigned char bitB = bit & 1;
if (bitB)
*internalstate |= bitB;
else
*internalstate &= (~bitB ^ 1);
}
// MD2 functions
void md2_init(MD2State* state) {
state->x1 = 0;
state->x2 = 0;
memset(state->h2, 0, n1);
memset(state->h1, 0, n1 * 3);
}
void md2_hashing(MD2State* state, unsigned char t1[], size_t b6) {
static unsigned char s4[256] = {
13,199,11,67,237,193,164,77,115,184,141,222,73,
38,147,36,150,87,21,104,12,61,156,101,111,145,
119,22,207,35,198,37,171,167,80,30,219,28,213,
121,86,29,214,242,6,4,89,162,110,175,19,157,
3,88,234,94,144,118,159,239,100,17,182,173,238,
68,16,79,132,54,163,52,9,58,57,55,229,192,
170,226,56,231,187,158,70,224,233,245,26,47,32,
44,247,8,251,20,197,185,109,153,204,218,93,178,
212,137,84,174,24,120,130,149,72,180,181,208,255,
189,152,18,143,176,60,249,27,227,128,139,243,253,
59,123,172,108,211,96,138,10,215,42,225,40,81,
65,90,25,98,126,154,64,124,116,122,5,1,168,
83,190,131,191,244,240,235,177,155,228,125,66,43,
201,248,220,129,188,230,62,75,71,78,34,31,216,
254,136,91,114,106,46,217,196,92,151,209,133,51,
236,33,252,127,179,69,7,183,105,146,97,39,15,
205,112,200,166,223,45,48,246,186,41,148,140,107,
76,85,95,194,142,50,49,134,23,135,169,221,210,
203,63,165,82,161,202,53,14,206,232,103,102,195,
117,250,99,0,74,160,241,2,113
};
int b4 = 0;
while (b6) {
for (; b6 && state->x2 < n1; b6--, state->x2++) {
int b5 = t1[b4++];
state->h1[state->x2 + n1] = b5;
state->h1[state->x2 + (n1 * 2)] = b5 ^ state->h1[state->x2];
state->x1 = state->h2[state->x2] ^= s4[b5 ^ state->x1];
}
if (state->x2 == n1) {
int b2 = 0;
state->x2 = 0;
for (int b3 = 0; b3 < (n1 + 2); b3++) {
for (int b1 = 0; b1 < (n1 * 3); b1++)
b2 = state->h1[b1] ^= s4[b2];
b2 = (b2 + b3) % 256;
}
}
}
}
void md2_end(MD2State* state, unsigned char h4[n1]) {
unsigned char h3[n1];
int n4 = n1 - state->x2;
for (int i = 0; i < n4; i++) h3[i] = n4;
md2_hashing(state, h3, n4);
md2_hashing(state, state->h2, sizeof(state->h2));
for (int i = 0; i < n1; i++) h4[i] = state->h1[i];
}
// RC4 functions
uint64_t next(RC4State* state) {
uint64_t z = (state->x += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
void rc4_init(RC4State* state, unsigned char key[]) {
int tmp;
for (state->i_rc4 = 0; state->i_rc4 < 256; state->i_rc4++) {
state->array_rc4[state->i_rc4] = state->i_rc4;
}
state->j_rc4 = 0;
for (state->i_rc4 = 0; state->i_rc4 < 256; state->i_rc4++) {
state->j_rc4 = (state->j_rc4 + state->array_rc4[state->i_rc4] + key[state->i_rc4 % 256]) % 256;
tmp = state->array_rc4[state->i_rc4];
state->array_rc4[state->i_rc4] = state->array_rc4[state->j_rc4];
state->array_rc4[state->j_rc4] = tmp;
}
state->i_rc4 = 0;
state->j_rc4 = 0;
}
unsigned char rc4_output(RC4State* state) {
uint8_t rndbyte, decal;
int tmp, t;
state->i_rc4 = (state->i_rc4 + 1) % 256;
state->j_rc4 = (state->j_rc4 + state->array_rc4[state->i_rc4]) % 256;
tmp = state->array_rc4[state->i_rc4];
state->array_rc4[state->i_rc4] = state->array_rc4[state->j_rc4];
state->array_rc4[state->j_rc4] = tmp;
t = (state->array_rc4[state->i_rc4] + state->array_rc4[state->j_rc4]) % 256;
if (state->xyz == 0) state->bb = next(state);
decal = 56 - (8 * state->xyz);
rndbyte = (state->bb >> decal) & 0xff;
state->xyz++;
if (state->xyz == 8) state->xyz = 0;
if (state->count == 0) {
rndbyte = rndbyte ^ state->array_rc4[t];
state->count = 1;
} else {
rndbyte = rndbyte + state->array_rc4[t];
state->count = 0;
}
return rndbyte;
}
/* Convert 64-bit integer to bytes (big-endian) */
static void u64_to_bytes_be(uint64_t val, unsigned char *out) {
for (int i = 0; i < 8; i++) {
out[i] = (unsigned char)((val >> (56 - 8 * i)) & 0xFF);
}
}
// RC2 functions
void rc2_keyschedule(RC2State* state) {
unsigned i;
// Phase 3 - copy to xkey in little-endian order
i = 63;
do {
state->xkey[i] = ((unsigned char *)state->xkey)[2 * i] +
(((unsigned char *)state->xkey)[2 * i + 1] << 8);
} while (i--);
}
void rc2_encrypt(RC2State* state) {
uint16_t x76, x54, x32, x10, i;
x76 = (state->plain[7] << 8) + state->plain[6];
x54 = (state->plain[5] << 8) + state->plain[4];
x32 = (state->plain[3] << 8) + state->plain[2];
x10 = (state->plain[1] << 8) + state->plain[0];
for (i = 0; i < 16; i++) {
x10 += (x32 & ~x76) + (x54 & x76) + state->xkey[4 * i + 0];
x10 = (x10 << 1) + (x10 >> 15 & 1);
x32 += (x54 & ~x10) + (x76 & x10) + state->xkey[4 * i + 1];
x32 = (x32 << 2) + (x32 >> 14 & 3);
x54 += (x76 & ~x32) + (x10 & x32) + state->xkey[4 * i + 2];
x54 = (x54 << 3) + (x54 >> 13 & 7);
x76 += (x10 & ~x54) + (x32 & x54) + state->xkey[4 * i + 3];
x76 = (x76 << 5) + (x76 >> 11 & 31);
if (i == 4 || i == 10) {
x10 += state->xkey[x76 & 63];
x32 += state->xkey[x10 & 63];
x54 += state->xkey[x32 & 63];
x76 += state->xkey[x54 & 63];
}
}
state->cipher[0] = (unsigned char)x10;
state->cipher[1] = (unsigned char)(x10 >> 8);
state->cipher[2] = (unsigned char)x32;
state->cipher[3] = (unsigned char)(x32 >> 8);
state->cipher[4] = (unsigned char)x54;
state->cipher[5] = (unsigned char)(x54 >> 8);
state->cipher[6] = (unsigned char)x76;
state->cipher[7] = (unsigned char)(x76 >> 8);
}
// Main cryptographic functions
void create_keys_rc2(CryptoContext* ctx, unsigned char key1[], size_t size1) {
unsigned char h4[n1];
// Initialize MD2 and hash the key
md2_init(&ctx->md2);
md2_hashing(&ctx->md2, key1, size1);
md2_end(&ctx->md2, h4);
// Copy first 16 bytes to keys
for (int i = 0; i < 16; i++) ctx->keys[i] = h4[i];
// Initialize RC4 with hashed key
rc4_init(&ctx->rc4, h4);
// Initialize RC4 state variables
ctx->rc4.x = 0;
for (int i = 0; i < 8; i++) ctx->rc4.x = (ctx->rc4.x << 8) + (h4[256 + i] & 0xff);
ctx->rc4.xyz = 0;
ctx->rc4.count = 0;
// Warm-up RC4
for (int i = 0; i < 22000; i++) rc4_output(&ctx->rc4);
// Generate RC2 keys
int k = rc4_output(&ctx->rc4) + 256;
for (int i = 0; i < k; i++) rc4_output(&ctx->rc4);
for (int i = 0; i < 64; i++) {
ctx->rc2.xkey[i] = (rc4_output(&ctx->rc4) << 8) + rc4_output(&ctx->rc4);
}
// Generate internal zero value
k = rc4_output(&ctx->rc4) + 256;
for (int i = 0; i < k; i++) rc4_output(&ctx->rc4);
ctx->internal_zero = 0;
for (int i = 0; i < 8; i++) {
ctx->internal_zero = (ctx->internal_zero << 8) + rc4_output(&ctx->rc4);
}
// Generate RC2 key schedule
rc2_keyschedule(&ctx->rc2);
}
void encryption_rc2(CryptoContext* ctx, uint8_t bits[49]) {
ctx->internal_state = ctx->internal_zero;
for (int sso = 0; sso < 49; sso++) {
// Prepare plaintext from internal state
ctx->rc2.plain[0] = (ctx->internal_state >> 56) & 0xff;
ctx->rc2.plain[1] = (ctx->internal_state >> 48) & 0xff;
ctx->rc2.plain[2] = (ctx->internal_state >> 40) & 0xff;
ctx->rc2.plain[3] = (ctx->internal_state >> 32) & 0xff;
ctx->rc2.plain[4] = (ctx->internal_state >> 24) & 0xff;
ctx->rc2.plain[5] = (ctx->internal_state >> 16) & 0xff;
ctx->rc2.plain[6] = (ctx->internal_state >> 8) & 0xff;
ctx->rc2.plain[7] = ctx->internal_state & 0xff;
// Encrypt with RC2
rc2_encrypt(&ctx->rc2);
// Reconstruct internal state from ciphertext
ctx->internal_state = 0;
for (int i = 0; i < 8; i++) {
ctx->internal_state = (ctx->internal_state << 8) + (ctx->rc2.cipher[i] & 0xff);
}
// XOR the bit and update internal state
bits[48 - sso] = bits[48 - sso] ^ (ctx->internal_state & 1);
ctx->internal_state = rol64(ctx->internal_state, 1);
swapbit(&ctx->internal_state, bits[48 - sso]);
}
}
void decrypt_rc2(CryptoContext* ctx, uint8_t bits[49]) {
uint8_t tempy;
ctx->internal_state = ctx->internal_zero;
for (int sso = 0; sso < 49; sso++) {
// Prepare plaintext from internal state
ctx->rc2.plain[0] = (ctx->internal_state >> 56) & 0xff;
ctx->rc2.plain[1] = (ctx->internal_state >> 48) & 0xff;
ctx->rc2.plain[2] = (ctx->internal_state >> 40) & 0xff;
ctx->rc2.plain[3] = (ctx->internal_state >> 32) & 0xff;
ctx->rc2.plain[4] = (ctx->internal_state >> 24) & 0xff;
ctx->rc2.plain[5] = (ctx->internal_state >> 16) & 0xff;
ctx->rc2.plain[6] = (ctx->internal_state >> 8) & 0xff;
ctx->rc2.plain[7] = ctx->internal_state & 0xff;
// Encrypt with RC2
rc2_encrypt(&ctx->rc2);
// Reconstruct internal state from ciphertext
ctx->internal_state = 0;
for (int i = 0; i < 8; i++) {
ctx->internal_state = (ctx->internal_state << 8) + (ctx->rc2.cipher[i] & 0xff);
}
// XOR the bit and update internal state
tempy = bits[48 - sso];
bits[48 - sso] = bits[48 - sso] ^ (ctx->internal_state & 1);
ctx->internal_state = rol64(ctx->internal_state, 1);
swapbit(&ctx->internal_state, tempy);
}
}
/* Key creation for Retevis AP */
void retevis_rc2_keystream_creation(dsd_state *state, char *input) {
unsigned char key1[64]; memset(key1, 0, sizeof(key1));
unsigned char key2[64]; memset(key2, 0, sizeof(key2));
char buf[1024];
strncpy(buf, input, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
char *pEnd;
uint64_t K1 = strtoull(buf, &pEnd, 16);
uint64_t K2 = strtoull(pEnd, &pEnd, 16);
uint64_t K3 = strtoull(pEnd, &pEnd, 16);
uint64_t K4 = strtoull(pEnd, &pEnd, 16);
if (K3 != 0 || K4 != 0) //256-bit keys loaded as ASCII characters
{
//start loading K1, K2, K3, and K4 as ASCII to fill 64 bytes of key1
int k = 0; uint8_t x = 0;
for (int i = 0; i < 16; i++)
{
x = ((K1 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
for (int i = 0; i < 16; i++)
{
x = ((K2 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
for (int i = 0; i < 16; i++)
{
x = ((K3 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
for (int i = 0; i < 16; i++)
{
x = ((K4 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
//debug
// fprintf (stderr, "ASCII: ");
// for (int i = 0; i < 64; i++)
// fprintf (stderr, " %02X", key1[i]);
// fprintf (stderr, "\n");
// Initialize RC2 context
static CryptoContext rc2_ctx;
create_keys_rc2(&rc2_ctx, key1, 64);
// Store context in DSD state
state->rc2_context = malloc(sizeof(CryptoContext));
memcpy(state->rc2_context, &rc2_ctx, sizeof(CryptoContext));
fprintf(stderr, "DMR RETEVIS AP (RC2) 256-bit Key %016llX%016llX%016llX%016llX with Forced Application\n",
(unsigned long long)K1, (unsigned long long)K2, (unsigned long long)K3, (unsigned long long)K4);
state->retevis_ap = 1;
}
else //128-bit loaded as reverse bytes
{
u64_to_bytes_be(K1, &key1[0]);
u64_to_bytes_be(K2, &key1[8]);
// reverse load the key bytes into key2
for (int i=0;i<16;i++)
key2[i] = key1[15-i];
// Initialize RC2 context
static CryptoContext rc2_ctx;
create_keys_rc2(&rc2_ctx, key2, 16);
// Store context in DSD state
state->rc2_context = malloc(sizeof(CryptoContext));
memcpy(state->rc2_context, &rc2_ctx, sizeof(CryptoContext));
fprintf(stderr, "DMR RETEVIS AP (RC2) 128-bit Key %016llX%016llX with Forced Application\n",
(unsigned long long)K1, (unsigned long long)K2);
state->retevis_ap = 1;
}
}

View File

@@ -1,146 +0,0 @@
/*-------------------------------------------------------------------------------
* rc4.c Crypthings
* RC4 Alg
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//this version is for voice, going to transition to a block output version
void rc4_voice_decrypt(int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[])
{
int i, j, count;
uint8_t t, b;
//init Sbox
uint8_t S[256];
for(int i = 0; i < 256; i++) S[i] = i;
//Key Scheduling
j = 0;
for(i = 0; i < 256; i++)
{
j = (j + S[i] + key[i % keylength]) % 256;
t = S[i];
S[i] = S[j];
S[j] = t;
}
//Drop Bytes and Cipher Byte XOR
i = j = 0;
for(count = 0; count < (messagelength + drop); count++)
{
i = (i + 1) % 256;
j = (j + S[i]) % 256;
t = S[i];
S[i] = S[j];
S[j] = t;
b = S[(S[i] + S[j]) % 256];
//return mbe payload byte here
if (count >= drop)
plain[count - drop] = b^cipher[count - drop];
}
}
//this is for PDU usage
void rc4_block_output (int drop, int keylen, int meslen, uint8_t * key, uint8_t * output_blocks)
{
int i, j, x, count;
unsigned int keylength = (unsigned int)keylen;
unsigned int messagelength = (unsigned int)meslen;
unsigned int S[256];
for(i=0; i<256; i++)
S[i] = i;
j = 0;
for(i = 0; i<256; i++)
{
j = (j + S[i] + key[i % keylength]) % 256;
unsigned int temp = S[i];
S[i] = S[j];
S[j] = temp;
}
//Generate Keystream
i = 0;
j = 0;
x = 0;
unsigned int byte;
// fprintf (stderr, " Keystream Octets = ");
for(count = 0; count < (messagelength + drop); count++)
{
i = (i + 1) % 256;
j = (j + S[i]) % 256;
unsigned int temp = S[i];
S[i] = S[j];
S[j] = temp;
byte = S[(S[i] + S[j]) % 256];
//Collect Output blocks
if (count >= drop)
output_blocks[x++] = byte;
}
}
//This is now verified to work after changing the drop byte value from 256 to 0.
//also, had to change the application to not skip the additional 7 bits like DMRA or P25 does.
void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value)
{
UNUSED(opts);
uint8_t key[5]; memset (key, 0, sizeof(key));
uint8_t kiv[5]; memset (kiv, 0, sizeof(kiv));
uint8_t mi[5]; memset (mi, 0, sizeof(mi));
uint8_t ks[135]; memset (ks, 0, sizeof(ks));
//load key_value into key array
key[0] = ((key_value & 0xFF00000000) >> 32UL);
key[1] = ((key_value & 0xFF000000) >> 24);
key[2] = ((key_value & 0xFF0000) >> 16);
key[3] = ((key_value & 0xFF00) >> 8);
key[4] = ((key_value & 0xFF) >> 0);
//load mi_value into mi array
mi[0] = ((mi_value & 0xFF00000000) >> 32UL);
mi[1] = ((mi_value & 0xFF000000) >> 24);
mi[2] = ((mi_value & 0xFF0000) >> 16);
mi[3] = ((mi_value & 0xFF00) >> 8);
mi[4] = ((mi_value & 0xFF) >> 0);
//pointer to the ks_octet storage
uint8_t * ks_octets;
if (state->currentslot == 0)
ks_octets = state->ks_octetL;
else ks_octets = state->ks_octetR;
//NOTE: Drop Byte value is 0
rc4_block_output(0, 5, 135, key, ks);
for (int i = 0; i < 5; i++)
kiv[i] = key[i] ^ mi[i];
for (int i = 0; i < 135; i++)
ks_octets[i] = kiv[i%5] ^ ks[i];
//debug
// fprintf (stderr, " KS: ");
// for (int i = 0; i < 135; i++)
// {
// if ((i != 0) && ((i%7) == 0))
// fprintf (stderr, " ");
// fprintf (stderr, "%02X", ks[i]); //ks_octets
// }
//NULL pointer to ks_octets
ks_octets = NULL;
//end line break
// fprintf (stderr, "\n");
}

View File

@@ -1,361 +0,0 @@
#include "dsd.h"
#include "dmr_const.h"
#include "pc4.h"
//interleaved code words for AMBE+2 (as it arrives over the air)
void ambe2_codeword_print_i (dsd_opts * opts, char ambe_fr[4][24])
{
uint8_t interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//reinterleave the frame
const int *w, *x, *y, *z;
w = rW; x = rX; y = rY; z = rZ;
for (int8_t i = 0; i < 36; i++)
{
interleaved[(i*2)+0] = (uint8_t)ambe_fr[*w][*x];
interleaved[(i*2)+1] = (uint8_t)ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
uint8_t bytes[9]; memset(bytes, 0, sizeof(bytes));
//pack
pack_bit_array_into_byte_array(interleaved, bytes, 9);
if (opts->payload == 1)
{
fprintf (stderr, " AMBE HEX(72) INT: ");
for (int8_t i = 0; i < 9; i++)
fprintf (stderr, "%02X", bytes[i]);
fprintf (stderr, "\n");
}
}
//de-interleaved code words for AMBE+2
void ambe2_codeword_print_b (dsd_opts * opts, char ambe_fr[4][24])
{
uint8_t fr_reverse[4][24]; memset(fr_reverse, 0, sizeof(fr_reverse));
for (int i = 0; i < 24; i++)
fr_reverse[0][i] = ambe_fr[0][23-i];
for (int i = 0; i < 23; i++)
fr_reverse[1][i] = ambe_fr[1][22-i];
for (int i = 0; i < 11; i++)
fr_reverse[2][i] = ambe_fr[2][10-i];
for (int i = 0; i < 14; i++)
fr_reverse[3][i] = ambe_fr[3][13-i];
uint32_t v0 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[0], 24); //24
uint32_t v1 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[1], 23); //23
uint32_t v2 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[2], 11); //11
uint32_t v3 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[3], 14); //14
uint32_t c0 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[0], 12);
uint32_t c1 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[1], 12);
//72 bit version
unsigned long long int hex1 = ((unsigned long long int)v0 << 40ULL) + ((unsigned long long int)v1 << 17ULL) + ((unsigned long long int)v2 << 6ULL) + (v3 >> 8);
unsigned long long int hex2 = v3 & 0xFF;
//49 bit version prior to golay correction and c1 demodulation pN
unsigned long long int hex49 = ((unsigned long long int)c0 << 37ULL) + ((unsigned long long int)c1 << 25ULL) + ((unsigned long long int)v2 << 14ULL) + v3;
UNUSED(opts);
if (opts->payload == 1)
{
fprintf (stderr, " AMBE HEX(72): %016llX%02llX \n", hex1, hex2);
fprintf (stderr, " AMBE HEX(49): %014llX\n", hex49 << 7);
}
}
//de-interleaved code words for AMBE+2
void ambe2_codeword_print_f (dsd_opts * opts, char ambe_fr[4][24])
{
uint32_t v0 = (uint32_t)convert_bits_into_output((uint8_t *)ambe_fr[0], 24); //24
uint32_t v1 = (uint32_t)convert_bits_into_output((uint8_t *)ambe_fr[1], 23); //23
uint32_t v2 = (uint32_t)convert_bits_into_output((uint8_t *)ambe_fr[2], 11); //11
uint32_t v3 = (uint32_t)convert_bits_into_output((uint8_t *)ambe_fr[3], 14); //14
// if (opts->payload == 1)
// fprintf (stderr, " AMBE V0: %06X; V1: %06X; V2: %03X; V3: %04X; \n", v0, v1, v2, v3);
unsigned long long int hex1 = ((unsigned long long int)v0 << 40ULL) + ((unsigned long long int)v1 << 17ULL) + ((unsigned long long int)v2 << 6ULL) + (v3 >> 8);
unsigned long long int hex2 = v3 & 0xFF;
if (opts->payload == 1)
fprintf (stderr, " AMBE HEX(72): %016llX%02llX \n", hex1, hex2);
}
//NOTE: This mode DOES NOT work over a repeater, simplex only
//repeaters may or will attempt to correct the frame errors
void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int fnum)
{
char interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//interleave the frame
const int *w, *x, *y, *z;
w = rW; x = rX; y = rY; z = rZ;
for (int8_t i = 0; i < 36; i++)
{
interleaved[(i*2)+0] = ambe_fr[*w][*x];
interleaved[(i*2)+1] = ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
uint8_t ks_bytes[10]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t ks[80]; memset(ks, 0, sizeof(ks));
ks_bytes[0] = (state->H >> 8) & 0xFF;
ks_bytes[1] = (state->H >> 0) & 0xFF;
//copy same bytes into rest of byte array
for (int16_t i = 2; i < 10; i++)
ks_bytes[i] = ks_bytes[i%2];
//convert byte array into a bit array
unpack_byte_array_into_bit_array(ks_bytes, ks, 10);
//set ks idx position (-1)
int idx = 0;
if (fnum == 0)
idx = 79;
else idx = 71;
//apply keystream to interleave
for (int8_t i = 0; i < 72; i++)
interleaved[i] ^= ks[idx--];
//deinterleave back into ambe_fr frame
w = rW; x = rX; y = rY; z = rZ;
int k = 0;
for (int8_t i = 0; i < 36; i++)
{
ambe_fr[*w][*x] = interleaved[k++];
ambe_fr[*y][*z] = interleaved[k++];
w++;
x++;
y++;
z++;
}
}
void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input)
{
unsigned char key1[64]; memset(key1, 0, sizeof(key1));
unsigned char key2[64]; memset(key2, 0, sizeof(key2));
char buf[1024];
strncpy(buf, input, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
char *pEnd;
uint64_t K1 = strtoull(buf, &pEnd, 16);
uint64_t K2 = strtoull(pEnd, &pEnd, 16);
uint64_t K3 = strtoull(pEnd, &pEnd, 16);
uint64_t K4 = strtoull(pEnd, &pEnd, 16);
if (K3 != 0 || K4 != 0) //256-bit keys loaded as ASCII characters
{
//start loading K1, K2, K3, and K4 as ASCII to fill 64 bytes of key1
int k = 0; uint8_t x = 0;
for (int i = 0; i < 16; i++)
{
x = ((K1 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
for (int i = 0; i < 16; i++)
{
x = ((K2 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
for (int i = 0; i < 16; i++)
{
x = ((K3 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
for (int i = 0; i < 16; i++)
{
x = ((K4 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
//debug
// fprintf (stderr, "ASCII: ");
// for (int i = 0; i < 64; i++)
// fprintf (stderr, " %02X", key1[i]);
// fprintf (stderr, "\n");
/* Create key schedule */
create_keys(&ctx, key1, 64);
ctx.rounds = nbround;
fprintf(stderr, "DMR TYT AP (PC4) 256-bit Key %016llX%016llX%016llX%016llX with Forced Application\n",
(unsigned long long)K1, (unsigned long long)K2, (unsigned long long)K3, (unsigned long long)K4);
state->tyt_ap = 1;
}
else //128-bit loaded as reverse bytes
{
u64_to_bytes_be(K1, &key1[0]);
u64_to_bytes_be(K2, &key1[8]);
// reverse load the key bytes into key2
for (int i=0;i<16;i++)
key2[i] = key1[15-i];
/* Create key schedule */
create_keys(&ctx, key2, 16);
ctx.rounds = nbround;
fprintf (stderr,"DMR TYT AP (PC4) 128-bit Key %016llX%016llX with Forced Application\n",
(unsigned long long int)K1, (unsigned long long int)K2);
state->tyt_ap = 1;
}
}
void tyt_ep_aes_keystream_creation(dsd_state * state, char * input)
{
char buf[1024];
strncpy(buf, input, 1023);
buf[1023] = '\0';
char *pEnd;
unsigned long long int K1 = strtoull (buf, &pEnd, 16);
unsigned long long int K2 = strtoull (pEnd, &pEnd, 16);
//
uint8_t static_key[32];
memset(static_key, 0, sizeof(static_key));
//static key value
static_key[0]=0x6e; static_key[1]=0x02; static_key[2]=0x8d; static_key[3]=0x8a;
static_key[4]=0xca; static_key[5]=0xeb; static_key[6]=0x9b; static_key[7]=0xbe;
static_key[8]=0x42; static_key[9]=0x72; static_key[10]=0xfb; static_key[11]=0x82;
static_key[12]=0x64; static_key[13]=0x56; static_key[14]=0x31; static_key[15]=0xfa;
//the key value provided by user
uint8_t user_key[16];
memset(user_key, 0, sizeof(user_key));
//Load user key into array to manipulate
for (int i = 0; i < 8; i++)
{
user_key[i+0] = (K1 >> (56-(i*8))) & 0xFF;
user_key[i+8] = (K2 >> (56-(i*8))) & 0xFF;
}
uint8_t input_register[16];
memset(input_register, 0, sizeof(input_register));
//manipulate user provided key by loading bytes in reverse order into the input_register
for (int i = 0; i < 16; i++)
input_register[15-i] = user_key[i];
uint8_t ks_bytes[16];
memset(ks_bytes, 0, sizeof(ks_bytes));
//create keystream
aes_ofb_keystream_output(input_register, static_key, ks_bytes, 0, 1);
uint8_t ks_bits[128];
memset(ks_bits, 0, sizeof(ks_bits));
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, 16);
//load static keystream into ctx.bits since that isn't ever zeroed out
for (int i = 0; i < 49; i++)
ctx.bits[i] = ks_bits[i];
fprintf (stderr,"DMR TYT EP (AES-128) Key %016llX%016llX with Forced Application\n", K1, K2);
state->tyt_ep = 1;
}
//connect systems 72-bit (9-byte) Extended Encryption //TODO: Move this later
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24])
{
char interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//interleave the frame
const int *w, *x, *y, *z;
w = rW; x = rX; y = rY; z = rZ;
for (int8_t i = 0; i < 36; i++)
{
interleaved[(i*2)+0] = ambe_fr[*w][*x];
interleaved[(i*2)+1] = ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
uint8_t ks_bytes[9]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t ks[72]; memset(ks, 0, sizeof(ks));
//keys are loaded in reverse byte order, stored in state->static_ks_bits[0]
for (int i = 0; i < 9; i++)
ks_bytes[i] = state->static_ks_bits[0][8-i];
//convert byte array into a bit array
unpack_byte_array_into_bit_array(ks_bytes, ks, 9);
//apply keystream to interleave
for (int8_t i = 0; i < 72; i++)
interleaved[i] ^= ks[71-i];
//deinterleave back into ambe_fr frame
w = rW; x = rX; y = rY; z = rZ;
int k = 0;
for (int8_t i = 0; i < 36; i++)
{
ambe_fr[*w][*x] = interleaved[k++];
ambe_fr[*y][*z] = interleaved[k++];
w++;
x++;
y++;
z++;
}
}

View File

@@ -1,214 +0,0 @@
/*-------------------------------------------------------------------------------
* dmr_34.c
* DMR (and P25) 3/4 Rate Simple Trellis Decoder
*
* LWVMOBILE
* 2023-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
uint8_t interleave[98] = {
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
//this is a convertion table for converting the dibit pairs into constellation points
uint8_t constellation_map[16] = {
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
};
//digitized dibit to OTA symbol conversion for reference
//0 = +1; 1 = +3;
//2 = -1; 3 = -3;
//finite state machine values
uint8_t fsm[64] = {
0, 8, 4, 12, 2, 10, 6, 14,
4, 12, 2, 10, 6, 14, 0, 8,
1, 9, 5, 13, 3, 11, 7, 15,
5, 13, 3, 11, 7, 15, 1, 9,
3, 11, 7, 15, 1, 9, 5, 13,
7, 15, 1, 9, 5, 13, 3, 11,
2, 10, 6, 14, 0, 8, 4, 12,
6, 14, 0, 8, 4, 12, 2, 10};
//attempt to find the surviving path, or the 'best' path available (most positions gained)
uint8_t fix_34(uint8_t * p, uint8_t state, int position)
{
int i, j, k, counter, best_p, best_v, survivors;
uint8_t temp_s, tri, t;
//status of surviving paths -- debug
int s[8];
memset (s, 0, 8*sizeof(int));
//assign all potentially correct points to temporary storage
uint8_t temp_p[8];
temp_p[0] = (p[position] ^ 1) & 0xF;
temp_p[1] = (p[position] ^ 3) & 0xF;
temp_p[2] = (p[position] ^ 5) & 0xF;
temp_p[3] = (p[position] ^ 7) & 0xF;
temp_p[4] = (p[position] ^ 9) & 0xF;
temp_p[5] = (p[position] ^ 11) & 0xF;
temp_p[6] = (p[position] ^ 13) & 0xF;
temp_p[7] = (p[position] ^ 15) & 0xF;
best_p = 0; //best position
best_v = 0; //best path value
for (k = 0; k < 8; k++)
{
temp_s = state;
counter = 0;
tri = 0;
for (i = position; i < 49; i++)
{
//assign temp t either as our temp_p, or next point of i
if (i == position) t = temp_p[k];
else t = p[i];
if (tri != 0xFF) //while the path survives
{
tri = 0xFF;
for (j = 0; j < 8; j++)
{
if ( fsm[(temp_s*8)+j] == t )
{
//return our tribit value and state for the next point
tri = temp_s = (uint8_t)j;
counter++;
break;
}
}
if (counter > best_p)
{
best_p = counter;
best_v = k; //if we make it further on current path, assign as best path
}
// surviving path -- made it to 49
if (i == 48) s[k] = 1;
}
}
}
//NOTE: If there are no surviving paths, then that means there is another bit/point err
//Ideally, at the end of a single or multiple fixes, there should only be one survivor, but in practice,
//I've seen up to 4 surviving paths and somehow, get a good CRC and a good LRRP or LOCN decode
//multiple survivors seems common when the starting err position is very close to 49
//tally number of surviving paths
survivors = 0;
for (k = 0; k < 8; k++)
{
if (s[k] == 1) survivors++;
}
//debug
// fprintf (stderr, "START: %d; BEST_P: %d; BEST_V: %d; Survivors: %d; Point: %d; ", position, best_p, best_v, survivors, temp_p[best_v]);
return temp_p[best_v]; //return the point value of the best path's starting point value
}
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
{
int i, j;
uint32_t irr_err = 0; //irrecoverable errors
uint8_t deinterleaved_dibits[98];
memset (deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
//deinterleave our input dibits
for (i = 0; i < 98; i++)
deinterleaved_dibits[interleave[i]] = input[i];
//pack the input into nibbles (dibit pairs)
uint8_t nibs[49];
memset (nibs, 0, sizeof(nibs));
for (i = 0; i < 49; i++)
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
//convert our dibit pairs into constellation point values
uint8_t point[49];
memset (point, 0xFF, sizeof(point));
for (i = 0; i < 49; i++)
point[i] = constellation_map[nibs[i]];
//debug view points
// fprintf (stderr, "\n P =");
// for (i = 0; i < 49; i++)
// fprintf (stderr, " %02d", point[i]);
//convert constellation points into tribit values using the FSM
uint8_t state = 0;
uint32_t tribits[49];
memset (tribits, 0xF, sizeof(tribits));
for (i = 0; i < 49; i++)
{
for (j = 0; j < 8; j++)
{
if ( fsm[(state*8)+j] == point[i] )
{
//return our tribit value and state for the next point
tribits[i] = state = (uint8_t)j;
break;
}
}
//if tribit value is greater than 7, then we have a decoding error
if (tribits[i] > 7)
{
irr_err++; //tally number of errors
//debug point, position of error, and state value
// fprintf (stderr, "\n P: %d, %d:%d; ", point[i], i, state);
//attempt to fix point by looking for the best path from here
point[i] = fix_34(point, state, i);
//Make a hard decision and flip point to fit in current state
// point[i] ^= 7; //lucky number 7 (0111) //fallback
//decrement one and resume decoding
i--;
}
}
//debug view tribits/states
// fprintf (stderr, "\n T =");
// for (i = 0; i < 49; i++)
// fprintf (stderr, " %02d", tribits[i]);
//convert tribits into a return payload
uint32_t temp = 0;
//break into chunks of 24 bit values and shuffle into 8-bit (byte) treturn values
for (i = 0; i < 6; i++)
{
temp = (tribits[(i*8)+0] << 21) + (tribits[(i*8)+1] << 18) + (tribits[(i*8)+2] << 15) + (tribits[(i*8)+3] << 12) +
(tribits[(i*8)+4] << 9) + (tribits[(i*8)+5] << 6) + (tribits[(i*8)+6] << 3) + (tribits[(i*8)+7] << 0);
treturn[(i*3)+0] = (temp >> 16) & 0xFF;
treturn[(i*3)+1] = (temp >> 8) & 0xFF;
treturn[(i*3)+2] = (temp >> 0) & 0xFF;
}
//trellis point/state err tally
// if (irr_err != 0)
// fprintf (stderr, " P_ERR = %d", irr_err);
return (0);
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,15 +9,13 @@
#include "dsd.h" #include "dsd.h"
#include "dmr_const.h" #include "dmr_const.h"
// #define PRINT_AMBE72 //enable to view 72-bit AMBE codewords
//A subroutine for processing each TDMA frame individually to allow for //A subroutine for processing each TDMA frame individually to allow for
//processing voice and/or data on both BS slots (channels) simultaneously //processing voice and/or data on both BS slots (channels) simultaneously
void dmrBS (dsd_opts * opts, dsd_state * state) void dmrBS (dsd_opts * opts, dsd_state * state)
{ {
char * timestr = NULL; int i, j, k, l, dibit;
int *dibit_p;
int i, dibit;
char ambe_fr[4][24]; char ambe_fr[4][24];
char ambe_fr2[4][24]; char ambe_fr2[4][24];
char ambe_fr3[4][24]; char ambe_fr3[4][24];
@@ -27,22 +25,18 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
char redundancyB[36]; char redundancyB[36];
//memcpy of ambe_fr for late entry //memcpy of ambe_fr for late entry
uint8_t m1[4][24]; char m1[4][24];
uint8_t m2[4][24]; char m2[4][24];
uint8_t m3[4][24]; char m3[4][24];
const int *w, *x, *y, *z; const int *w, *x, *y, *z;
char sync[25]; char sync[25];
uint8_t syncdata[48]; char syncdata[48];
memset (syncdata, 0, sizeof(syncdata)); char EmbeddedSignalling[16];
uint8_t emb_pdu[16];
memset (emb_pdu, 0, sizeof(emb_pdu));
uint8_t emb_ok = 0; uint8_t emb_ok = 0;
uint8_t tact_okay = 0; uint8_t tact_okay = 0;
uint8_t cach_err = 0; uint8_t cach_err = 0;
UNUSED(cach_err);
uint8_t internalslot; uint8_t internalslot;
uint8_t vc1; uint8_t vc1;
@@ -52,11 +46,27 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
uint8_t cc = 25; uint8_t cc = 25;
uint8_t power = 9; //power and pre-emption indicator uint8_t power = 9; //power and pre-emption indicator
uint8_t lcss = 9; uint8_t lcss = 9;
UNUSED2(cc, lcss);
//would be ideal to grab all dibits and break them into bits to pass to new data handler? //would be ideal to grab all dibits and break them into bits to pass to new data handler?
uint8_t dummy_bits[196]; uint8_t dummy_bits[196];
memset (dummy_bits, 0, sizeof(dummy_bits)); memset (dummy_bits, 0, sizeof(dummy_bits));
//add time to mirror printFrameSync
time_t now;
char * getTime(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;
}
//Init slot lights //Init slot lights
sprintf (state->slot1light, " slot1 "); sprintf (state->slot1light, " slot1 ");
@@ -65,30 +75,20 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
//Init the color code status //Init the color code status
state->color_code_ok = 0; state->color_code_ok = 0;
//if coming from the bootsrap, then the slot will still be assigned the last value vc1 = 2;
//we want to set only that vc value to 2, the other to 7 vc2 = 2;
if (state->currentslot == 0)
{
vc1 = 2;
vc2 = 7;
}
if (state->currentslot == 1)
{
vc1 = 7;
vc2 = 2;
}
short int loop = 1; short int loop = 1;
short int skipcount = 0; short int skipcount = 0;
//cach //cach
uint8_t cachdata[25]; char cachdata[25];
int cachInterleave[24] = int cachInterleave[24] =
{0, 7, 8, 9, 1, 10, {0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14, 11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18, 15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23 19, 5, 20, 21, 22, 6, 23
}; };
//cach tact bits //cach tact bits
uint8_t tact_bits[7]; uint8_t tact_bits[7];
@@ -96,19 +96,6 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
//Run Loop while the getting is good //Run Loop while the getting is good
while (loop == 1) { while (loop == 1) {
if (exitflag == 1)
{
cleanupAndExit (opts, state);
}
timestr = getTimeC();
memset (ambe_fr, 0, sizeof(ambe_fr));
memset (ambe_fr2, 0, sizeof(ambe_fr2));
memset (ambe_fr3, 0, sizeof(ambe_fr3));
memset (emb_pdu, 0, sizeof(emb_pdu));
memset (syncdata, 0, sizeof(syncdata));
internalslot = -1; //reset here so we know if this value is being set properly internalslot = -1; //reset here so we know if this value is being set properly
for(i = 0; i < 12; i++) for(i = 0; i < 12; i++)
{ {
@@ -127,7 +114,7 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
//disabled for dmr_cach testing //disabled for dmr_cach testing
tact_okay = 0; tact_okay = 0;
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1; if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
if (tact_okay != 1) goto END; if (tact_okay != 1) goto END; //when going to end for bad tact or emb, consider using getdibit or skipdibit to 'stash' dibits and get better return on next sync pattern? in data particularly
internalslot = state->currentslot = tact_bits[1]; internalslot = state->currentslot = tact_bits[1];
@@ -196,19 +183,19 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
{ {
dibit = getDibit(opts, state); dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+66] = dibit; state->dmr_stereo_payload[i+66] = dibit;
sync[i] = (dibit | 1) + 48; //Sync Burst String as ones and threes sync[i] = (dibit | 1) + 48; //how does this work again?
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
//embedded link control //embedded link control
if(internalslot == 0 && vc1 > 1 && vc1 < 7) //grab on vc1 values 2-5 B C D E, and F if(internalslot == 0 && vc1 > 1 && vc1 < 7) //grab on vc1 values 2-5 B C D and E (F for Single/RC)
{ {
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1 state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0 state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
} }
if(internalslot == 1 && vc2 > 1 && vc2 < 7) //grab on vc2 values 2-5 B C D E, and F if(internalslot == 1 && vc2 > 1 && vc2 < 7) //grab on vc2 values 2-5 B C D and E (F for Single/RC)
{ {
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1 state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0 state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
@@ -217,8 +204,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
} }
sync[24] = 0; sync[24] = 0;
for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i]; for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40]; for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling //Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++) for(i = 0; i < 18; i++)
@@ -267,8 +254,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
//check for sync pattern here after collected the rest of the payload, decide what to do with it //check for sync pattern here after collected the rest of the payload, decide what to do with it
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 ) if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
{ {
fprintf (stderr,"%s ", timestr); fprintf (stderr,"%s ", getTime());
if (internalslot == 0) if (internalslot == 0)
{ {
if (opts->inverted_dmr == 0) if (opts->inverted_dmr == 0)
@@ -276,11 +263,11 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
fprintf (stderr,"Sync: +DMR "); fprintf (stderr,"Sync: +DMR ");
} }
else fprintf (stderr,"Sync: -DMR "); else fprintf (stderr,"Sync: -DMR ");
vc1 = 7; //set to 7 so we can see that we should not be on a VC unless a framesync comes in for it first vc1 = 1;
//close MBEout file - slot 1 //close MBEout file - slot 1
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state); if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
} }
if (internalslot == 1) if (internalslot == 1)
{ {
@@ -290,7 +277,7 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
} }
else fprintf (stderr,"Sync: -DMR "); else fprintf (stderr,"Sync: -DMR ");
vc2 = 7; //set to 7 so we can see that we should not be on a VC unless a framesync comes in for it first vc2 = 1;
//close MBEout file - slot 2 //close MBEout file - slot 2
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state); if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
@@ -301,138 +288,26 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
goto SKIP; goto SKIP;
} }
//ETSI TS 102 361-1 V2.6.1 Table E.2 and E.4 //only play voice on no data sync
//check for a rc burst with bptc or 34 rate data in it (testing only) if (strcmp (sync, DMR_BS_DATA_SYNC) != 0) //we already have a tact ecc check, so we won't get here without that, see if there is any other eccs we can run just to make sure
// #define RC_TESTING //disable if not in use
#ifdef RC_TESTING
//skip the vc counter, just look at the QR and P/Pi if not voice or data sync pattern
if ( (strcmp (sync, DMR_BS_DATA_SYNC) != 0) && (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) )
{ {
//Golay_20_8_decode FEC for the burst type (SlotType) //check the embedded signalling, if bad at this point, we probably aren't quite in sync
unsigned char SlotType[20]; if(QR_16_7_6_decode(EmbeddedSignalling)) emb_ok = 1;
memset (SlotType, 0, sizeof(SlotType));
uint8_t k = 61;
for (uint8_t i = 0; i < 5; i++)
{
SlotType[(i*2)+0] = (state->dmr_stereo_payload[k+0] >> 1) & 1;
SlotType[(i*2)+1] = (state->dmr_stereo_payload[k++] >> 0) & 1;
}
k = 90;
for (uint8_t i = 0; i < 5; i++)
{
SlotType[(i*2)+10] = (state->dmr_stereo_payload[k+0] >> 1) & 1;
SlotType[(i*2)+11] = (state->dmr_stereo_payload[k++] >> 0) & 1;
}
//if the QR FEC is good, tact/cach FEC is good, and slot type burst FEC is good, and the P/PI bit is on for RC
//NOTE: This can still trigger on bad signal when it should be a data sync pattern but signal drops out or
//occassionally on p_clear with trunking tuner logic active and partial stale dibits in the buffer
if (QR_16_7_6_decode(emb_pdu) && emb_pdu[4] && tact_okay == 1) //during voice test
{
if (Golay_20_8_decode(SlotType))
{
fprintf (stderr,"%s ", timestr);
if (opts->inverted_dmr == 0)
fprintf (stderr,"Sync: +RC ");
else fprintf (stderr,"Sync: -RC ");
dmr_data_sync (opts, state);
}
for (i = 0; i < 48; i++)
state->dmr_embedded_signalling[internalslot][5][i] = syncdata[i];
dmr_sbrc (opts, state, emb_pdu[4]);
emb_ok = 1;
//give an audio cue when this happens (low, high)
beeper (opts, state, internalslot, 40, 86, 3);
beeper (opts, state, internalslot, 80, 86, 3);
//put into Event History
state->event_history_s[0].Event_History_Items[internalslot].color_pair = 4;
watchdog_event_datacall (opts, state, 0, 0, "DMR Reverse Channel P/PI Indicator On (FEC Okay);", internalslot);
push_event_history (&state->event_history_s[internalslot]);
init_event_history (&state->event_history_s[internalslot], 0, 1);
if (Golay_20_8_decode(SlotType))
{
skipcount++;
goto SKIP;
}
}
}
#endif //RC_TESTING
//check to see if we are expecting a VC at this point vc > 7
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0 && internalslot == 0 && vc1 > 6)
{
fprintf (stderr,"%s ", timestr);
//simplifying things
// char polarity[3];
char light[18];
sprintf (light, "%s", " [SLOT1] slot2 ");
fprintf (stderr,"Sync: DMR %s", light);
fprintf (stderr, "%s", KCYN);
fprintf (stderr, "| Frame Sync Err: %d", vc1);
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
vc1++;
//this should give it enough time to find the next frame sync pattern, if it exists, if not, then trigger a resync
if ( vc1 > 13 ) goto END;
else goto SKIP;
}
//check to see if we are expecting a VC at this point vc > 7
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0 && internalslot == 1 && vc2 > 6)
{
fprintf (stderr,"%s ", timestr);
//simplifying things
// char polarity[3];
char light[18];
sprintf (light, "%s", " slot1 [SLOT2] ");
fprintf (stderr,"Sync: DMR %s", light);
fprintf (stderr, "%s", KCYN);
fprintf (stderr, "| Frame Sync Err: %d", vc2);
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
vc2++;
//this should give it enough time to find the next frame sync pattern, if it exists, if not, then trigger a resync
if ( vc2 > 13 ) goto END;
else goto SKIP;
}
//only play voice on no data sync, and VC values are within expected values 1-6
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0) //we already have a tact ecc check, so we won't get here without that, see if there is any other eccs we can run just to make sure
{
//check the embedded signalling, if bad at this point, we probably aren't quite in sync
if(QR_16_7_6_decode(emb_pdu)) emb_ok = 1;
else emb_ok = 0; else emb_ok = 0;
//disable the goto END; if this causes more problems than fixing on late entry dual voices i.e., lots of forced resyncs //disable the goto END; if this causes more problems than fixing on late entry dual voices i.e., lots of forced resyncs
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? "); if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
else if (emb_ok == 1) else if (emb_ok == 1)
{ {
cc = ((emb_pdu[0] << 3) + (emb_pdu[1] << 2) + (emb_pdu[2] << 1) + emb_pdu[3]); cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
power = emb_pdu[4]; power = EmbeddedSignalling[4];
lcss = ((emb_pdu[5] << 1) + emb_pdu[6]); lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
state->dmr_color_code = state->color_code = cc;
} }
skipcount = 0; //reset skip count if processing voice frames skipcount = 0; //reset skip count if processing voice frames
fprintf (stderr,"%s ", timestr); fprintf (stderr,"%s ", getTime());
//simplifying things //simplifying things
char polarity[3]; char polarity[3];
@@ -445,7 +320,7 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
sprintf (light, "%s", " [SLOT1] slot2 "); sprintf (light, "%s", " [SLOT1] slot2 ");
//open MBEout file - slot 1 //open MBEout file - slot 1
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state); if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
} }
else else
{ {
state->dmrburstR = 16; state->dmrburstR = 16;
@@ -453,116 +328,64 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
sprintf (light, "%s", " slot1 [SLOT2] "); sprintf (light, "%s", " slot1 [SLOT2] ");
//open MBEout file - slot 2 //open MBEout file - slot 2
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state); if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
} }
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+"); if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
else sprintf (polarity, "%s", "-"); else sprintf (polarity, "%s", "-");
if (state->dmr_color_code != 16)
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
else fprintf (stderr,"Sync: %sDMR %s| Color Code=XX | VC%d ", polarity, light, vc);
if (internalslot == 0 && vc1 == 6) fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
if (internalslot == 0 && vc1 == 6)
{ {
//this needs to run prior to embedded link control
if (state->payload_algid == 0x02)
hytera_enhanced_alg_refresh(state);
//process embedded link control //process embedded link control
fprintf (stderr, "\n"); fprintf (stderr, "\n");
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB); dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
//check the single burst/reverse channel opportunity -- moved //check the single burst/reverse channel opportunity
// dmr_sbrc (opts, state, power); dmr_sbrc (opts, state, power);
} }
if (internalslot == 1 && vc2 == 6) if (internalslot == 1 && vc2 == 6)
{ {
//this needs to run prior to embedded link control
if (state->payload_algidR == 0x02)
hytera_enhanced_alg_refresh(state);
//process embedded link control //process embedded link control
fprintf (stderr, "\n"); fprintf (stderr, "\n");
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB); dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
//check the single burst/reverse channel opportunity -- moved //check the single burst/reverse channel opportunity
// dmr_sbrc (opts, state, power); dmr_sbrc (opts, state, power);
} }
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
//copy ambe_fr frames first, running process mbe will correct them, //copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when //but this also leads to issues extracting good le mi values when
//we go to do correction on them there too //we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1)); memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2)); memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3)); memcpy (m3, ambe_fr3, sizeof(m3));
if (state->tyt_bp == 1)
{
tyt16_ambe2_codeword_keystream(state, ambe_fr, 0);
tyt16_ambe2_codeword_keystream(state, ambe_fr2, 1);
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
ambe2_codeword_print_i(opts, ambe_fr3);
#endif
processMbeFrame (opts, state, NULL, ambe_fr, NULL); processMbeFrame (opts, state, NULL, ambe_fr, NULL);
if(internalslot == 0)
{
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
}
else
{
memcpy(state->f_r4[0], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
memcpy(state->s_r4[0], state->s_r, sizeof(state->s_r));
memcpy(state->s_r4u[0], state->s_ru, sizeof(state->s_ru));
}
processMbeFrame (opts, state, NULL, ambe_fr2, NULL); processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
if(internalslot == 0)
{
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
}
else
{
memcpy(state->f_r4[1], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
memcpy(state->s_r4[1], state->s_r, sizeof(state->s_r));
memcpy(state->s_r4u[1], state->s_ru, sizeof(state->s_ru));
}
processMbeFrame (opts, state, NULL, ambe_fr3, NULL); processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
if(internalslot == 0)
cach_err = dmr_cach (opts, state, cachdata);
if (opts->payload == 0) fprintf (stderr, "\n");
// run alg refresh after vc6 ambe processing
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state);
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
//increment the vc counters
if (internalslot == 0) vc1++;
if (internalslot == 1) vc2++;
//update cc amd vc sync time for trunking purposes (particularly Con+)
if (opts->p25_is_tuned == 1)
{ {
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); state->last_vc_sync_time = time(NULL);
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l)); state->last_cc_sync_time = time(NULL);
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu)); }
}
else //'DSP' output to file
{
memcpy(state->f_r4[2], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
memcpy(state->s_r4[2], state->s_r, sizeof(state->s_r));
memcpy(state->s_r4u[2], state->s_ru, sizeof(state->s_ru));
}
//'DSP' output to file -- run before sbrc
if (opts->use_dsp_output == 1) if (opts->use_dsp_output == 1)
{ {
FILE * pFile; //file pointer FILE * pFile; //file pointer
@@ -582,34 +405,6 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
fclose (pFile); fclose (pFile);
} }
//run sbrc here to look for the late entry key and alg after we observe potential errors in VC6
if (internalslot == 0 && vc1 == 6) dmr_sbrc (opts, state, power);
if (internalslot == 1 && vc2 == 6) dmr_sbrc (opts, state, power);
cach_err = dmr_cach (opts, state, cachdata);
if (opts->payload == 0) fprintf (stderr, "\n");
// run alg refresh after vc6 ambe processing
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state);
if (internalslot == 0 && vc1 == 6) state->static_ks_counter[0] = 0;
if (internalslot == 1 && vc2 == 6) state->static_ks_counter[1] = 0;
if (opts->dmr_le != 2) //if not Hytera Enhanced
dmr_late_entry_mi_fragment (opts, state, vc%7, m1, m2, m3);
//increment the vc counters
if (internalslot == 0) vc1++;
if (internalslot == 1) vc2++;
//update cc amd vc sync time for trunking purposes (particularly Con+)
if (opts->p25_is_tuned == 1)
{
state->last_vc_sync_time = time(NULL);
state->last_cc_sync_time = time(NULL);
}
//reset err checks //reset err checks
cach_err = 1; cach_err = 1;
tact_okay = 0; tact_okay = 0;
@@ -627,17 +422,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
} }
SKIP: SKIP:
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with dmr_data_sync
//both working now, will need support added for ENC audio and no key
//NOTE: We want this to play regardless of whether the slot is voice or data, to play silence in one slot and voice in the second, or voices in both
if (internalslot == 1 && opts->floating_point == 1 && opts->pulse_digi_rate_out == 8000)
playSynthesizedVoiceFS3 (opts, state); //Float Stereo Mix 3v2
if (internalslot == 1 && opts->floating_point == 0 && opts->pulse_digi_rate_out == 8000)
playSynthesizedVoiceSS3 (opts, state); //Short Stereo Mix 3v2
if (skipcount > 3) //after 3 onsecutive data frames, drop back to getFrameSync and process with dmr_data_sync (need one more in order to push last voice on slot 2 only voice)
{ {
//set tests to all good so we don't get a bogus/redundant voice error //set tests to all good so we don't get a bogus/redundant voice error
cach_err = 0; cach_err = 0;
tact_okay = 1; tact_okay = 1;
emb_ok = 1; emb_ok = 1;
@@ -650,21 +437,6 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
ncursesPrinter(opts, state); ncursesPrinter(opts, state);
} }
//slot 1
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
//slot 2 for TDMA systems
watchdog_event_history(opts, state, 1);
watchdog_event_current(opts, state, 1);
//
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
} // while loop } // while loop
END: END:
@@ -678,100 +450,80 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state); if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state); if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
//clear out any stale audio storage buffers
memset (state->f_l4, 0.0f, sizeof(state->f_l4));
memset (state->f_r4, 0.0f, sizeof(state->f_r4));
memset (state->s_l4, 0, sizeof(state->s_l4));
memset (state->s_r4, 0, sizeof(state->s_r4));
//if we have a tact or emb err, then produce sync pattern/err message //if we have a tact or emb err, then produce sync pattern/err message
if (tact_okay != 1 || emb_ok != 1) if (tact_okay != 1 || emb_ok != 1)
{ {
fprintf (stderr,"%s ", timestr); fprintf (stderr,"%s ", getTime());
fprintf (stderr,"Sync: DMR "); fprintf (stderr,"Sync: DMR ");
fprintf (stderr, "%s", KRED); fprintf (stderr, "%s", KRED);
fprintf (stderr, "| VOICE CACH/EMB ERR"); fprintf (stderr, "| VOICE CACH/EMB ERR");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
//run refresh if either slot had an active MI in it. //run refresh if either slot had an active MI in it.
if (state->payload_algid >= 0x21) if (state->payload_algid != 0)
{ {
state->currentslot = 0; state->currentslot = 0;
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
else if (state->payload_algid == 0x02) if (state->payload_algidR != 0)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 0;
dmr_alg_refresh (opts, state);
}
if (state->payload_algidR >= 0x21)
{ {
state->currentslot = 1; state->currentslot = 1;
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
else if (state->payload_algidR == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 1;
dmr_alg_refresh (opts, state);
}
//failsafe to reset all data header and blocks when bad tact or emb //failsafe to reset all data header and blocks when bad tact or emb
dmr_reset_blocks (opts, state); dmr_reset_blocks (opts, state);
} }
//
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
//reset static ks counter
state->static_ks_counter[0] = 0;
state->static_ks_counter[1] = 0;
} }
//Process buffered half frame and 2nd half and then jump to full BS decoding //Process buffered half frame and 2nd half and then jump to full BS decoding
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state) void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
{ {
int i, dibit; int i, j, k, l, dibit;
int *dibit_p; int *dibit_p;
char ambe_fr[4][24]; char ambe_fr[4][24];
char ambe_fr2[4][24]; char ambe_fr2[4][24];
char ambe_fr3[4][24]; char ambe_fr3[4][24];
memset (ambe_fr, 0, sizeof(ambe_fr));
memset (ambe_fr2, 0, sizeof(ambe_fr2));
memset (ambe_fr3, 0, sizeof(ambe_fr3));
//memcpy of ambe_fr for late entry //memcpy of ambe_fr for late entry
uint8_t m1[4][24]; char m1[4][24];
uint8_t m2[4][24]; char m2[4][24];
uint8_t m3[4][24]; char m3[4][24];
const int *w, *x, *y, *z; const int *w, *x, *y, *z;
char sync[25]; char sync[25];
uint8_t tact_okay = 0; uint8_t tact_okay = 0;
uint8_t cach_err = 0; uint8_t cach_err = 0;
uint8_t sync_okay = 1; uint8_t sync_okay = 1;
UNUSED(cach_err);
uint8_t internalslot; uint8_t internalslot;
uint8_t cachdata[25]; char cachdata[25];
int cachInterleave[24] = int cachInterleave[24] =
{0, 7, 8, 9, 1, 10, {0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14, 11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18, 15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23 19, 5, 20, 21, 22, 6, 23
}; };
//add time to mirror printFrameSync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * timestr = getTimeC(); char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
//payload buffer //payload buffer
//CACH + First Half Payload + Sync = 12 + 54 + 24 //CACH + First Half Payload + Sync = 12 + 54 + 24
@@ -800,13 +552,11 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
//decode and correct tact and compare //decode and correct tact and compare
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1; if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
if (tact_okay != 1) goto END; if (tact_okay != 1) goto END;
internalslot = state->currentslot = tact_bits[1]; internalslot = state->currentslot = tact_bits[1];
//reset static ks counter
state->static_ks_counter[internalslot] = 0;
//Setup for first AMBE Frame //Setup for first AMBE Frame
//Interleave Schedule //Interleave Schedule
@@ -862,7 +612,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0) if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
{ {
sync_okay = 0; sync_okay = 0;
goto END; goto END;
} }
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling //Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
@@ -923,7 +673,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
fclose (pFile); fclose (pFile);
} }
fprintf (stderr,"%s ", timestr); fprintf (stderr,"%s ", getTime());
char polarity[3]; char polarity[3];
char light[18]; char light[18];
@@ -932,102 +682,34 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
sprintf (light, "%s", " [SLOT1] slot2 "); sprintf (light, "%s", " [SLOT1] slot2 ");
//open MBEout file - slot 1 //open MBEout file - slot 1
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state); if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
} }
else else
{ {
sprintf (light, "%s", " slot1 [SLOT2] "); sprintf (light, "%s", " slot1 [SLOT2] ");
//open MBEout file - slot 2 //open MBEout file - slot 2
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state); if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
} }
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+"); if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
else sprintf (polarity, "%s", "-"); else sprintf (polarity, "%s", "-");
if (state->dmr_color_code != 16)
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code); fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code);
else fprintf (stderr,"Sync: %sDMR %s| Color Code=XX | VC1*", polarity, light);
dmr_alg_reset (opts, state); dmr_alg_reset (opts, state);
//copy ambe_fr frames first, running process mbe will correct them, //copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when //but this also leads to issues extracting good le mi values when
//we go to do correction on them there too //we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1)); memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2)); memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3)); memcpy (m3, ambe_fr3, sizeof(m3));
if (state->tyt_bp == 1)
{
tyt16_ambe2_codeword_keystream(state, ambe_fr, 0);
tyt16_ambe2_codeword_keystream(state, ambe_fr2, 1);
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
ambe2_codeword_print_i(opts, ambe_fr3);
#endif
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
// processMbeFrame (opts, state, NULL, ambe_fr, NULL);
// processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
// processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
processMbeFrame (opts, state, NULL, ambe_fr, NULL); processMbeFrame (opts, state, NULL, ambe_fr, NULL);
if(internalslot == 0)
{
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
}
else
{
memcpy(state->f_r4[0], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
memcpy(state->s_r4[0], state->s_r, sizeof(state->s_r));
memcpy(state->s_r4u[0], state->s_ru, sizeof(state->s_ru));
}
processMbeFrame (opts, state, NULL, ambe_fr2, NULL); processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
if(internalslot == 0)
{
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
}
else
{
memcpy(state->f_r4[1], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
memcpy(state->s_r4[1], state->s_r, sizeof(state->s_r));
memcpy(state->s_r4u[1], state->s_ru, sizeof(state->s_ru));
}
processMbeFrame (opts, state, NULL, ambe_fr3, NULL); processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
if(internalslot == 0)
{
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
}
else
{
memcpy(state->f_r4[2], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
memcpy(state->s_r4[2], state->s_r, sizeof(state->s_r));
memcpy(state->s_r4u[2], state->s_ru, sizeof(state->s_ru));
}
//collect the mi fragment //collect the mi fragment
if (opts->dmr_le != 2) //if not Hytera Enhanced dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
cach_err = dmr_cach (opts, state, cachdata); cach_err = dmr_cach (opts, state, cachdata);
if (opts->payload == 0) fprintf (stderr, "\n"); if (opts->payload == 0) fprintf (stderr, "\n");
@@ -1035,55 +717,32 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
//update voice sync time for trunking purposes (particularly Con+) //update voice sync time for trunking purposes (particularly Con+)
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL); if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
//NOTE: Only play on slot 1, if slot 0, then it'll play after the next TDMA frame in the BS loop instead
if (internalslot == 1 && opts->floating_point == 1 && opts->pulse_digi_rate_out == 8000)
playSynthesizedVoiceFS3 (opts, state); //Float Stereo Mix 3v2
if (internalslot == 1 && opts->floating_point == 0 && opts->pulse_digi_rate_out == 8000)
playSynthesizedVoiceSS3 (opts, state); //Short Stereo Mix 3v2
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
END: END:
//if we have a tact err, then produce sync pattern/err message //if we have a tact err, then produce sync pattern/err message
if (tact_okay != 1 || sync_okay != 1) if (tact_okay != 1 || sync_okay != 1)
{ {
fprintf (stderr,"%s ", timestr); fprintf (stderr,"%s ", getTime());
fprintf (stderr,"Sync: DMR "); fprintf (stderr,"Sync: DMR ");
fprintf (stderr, "%s", KRED); fprintf (stderr, "%s", KRED);
fprintf (stderr, "| VOICE CACH/SYNC ERR"); fprintf (stderr, "| VOICE CACH/SYNC ERR");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
//run refresh if either slot had an active MI in it. //run refresh if either slot had an active MI in it.
if (state->payload_algid >= 0x21) if (state->payload_algid != 0)
{ {
state->currentslot = 0; state->currentslot = 0;
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
else if (state->payload_algid == 0x02) if (state->payload_algidR != 0)
{
state->currentslot = 0;
hytera_enhanced_alg_refresh(state);
dmr_alg_refresh (opts, state);
}
if (state->payload_algidR >= 0x21)
{ {
state->currentslot = 1; state->currentslot = 1;
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
else if (state->payload_algid == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 1;
dmr_alg_refresh (opts, state);
}
//failsafe to reset all data header and blocks when bad tact or emb //failsafe to reset all data header and blocks when bad tact or emb
dmr_reset_blocks (opts, state); dmr_reset_blocks (opts, state);
} }
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -25,17 +25,16 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
int *dibit_p; int *dibit_p;
char sync[25]; char sync[25];
char syncdata[48]; char syncdata[48];
uint8_t cachdata[25]; char cachdata[25];
UNUSED(syncdata);
char bursttype[5];
uint8_t burst; uint8_t burst;
char info[196]; char info[196];
unsigned char SlotType[20]; unsigned char SlotType[20];
unsigned int SlotTypeOk; unsigned int SlotTypeOk;
uint8_t cach_err = 0; uint8_t cach_err = 0;
UNUSED(cach_err);
int cachInterleave[24] = int cachInterleave[24] =
{0, 7, 8, 9, 1, 10, {0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14, 11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18, 15, 3, 16, 4, 17, 18,
@@ -43,7 +42,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
}; };
dibit_p = state->dmr_payload_p - 90; dibit_p = state->dmr_payload_p - 90;
//collect cach and de-interleave //collect cach and de-interleave
for (i = 0; i < 12; i++) for (i = 0; i < 12; i++)
{ {
@@ -76,9 +75,10 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
if ( Hamming_7_4_decode (tact_bits) ) cach_okay = 1; if ( Hamming_7_4_decode (tact_bits) ) cach_okay = 1;
else else
{ {
cach_okay = -1; cach_okay = -1;
SlotTypeOk = 0; SlotTypeOk = 0;
goto END; goto END;
} }
state->currentslot = tact_bits[1]; state->currentslot = tact_bits[1];
@@ -116,7 +116,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
} }
if (state->dmr_stereo == 1) if (state->dmr_stereo == 1)
{ {
dibit = (int)state->dmr_stereo_payload[61]; dibit = (int)state->dmr_stereo_payload[61];
} }
else state->dmr_stereo_payload[61] = dibit; else state->dmr_stereo_payload[61] = dibit;
@@ -146,7 +146,7 @@ dmr_data_sync (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]; dibit = (int)state->dmr_stereo_payload[63];
} }
else state->dmr_stereo_payload[63] = dibit; else state->dmr_stereo_payload[63] = dibit;
@@ -161,7 +161,7 @@ dmr_data_sync (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]; dibit = (int)state->dmr_stereo_payload[64];
} }
else state->dmr_stereo_payload[64] = dibit; else state->dmr_stereo_payload[64] = dibit;
@@ -180,6 +180,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
dibit = (int)state->dmr_stereo_payload[65]; dibit = (int)state->dmr_stereo_payload[65];
} }
else state->dmr_stereo_payload[65] = dibit; 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
@@ -194,7 +195,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
} }
if (state->dmr_stereo == 1) if (state->dmr_stereo == 1)
{ {
dibit = (int)state->dmr_stereo_payload[i+66]; dibit = (int)state->dmr_stereo_payload[i+66];
} }
else state->dmr_stereo_payload[i+66] = dibit; else state->dmr_stereo_payload[i+66] = dibit;
@@ -202,7 +203,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
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;
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) ) if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )
{ {
@@ -240,14 +241,14 @@ dmr_data_sync (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) if (state->dmr_stereo == 0)
{ {
dibit = getDibit(opts, state); dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+90] = dibit; state->dmr_stereo_payload[i+90] = dibit;
} }
if (state->dmr_stereo == 1) if (state->dmr_stereo == 1)
{ {
dibit = (int)state->dmr_stereo_payload[i+90]; 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
@@ -259,8 +260,9 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1; if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
else else
{ {
SlotTypeOk = 0; SlotTypeOk = 0;
goto END; goto END;
} }
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0); state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
@@ -278,7 +280,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
// Current slot - Second Half - Data Payload - 2nd part // Current slot - Second Half - Data Payload - 2nd part
for (i = 0; i < 49; i++) for (i = 0; i < 49; i++)
{ {
if (state->dmr_stereo == 0) if (state->dmr_stereo == 0)
{ {
dibit = getDibit(opts, state); dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+95] = dibit; state->dmr_stereo_payload[i+95] = dibit;
@@ -286,21 +288,21 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
if (state->dmr_stereo == 1) if (state->dmr_stereo == 1)
{ {
dibit = (int)state->dmr_stereo_payload[i+95]; dibit = (int)state->dmr_stereo_payload[i+95];
} }
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
dmr_data_burst_handler(opts, state, (uint8_t *)info, burst); if (state->dmr_ms_rc == 0) dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
//don't run cach on simplex or mono //don't run cach on simplex or mono
if (state->dmr_ms_mode == 0 && opts->dmr_mono == 0) if (state->dmr_ms_mode == 0 && opts->dmr_mono == 0)
{ {
cach_err = dmr_cach (opts, state, cachdata); cach_err = dmr_cach (opts, state, cachdata);
} }
//ending line break //ending line break
fprintf(stderr, "\n"); fprintf(stderr, "\n");
@@ -316,30 +318,37 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
} }
// 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);
} }
#define CON_TUNEAWAY //disable if any unlock issues noted on the logic //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
#ifdef CON_TUNEAWAY
//simplified, if IDLE condition, drop cc and vc sync time so when RF channel tears down, we go back much quicker
//NOTE: This was adopted, because going back to the CC early and then to be sent back to the channel grant,
//only for it to tear down and being sent back to the CC again isn't very efficient for trunk tracking
//NOTE: This will still leave the tuner in the 'locked' state when tuning a voice channel grant on the CC,
//and will remain locked until a new voice channel grant is received, but its just asthetic, trying to fix it
//is too much of a hassle and causes other issues like CC hunting, etc.
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1) if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1)
{ {
int clear = 0; //bug fixed that caused hopping from CC to VC; decreased to 2 seconds.
//IF both slots currently signalling IDLE if ( (time(NULL) - state->last_vc_sync_time > 2) )
if (state->dmrburstL == 9 && state->dmrburstR == 9) clear = 1;
if (clear == 1)
{ {
state->last_cc_sync_time = 0; if (opts->use_rigctl == 1) //rigctl tuning
state->last_vc_sync_time = 0; {
if (opts->setmod_bw != 0) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
}
else if (opts->audio_in_type == 3) //rtl_fm tuning
{
//UDP command to tune the RTL dongle
rtl_udp_tune(opts, state, state->p25_cc_freq);
}
opts->p25_is_tuned = 0;
//zero out vc frequencies
state->p25_vc_freq[0] = 0;
state->p25_vc_freq[1] = 0;
memset (state->active_channel, 0, sizeof(state->active_channel));
state->last_cc_sync_time = time(NULL);
state->is_con_plus = 0; //con+ flag off
} }
} }
#endif
} }

View File

@@ -6,15 +6,18 @@
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c * Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
* *
* LWVMOBILE * LWVMOBILE
* 2023-12 DSD-FME Florida Man Edition * 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
//TODO: Test USBD LIP Decoder with Real World Samples (if/when available) //WIP: Unified Single Block Data - USBD -- TODO: USBD LIP Protocol Handling
//TODO: Test UDT NMEA and LIP Decoders with Real World Samples (if/when available) //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" #include "dsd.h"
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst) void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst)
{ {
uint32_t i, j, k; uint32_t i, j, k;
@@ -23,13 +26,13 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
uint32_t CRCCorrect = 0; uint32_t CRCCorrect = 0;
uint32_t IrrecoverableErrors = 0; uint32_t IrrecoverableErrors = 0;
uint8_t slot = state->currentslot; uint8_t slot = state->currentslot;
uint8_t block = state->data_block_counter[slot];
//confirmed data //confirmed data
uint8_t dbsn = 0; //data block serial number for confirmed data blocks uint8_t dbsn = 0; //data block serial number for confirmed data blocks
uint8_t blockcounter = 0; //local block count uint8_t blockcounter = 0; //local block count
uint8_t confdatabits[250]; //array to reshuffle conf data block bits into sequence for crc9 check uint8_t confdatabits[250]; //array to reshuffle conf data block bits into sequence for crc9 check
memset (confdatabits, 0, sizeof(confdatabits)); memset (confdatabits, 0, sizeof(confdatabits));
UNUSED(dbsn);
//BPTC 196x96 Specific //BPTC 196x96 Specific
uint8_t BPTCDeInteleavedData[196]; uint8_t BPTCDeInteleavedData[196];
@@ -42,16 +45,9 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
uint8_t LC_DataBytes[10]; uint8_t LC_DataBytes[10];
int Burst = -1; int Burst = -1;
memset (BPTCDeInteleavedData, 0, sizeof(BPTCDeInteleavedData));
memset (BPTCDmrDataBit, 0, sizeof(BPTCDmrDataBit));
memset (BPTCDmrDataByte, 0, sizeof(BPTCDmrDataByte));
memset (BptcDataMatrix, 0, sizeof(BptcDataMatrix));
memset (LC_DataBit, 0, sizeof(LC_DataBit));
memset (LC_DataBytes, 0, sizeof(LC_DataBytes));
//PDU Bytes and Bits //PDU Bytes and Bits
uint8_t DMR_PDU[25]; uint8_t DMR_PDU[25];
uint8_t DMR_PDU_bits[196]; uint8_t DMR_PDU_bits[196];
memset (DMR_PDU, 0, sizeof (DMR_PDU)); memset (DMR_PDU, 0, sizeof (DMR_PDU));
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits)); memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
@@ -60,19 +56,19 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
uint8_t is_ras = 0; uint8_t is_ras = 0;
uint8_t crc_original_validity = 0; uint8_t crc_original_validity = 0;
uint32_t crcmask = 0; uint32_t crcmask = 0;
uint8_t crclen = 0; uint8_t crclen = 0;
uint8_t is_bptc = 0; uint8_t is_bptc = 0;
uint8_t is_trellis = 0; uint8_t is_trellis = 0;
uint8_t is_emb = 0; uint8_t is_emb = 0;
uint8_t is_lc = 0; uint8_t is_lc = 0;
uint8_t is_full = 0; uint8_t is_full = 0;
uint8_t is_udt = 0; uint8_t is_udt = 0;
uint8_t pdu_len = 0; uint8_t pdu_len = 0;
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed) uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
uint8_t usbd_st = 0; //usbd service type uint8_t usbd_st = 0; //usbd service type
switch(databurst){ switch(databurst){
case 0x00: //PI case 0x00: //PI
@@ -86,14 +82,14 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
is_bptc = 1; is_bptc = 1;
is_lc = 1; is_lc = 1;
crclen = 24; crclen = 24;
crcmask = 0x969696; crcmask = 0x969696;
pdu_len = 12; //12 bytes pdu_len = 12; //12 bytes
sprintf(state->fsubtype, " VLC "); sprintf(state->fsubtype, " VLC ");
break; break;
case 0x02: //TLC case 0x02: //TLC
is_bptc = 1; is_bptc = 1;
is_lc = 1; is_lc = 1;
crcmask = 0x999999; crcmask = 0x999999;
crclen = 24; crclen = 24;
pdu_len = 12; //12 bytes pdu_len = 12; //12 bytes
sprintf(state->fsubtype, " TLC "); sprintf(state->fsubtype, " TLC ");
@@ -101,7 +97,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
case 0x03: //CSBK case 0x03: //CSBK
is_bptc = 1; is_bptc = 1;
crclen = 16; crclen = 16;
crcmask = 0xA5A5; crcmask = 0xA5A5;
pdu_len = 12; //12 bytes pdu_len = 12; //12 bytes
sprintf(state->fsubtype, " CSBK"); sprintf(state->fsubtype, " CSBK");
break; break;
@@ -128,21 +124,19 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
case 0x07: //1/2 Rate Data case 0x07: //1/2 Rate Data
is_bptc = 1; is_bptc = 1;
crclen = 9; //confirmed data only crclen = 9; //confirmed data only
crcmask = 0x0F0; crcmask = 0x0F0;
pdu_len = 12; //12 bytes unconfirmed pdu_len = 12; //12 bytes unconfirmed
sprintf(state->fsubtype, " R12U "); sprintf(state->fsubtype, " R12U ");
if (state->data_conf_data[slot] == 1) if (state->data_conf_data[slot] == 1)
{ {
pdu_len = 10; pdu_len = 10;
pdu_start = 2; //start at plus two when assembling pdu_start = 2; //start at plus two when assembling
sprintf(state->fsubtype, " R12C "); sprintf(state->fsubtype, " R12C ");
} }
if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks
{ {
is_udt = 1; is_udt = 1;
if (state->data_conf_data[slot] == 1) sprintf(state->fsubtype, " UDTC ");
sprintf(state->fsubtype, " UDTC "); //confirmed data
else sprintf(state->fsubtype, " UDTU "); //unconfirmed data
} }
break; break;
case 0x08: //3/4 Rate Data case 0x08: //3/4 Rate Data
@@ -153,10 +147,10 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " R34U "); sprintf(state->fsubtype, " R34U ");
if (state->data_conf_data[slot] == 1) if (state->data_conf_data[slot] == 1)
{ {
pdu_len = 16; pdu_len = 16;
pdu_start = 2; //start at plus two when assembling pdu_start = 2; //start at plus two when assembling
sprintf(state->fsubtype, " R34C "); sprintf(state->fsubtype, " R34C ");
} }
break; break;
case 0x09: //Idle case 0x09: //Idle
//pseudo-random data fill, no need to do anything with this //pseudo-random data fill, no need to do anything with this
@@ -164,16 +158,16 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
break; break;
case 0x0A: //1 Rate Data case 0x0A: //1 Rate Data
crclen = 9; //confirmed data only crclen = 9; //confirmed data only
crcmask = 0x10F; crcmask = 0x10F;
is_full = 1; is_full = 1;
pdu_len = 24; //192 bits 24 bytes + 4 pad bits pdu_len = 25; //196 bits - 24.5 bytes
sprintf(state->fsubtype, " R_1U "); sprintf(state->fsubtype, " R_1U ");
if (state->data_conf_data[slot] == 1) if (state->data_conf_data[slot] == 1)
{ {
pdu_len = 22; //start at plus two when assembling pdu_len = 23;
pdu_start = 2; //start at plus two when assembling pdu_start = 2; //start at plus two when assembling
sprintf(state->fsubtype, " R_1C "); sprintf(state->fsubtype, " R_1C ");
} }
break; break;
case 0x0B: //Unified Single Block Data USBD case 0x0B: //Unified Single Block Data USBD
is_bptc = 1; is_bptc = 1;
@@ -182,7 +176,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
pdu_len = 12; //12 bytes pdu_len = 12; //12 bytes
sprintf(state->fsubtype, " USBD "); sprintf(state->fsubtype, " USBD ");
break; break;
//special types (not real data 'sync' bursts) //special types (not real data 'sync' bursts)
case 0xEB: //Embedded Signalling case 0xEB: //Embedded Signalling
crclen = 5; crclen = 5;
@@ -193,12 +187,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
default: default:
//Slot Type FEC should catch this so we never see it, //Slot Type FEC should catch this so we never see it,
//but if it doesn't, then we can still dump the entire 'packet' //but if it doesn't, then we can still dump the entire 'packet'
//treat like rate 1 unconfirmed data //treat like rate 1 unconfirmed data
is_full = 1; is_full = 1;
pdu_len = 25; //196 bits - 24.5 bytes pdu_len = 25; //196 bits - 24.5 bytes
sprintf(state->fsubtype, " _UNK "); sprintf(state->fsubtype, " _UNK ");
break; break;
} }
//flag off prop head when not looking at data blocks //flag off prop head when not looking at data blocks
@@ -206,12 +200,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
if (databurst != 0xEB) if (databurst != 0xEB)
{ {
if (state->dmr_ms_mode == 0) if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
{
if (state->dmr_color_code != 16)
fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
else fprintf(stderr, "| Color Code=XX ");
}
fprintf(stderr, "|%s", state->fsubtype); fprintf(stderr, "|%s", state->fsubtype);
//'DSP' output to file //'DSP' output to file
@@ -234,7 +223,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
fclose (pFile); fclose (pFile);
} }
} }
//Most Data Sync Burst types will use the bptc 196x96 //Most Data Sync Burst types will use the bptc 196x96
if (is_bptc) if (is_bptc)
{ {
@@ -252,7 +241,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x04); BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x04);
//debug print //debug print
//fprintf (stderr, " RAS? %X - %d %d %d", BPTCReservedBits, R[0], R[1], R[2]); //fprintf (stderr, " RAS? %X - %d %d %d", BPTCReservedBits, R[0], R[1], R[2]);
/* Convert the 96 bits BPTC data into 12 bytes */ /* Convert the 96 bits BPTC data into 12 bytes */
k = 0; k = 0;
@@ -269,16 +258,16 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */ /* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
CRCExtracted = 0; CRCExtracted = 0;
for(i = 0; i < crclen; i++) for(i = 0; i < crclen; i++)
{ {
CRCExtracted = CRCExtracted << 1; CRCExtracted = CRCExtracted << 1;
CRCExtracted = CRCExtracted | (uint32_t)(BPTCDmrDataBit[i + 96 - crclen] & 1); CRCExtracted = CRCExtracted | (uint32_t)(BPTCDmrDataBit[i + 96 - crclen] & 1);
} }
/* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */ /* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */
CRCExtracted = CRCExtracted ^ crcmask; CRCExtracted = CRCExtracted ^ crcmask;
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */ /* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask); if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
//set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these) //set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these)
@@ -287,7 +276,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
CRCComputed = 0; CRCComputed = 0;
CRCExtracted = 0; CRCExtracted = 0;
CRCCorrect = 1; CRCCorrect = 1;
} }
//run CRC9 on intermediate and last 1/2 confirmed data blocks //run CRC9 on intermediate and last 1/2 confirmed data blocks
else if (state->data_conf_data[slot] == 1 && databurst == 0x7) else if (state->data_conf_data[slot] == 1 && databurst == 0x7)
@@ -307,32 +296,32 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
{ {
CRCCorrect = 1; CRCCorrect = 1;
state->data_block_crc_valid[slot][blockcounter] = 1; state->data_block_crc_valid[slot][blockcounter] = 1;
} }
else state->data_block_crc_valid[slot][blockcounter] = 0; else state->data_block_crc_valid[slot][blockcounter] = 0;
} }
//run CCITT on other data forms //run CCITT on other data forms
else else
{ {
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit); CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
if (CRCComputed == CRCExtracted) CRCCorrect = 1; if (CRCComputed == CRCExtracted) CRCCorrect = 1;
else CRCCorrect = 0; else CRCCorrect = 0;
} }
//set the 'RAS Flag', if no irrecoverable errors but bad crc, only when enabled by user (to prevent a lot of bad data CSBKs) //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; 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 //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 (BPTCDmrDataByte[1] == 0x68) is_ras = 0;
//if this is a RAS system, set the CRC to okay if irrecoverable errors are okay //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 we don't do this, then we can't view some data (CSBKs on RAS enabled systems)
if (is_ras == 1) if (is_ras == 1)
{ {
crc_original_validity = CRCCorrect; crc_original_validity = CRCCorrect;
CRCCorrect = 1; CRCCorrect = 1;
} }
if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
{ {
@@ -342,7 +331,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
} }
else state->data_block_crc_valid[slot][0] = 0; else state->data_block_crc_valid[slot][0] = 0;
} }
/* Convert corrected x bytes into x*8 bits */ /* Convert corrected x bytes into x*8 bits */
for(i = 0, j = 0; i < pdu_len; i++, j+=8) for(i = 0, j = 0; i < pdu_len; i++, j+=8)
{ {
@@ -372,7 +361,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//Embedded Signalling will use BPTC 128x77 //Embedded Signalling will use BPTC 128x77
if (is_emb) if (is_emb)
{ {
CRCExtracted = 0; CRCExtracted = 0;
CRCComputed = 0; CRCComputed = 0;
IrrecoverableErrors = 0; IrrecoverableErrors = 0;
@@ -409,7 +398,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
if(CRCExtracted == CRCComputed) CRCCorrect = 1; if(CRCExtracted == CRCComputed) CRCCorrect = 1;
else CRCCorrect = 0; else CRCCorrect = 0;
for (i = 0; i < 72; i++) for (i = 0; i < 72; i++)
{ {
DMR_PDU_bits[i] = LC_DataBit[i]; DMR_PDU_bits[i] = LC_DataBit[i];
@@ -422,29 +411,34 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
} }
//the sexy one //the ugly one
if (is_trellis) if (is_trellis)
{ {
CRCExtracted = 0; CRCExtracted = 0;
CRCComputed = 0; CRCComputed = 0;
IrrecoverableErrors = 1; IrrecoverableErrors = 1;
bool trellis_good = TRUE;
uint8_t tdibits[98]; uint8_t tdibits[98];
memset (tdibits, 0, sizeof(tdibits)); memset (tdibits, 0, sizeof(tdibits));
//reconstitute info bits into dibits for the trellis decoder //reconstitute info bits into reverse ordered dibits for the trellis decoder
for (i = 0; i < 98; i++) for (i = 0; i < 98; i++)
tdibits[i] = (info[i*2] << 1) | info[i*2+1]; {
tdibits[97-i] = (info[i*2] << 1) | info[i*2+1];
}
uint8_t TrellisReturn[18]; unsigned char TrellisReturn[18];
memset (TrellisReturn, 0, sizeof(TrellisReturn)); memset (TrellisReturn, 0, sizeof(TrellisReturn));
IrrecoverableErrors = dmr_34(tdibits, TrellisReturn);
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis trellis_good = CDMRTrellisDecode(tdibits, TrellisReturn);
//they may have been successfully corrected, the CRC will reveal as much if (trellis_good == TRUE) IrrecoverableErrors = 0;
// else (fprintf (stderr, " Trellis Failure")); //debug print
for (i = 0; i < pdu_len; i++) for (i = 0; i < pdu_len; i++) //18
DMR_PDU[i] = TrellisReturn[i+pdu_start]; {
DMR_PDU[i] = (uint8_t)TrellisReturn[i+pdu_start];
}
for(i = 0, j = 0; i < 18; i++, j+=8) for(i = 0, j = 0; i < 18; i++, j+=8)
{ {
@@ -460,7 +454,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//set CRC to correct on unconfirmed 3/4 data blocks (for reporting due to no CRC available on these) //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; if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
//run CRC9 on intermediate and last 3/4 data blocks //run CRC9 on intermediate and last 3/4 data blocks
else if (state->data_conf_data[slot] == 1) else if (state->data_conf_data[slot] == 1)
{ {
@@ -470,18 +464,21 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
CRCExtracted = CRCExtracted ^ crcmask; CRCExtracted = CRCExtracted ^ crcmask;
//reorganize the DMR_PDU_bits array into confdatabits, just for CRC9 check //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 < 128; i++) confdatabits[i] = DMR_PDU_bits[i + 16];
for(i = 0; i < 7; i++) confdatabits[i + 128] = DMR_PDU_bits[i]; for(i = 0; i < 7; i++) confdatabits[i + 128] = DMR_PDU_bits[i];
CRCComputed = ComputeCrc9Bit(confdatabits, 135); CRCComputed = ComputeCrc9Bit(confdatabits, 135);
if (CRCExtracted == CRCComputed) if (CRCExtracted == CRCComputed)
{ {
CRCCorrect = 1; CRCCorrect = 1;
state->data_block_crc_valid[slot][blockcounter] = 1; state->data_block_crc_valid[slot][blockcounter] = 1;
} }
else else
{
// IrrecoverableErrors = 9; //set as a shim since the trellis decoder is a pain to fix
state->data_block_crc_valid[slot][blockcounter] = 0; state->data_block_crc_valid[slot][blockcounter] = 0;
}
} }
@@ -503,24 +500,22 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
if (is_full) //Rate 1 Data if (is_full) //Rate 1 Data
{ {
//assembly (w/ confirmed data) on a working Tier III Tait System
CRCExtracted = 0; CRCExtracted = 0;
CRCComputed = 0; CRCComputed = 0;
IrrecoverableErrors = 0; //implicit, since there is no encoding IrrecoverableErrors = 0; //implicit, since there is no encoding
//pack rate 1 data for a total of up to 24 bytes, (22 if Confirmed Data) for (i = 0; i < 24; i++)
//skipping the 4 padding bits located at 96,97,98,99 using the ptr values {
//as offsets for confirmed data, len, and continue points for packing DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8);
int bit_ptr = pdu_start * 8; }
int byte_ptr = 0; //leftover 4 bits, manual doesn't say which 4 so assuming last 4
pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12-pdu_start); //pushing last 4 to 25 and shifting it
bit_ptr = 100; byte_ptr = 12-pdu_start; DMR_PDU[25] = (uint8_t)ConvertBitIntoBytes(&info[192], 4);
pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12); 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) //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; if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
//run CRC9 on intermediate and last 1 rate data blocks //run CRC9 on intermediate and last 1 rate data blocks
else if (state->data_conf_data[slot] == 1) else if (state->data_conf_data[slot] == 1)
{ {
@@ -530,22 +525,18 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
CRCExtracted = CRCExtracted ^ crcmask; CRCExtracted = CRCExtracted ^ crcmask;
//reorganize the info bit array into confdatabits, just for CRC9 check //reorganize the info bit array into confdatabits, just for CRC9 check
int k = 0; //unconfirmed, haven't seen any samples with rate 1 data
for (i = 16; i < 96; i++) confdatabits[k++] = info[i]; //first half for(i = 0; i < 176; i++) confdatabits[i] = info[i + 16];
for (i = 100; i < 196; i++) confdatabits[k++] = info[i]; //second half for(i = 0; i < 7; i++) confdatabits[i + 176] = info[i];
for (i = 0; i < 7; i++) confdatabits[k++] = info[i]; //DBSN
CRCComputed = ComputeCrc9Bit(confdatabits, k);
CRCComputed = ComputeCrc9Bit(confdatabits, 183);
if (CRCExtracted == CRCComputed) if (CRCExtracted == CRCComputed)
{ {
CRCCorrect = 1; CRCCorrect = 1;
state->data_block_crc_valid[slot][blockcounter] = 1; state->data_block_crc_valid[slot][blockcounter] = 1;
} }
else state->data_block_crc_valid[slot][blockcounter] = 0; else state->data_block_crc_valid[slot][blockcounter] = 0;
//debug confirmed data values (working now)
// fprintf (stderr, " K: %d; DSBN: %d; CRC: %03X / %03X;", k, dbsn, CRCComputed, CRCExtracted);
} }
//copy info to dmr_pdu_bits //copy info to dmr_pdu_bits
@@ -553,14 +544,14 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
} }
//time for some pi //time for some pi
if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors); if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
//full link control //full link control
if (databurst == 0x01) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, &IrrecoverableErrors, 1); //VLC 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 == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, &IrrecoverableErrors, 3); //EMB if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 3); //EMB
//dmr data header and multi block types (header, 1/2, 3/4, 1, UDT) - type 1 //dmr data header and multi block types (header, 1/2, 3/4, 1, UDT) - type 1
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors); if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors);
@@ -571,14 +562,14 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//control signalling types (CSBK, MBC) //control signalling types (CSBK, MBC)
if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors); if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
//both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu //both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu
if (databurst == 0x04) if (databurst == 0x04)
{ {
state->data_block_counter[slot] = 0; //zero block counter before running header state->data_block_counter[slot] = 0; //zero block counter before running header
state->data_header_valid[slot] = 1; //set valid header since we received one state->data_header_valid[slot] = 1; //set valid header since we received one
dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2); dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
} }
if (databurst == 0x05) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2); if (databurst == 0x05) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
//Unified Single Data Block (USBD) -- Not to be confused with Unified Data Transport (UDT) //Unified Single Data Block (USBD) -- Not to be confused with Unified Data Transport (UDT)
@@ -587,8 +578,8 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//TODO: provide more robust handling at a later date -- ETSI TS 102 361-4 V1.11.1 (2021-01) 6.6.11.3 //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); usbd_st = (uint8_t)ConvertBitIntoBytes(&DMR_PDU_bits[0], 4);
fprintf (stderr, "%s\n", KYEL); fprintf (stderr, "%s\n", KYEL);
fprintf (stderr, " USBD - "); fprintf (stderr, " Unified Single Block Data - ");
if (usbd_st == 0) lip_protocol_decoder (opts, state, DMR_PDU_bits); if (usbd_st == 0) fprintf (stderr, "Location Information Protocol");
else if (usbd_st > 8) fprintf (stderr, "Manufacturer Specific Service %d ", usbd_st); else if (usbd_st > 8) fprintf (stderr, "Manufacturer Specific Service %d ", usbd_st);
else fprintf (stderr, "Reserved %d ", usbd_st); else fprintf (stderr, "Reserved %d ", usbd_st);
} }
@@ -597,7 +588,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
if (is_ras == 1) CRCCorrect = crc_original_validity; 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) //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 == 1 && databurst != 0x09 && databurst != 0x05) ; //fprintf(stderr, "(CRC OK)");
// if (IrrecoverableErrors == 0 && CRCCorrect == 0 && databurst != 0x09 && databurst != 0x05) // if (IrrecoverableErrors == 0 && CRCCorrect == 0 && databurst != 0x09 && databurst != 0x05)
// { // {
@@ -605,14 +596,14 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
// fprintf(stderr, " (FEC OK)"); // fprintf(stderr, " (FEC OK)");
// fprintf (stderr, "%s", KNRM); // fprintf (stderr, "%s", KNRM);
// } // }
if (IrrecoverableErrors != 0 && databurst != 0x08 && databurst != 0x09) //&& databurst != 0x05 if (IrrecoverableErrors != 0 && databurst != 0x09) //&& databurst != 0x05
{ {
fprintf (stderr, "%s", KRED); fprintf (stderr, "%s", KRED);
fprintf(stderr, " (FEC ERR)"); fprintf(stderr, " (FEC ERR)");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
//print whether or not the 'RAS Field' bits are set to indicate RAS enabled (to be verified) //print whether or not the 'RAS Field' bits are set to indicate RAS enabled (to be verified)
if (is_ras == 1) if (is_ras == 1)
@@ -623,7 +614,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
if (opts->payload == 1) fprintf (stderr, "%X ", BPTCReservedBits); if (opts->payload == 1) fprintf (stderr, "%X ", BPTCReservedBits);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05) if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
{ {
fprintf (stderr, "%s", KRED); fprintf (stderr, "%s", KRED);
@@ -647,7 +638,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed); // fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -9,11 +9,11 @@
#include "dsd.h" #include "dsd.h"
//gather ambe_fr mi fragments for processing //gather ambe_fr mi fragments for processing
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, uint8_t ambe_fr[4][24], uint8_t ambe_fr2[4][24], uint8_t ambe_fr3[4][24]) 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; uint8_t slot = state->currentslot;
//collect our fragments and place them into storage //collect our fragments and place them into storage
state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4); state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4); state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
@@ -25,24 +25,16 @@ void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc,
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state) void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
{ {
UNUSED(opts);
uint8_t slot = state->currentslot; uint8_t slot = state->currentslot;
int i, j; int i, j;
int g[3]; int g[3];
unsigned char mi_go_bits[24]; unsigned char mi_go_bits[24];
uint64_t mi_test = 0; uint64_t mi_test = 0;
uint64_t go_test = 0; uint64_t go_test = 0;
uint64_t mi_corrected = 0; uint64_t mi_corrected = 0;
uint64_t go_corrected = 0; uint64_t go_corrected = 0;
uint8_t mi_bits[36];
memset (mi_bits, 0, sizeof(mi_bits));
uint8_t mi_crc_cmp = 0;
uint8_t mi_crc_ext = 1;
uint8_t mi_crc_ok = 0;
mi_test = (state->late_entry_mi_fragment[slot][1][0] << 32L) | (state->late_entry_mi_fragment[slot][2][0] << 28) | (state->late_entry_mi_fragment[slot][3][0] << 24) | mi_test = (state->late_entry_mi_fragment[slot][1][0] << 32L) | (state->late_entry_mi_fragment[slot][2][0] << 28) | (state->late_entry_mi_fragment[slot][3][0] << 24) |
(state->late_entry_mi_fragment[slot][1][1] << 20) | (state->late_entry_mi_fragment[slot][2][1] << 16) | (state->late_entry_mi_fragment[slot][3][1] << 12) | (state->late_entry_mi_fragment[slot][1][1] << 20) | (state->late_entry_mi_fragment[slot][2][1] << 16) | (state->late_entry_mi_fragment[slot][3][1] << 12) |
(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); (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);
@@ -55,8 +47,8 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
{ {
for (i = 0; i < 12; i++) for (i = 0; i < 12; i++)
{ {
mi_go_bits[i] = (( mi_test << (i+j*12) ) & 0x800000000) >> 35; mi_go_bits[i] = (( mi_test << (i+j*12) ) & 0x800000000) >> 35;
mi_go_bits[i+12] = (( go_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 //execute golay decode and assign pass or fail to g
if ( Golay_24_12_decode(mi_go_bits) ) g[j] = 1; if ( Golay_24_12_decode(mi_go_bits) ) g[j] = 1;
@@ -68,133 +60,58 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
mi_corrected |= mi_go_bits[i]; mi_corrected |= mi_go_bits[i];
go_corrected = go_corrected << 1; go_corrected = go_corrected << 1;
go_corrected |= mi_go_bits[i+12]; go_corrected |= mi_go_bits[i+12];
//manipulate for crc check
mi_bits[i+(j*12)] = mi_go_bits[i];
} }
} }
unsigned long long int mi_final = 0; int mi_final = 0;
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF; mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
mi_crc_ext = (uint8_t)ConvertBitIntoBytes(&mi_bits[32], 4);
mi_crc_cmp = crc4(mi_bits, 32);
if (mi_crc_ext == mi_crc_cmp)
mi_crc_ok = 1;
//debug -- working now
// fprintf (stderr, " LE MI: %09llX; CRC EXT: %X; CRC CMP: %X; \n", mi_corrected, mi_crc_ext, mi_crc_cmp);
if (g[0] && g[1] && g[2]) if (g[0] && g[1] && g[2])
{ {
if (slot == 0 && state->payload_algid != 0) if (slot == 0 && state->payload_algid != 0)
{ {
if (state->payload_mi != mi_final) if (state->payload_mi != mi_final)
{ {
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08llX : %08llX ", state->payload_mi, mi_final); fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
if (mi_crc_ok == 1) state->payload_mi = mi_final; state->payload_mi = mi_final;
if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)");
else fprintf (stderr, "(CRC ERR)");
fprintf (stderr, "\n");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
//run afterwards, or le verification won't match up properly
//run expansions afterwards, or le verification won't match up properly if (state->payload_algid != 0x21)
//DES1
if (state->payload_algid == 0x22)
{ {
LFSR64 (state); LFSR64 (state);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//AES-128 or AES-256
if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{
LFSR128d (state);
fprintf (stderr, "\n");
}
} }
if (slot == 1 && state->payload_algidR != 0) if (slot == 1 && state->payload_algidR != 0)
{ {
if (state->payload_miR != mi_final) if (state->payload_miR != mi_final)
{ {
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08llX : %08llX ", state->payload_miR, mi_final); fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
if (mi_crc_ok == 1) state->payload_miR = mi_final; state->payload_miR = mi_final;
if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)");
else fprintf (stderr, "(CRC ERR)");
fprintf (stderr, "\n");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
//run afterwards, or le verification won't match up properly
//run expansions afterwards, or le verification won't match up properly if (state->payload_algidR != 0x21)
//DES1
if (state->payload_algidR == 0x22)
{ {
LFSR64 (state); LFSR64 (state);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//AES-128 or AES-256
if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{
LFSR128d (state);
fprintf (stderr, "\n");
}
} }
} }
//run LFSR even if golay fails
else if (slot == 0 && state->payload_algid != 0)
{
//DES1
if (state->payload_algid == 0x22)
{
LFSR64 (state);
fprintf (stderr, "\n");
}
//AES-128 or AES-256
if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{
LFSR128d (state);
fprintf (stderr, "\n");
}
}
else if (slot == 1 && state->payload_algidR != 0)
{
//DES1
if (state->payload_algidR == 0x22)
{
LFSR64 (state);
fprintf (stderr, "\n");
}
//AES-128 or AES-256
if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{
LFSR128d (state);
fprintf (stderr, "\n");
}
}
} }
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state) void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
{ {
UNUSED(opts);
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
state->dropL = 256; state->dropL = 256;
if (state->K1 != 0) if (state->K1 != 0)
{ {
state->DMRvcL = 0; state->DMRvcL = 0;
} }
@@ -204,44 +121,14 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
LFSR (state); LFSR (state);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
//LFSR64/128 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
if (state->payload_algid == 0x02)
{
state->DMRvcL = 0;
//LFSR already calculated, just dump it now
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid);
fprintf (stderr, " MI(40): %010llX;", state->payload_mi);
fprintf (stderr, " Hytera Enhanced;");
fprintf (stderr, "%s\n", KNRM);
}
if (state->payload_algid == 0x35 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
state->DMRvcL = 0;
state->payload_mi = kirisun_lfsr(state->payload_mi);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid);
fprintf (stderr, " MI(32): %08llX;", state->payload_mi);
fprintf (stderr, " Kirisun");
if (state->payload_algid == 0x36)
fprintf (stderr, " Advanced;");
else if (state->payload_algid == 0x37)
fprintf (stderr, " Universal;");
else fprintf (stderr, " Encryption;");
fprintf (stderr, "%s\n", KNRM);
}
} }
if (state->currentslot == 1) if (state->currentslot == 1)
{ {
state->dropR = 256; state->dropR = 256;
if (state->K1 != 0) if (state->K1 != 0)
{ {
state->DMRvcR = 0; state->DMRvcR = 0;
} }
@@ -251,37 +138,7 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
LFSR (state); LFSR (state);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
//LFSR64/128 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
if (state->payload_algidR == 0x02)
{
state->DMRvcR = 0;
//LFSR already calculated, just dump it now
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR);
fprintf (stderr, " MI(40): %010llX;", state->payload_miR);
fprintf (stderr, " Hytera Enhanced;");
fprintf (stderr, "%s\n", KNRM);
}
if (state->payload_algidR == 0x35 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
{
state->DMRvcR = 0;
state->payload_miR = kirisun_lfsr(state->payload_miR);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR);
fprintf (stderr, " MI(32): %08llX;", state->payload_miR);
fprintf (stderr, " Kirisun");
if (state->payload_algidR == 0x36)
fprintf (stderr, " Advanced;");
else if (state->payload_algidR == 0x37)
fprintf (stderr, " Universal;");
else fprintf (stderr, " Encryption;");
fprintf (stderr, "%s\n", KNRM);
}
} }
@@ -289,42 +146,28 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
void dmr_alg_reset (dsd_opts * opts, dsd_state * state) void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
{ {
UNUSED(opts);
state->dropL = 256; state->dropL = 256;
state->dropR = 256; state->dropR = 256;
state->DMRvcL = 0; state->DMRvcL = 0;
state->DMRvcR = 0; state->DMRvcR = 0;
// state->payload_miP = 0; //running these clears out before we can create a new keystream state->payload_miP = 0;
// state->payload_miN = 0; //running these clears out before we can create a new keystream state->payload_miN = 0;
} }
//handle Single Burst (Voice Burst F) or Reverse Channel Signalling //handle Single Burst (Voice Burst F) or Reverse Channel Signalling
void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power) void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
{ {
int i; int i;
uint8_t slot = state->currentslot; uint8_t slot = state->currentslot;
uint8_t sbrc_interleaved[32]; uint8_t sbrc_interleaved[32];
uint8_t sbrc_return[32]; uint8_t sbrc_return[32];
uint8_t sbrc_retcrc[32]; //crc significant bits of the return for SB
memset (sbrc_interleaved, 0, sizeof(sbrc_interleaved)); memset (sbrc_interleaved, 0, sizeof(sbrc_interleaved));
memset (sbrc_return, 0, sizeof(sbrc_return)); memset (sbrc_return, 0, sizeof(sbrc_return));
memset (sbrc_retcrc, 0, sizeof(sbrc_retcrc));
uint32_t irr_err = 0; uint32_t irr_err = 0;
uint32_t sbrc_hex = 0; uint32_t sbrc_hex = 0;
uint16_t crc_extracted = 7777; uint16_t crc_extracted = 7777;
uint16_t crc_computed = 9999; uint16_t crc_computed = 9999;
uint8_t crc7_okay = 0; //RC uint8_t crc_okay = 0;
uint8_t crc3_okay = 0; //TXI
uint8_t txi = 0; //SEE: https://patents.google.com/patent/US8271009B2
UNUSED(txi);
//NOTE: Any previous mentions to Cap+ in this area may have been in error,
//The signalling observed here was actually TXI information, not Cap+ Specifically
//check to see if this a TXI system
if (slot == 0 && state->dmr_so & 0x20) txi = 1;
if (slot == 1 && state->dmr_soR & 0x20) txi = 1;
// 9.3.2 Pre-emption and power control Indicator (PI) // 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 // 0 - The embedded signalling carries information associated to the same logical channel or the Null embedded message
@@ -338,11 +181,9 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
//bad emb burst, never set a valid power indicator value (probably 9) //bad emb burst, never set a valid power indicator value (probably 9)
if (power > 1) goto SBRC_END; if (power > 1) goto SBRC_END;
//arrange the return for proper order for the crc3 check //RC Channel CRC 7 Mask = 0x7A; CRC bits are used as privacy indicators on
for (i = 0; i < 8; i++) sbrc_retcrc[i] = sbrc_return[i+3]; //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)
//RC Channel CRC 7 Mask = 0x7A; CRC bits are used as privacy indicators on
//Single Voice Burst F (see below), other moto values seem to exist there as well -- See TXI patent
if (power == 1) //RC if (power == 1) //RC
{ {
crc_extracted = 0; crc_extracted = 0;
@@ -352,283 +193,124 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
crc_extracted = crc_extracted | sbrc_return[i+4]; crc_extracted = crc_extracted | sbrc_return[i+4];
} }
crc_extracted = crc_extracted ^ 0x7A; crc_extracted = crc_extracted ^ 0x7A;
crc_computed = crc7((uint8_t *) sbrc_return, 4); //#187 fix crc_computed = crc7((uint8_t *) sbrc_return, 11);
if (crc_extracted == crc_computed) crc7_okay = 1; if (crc_extracted == crc_computed) crc_okay = 1;
} }
else //if (txi == 1) //if TXI -- but TXI systems also carry the non-crc protected ENC identifiers else crc_okay = 1; //SB
{
crc_extracted = 0;
for (i = 0; i < 3; i++)
{
crc_extracted = crc_extracted << 1;
crc_extracted = crc_extracted | sbrc_return[i]; //first 3 most significant bits
}
crc_computed = crc3((uint8_t *) sbrc_retcrc, 8); //working now seems consistent as well
if (crc_extracted == crc_computed) crc3_okay = 1;
// fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
}
// else //crc_okay = 0; //CRC invalid / not available when is ENC Identifiers (I really hate that)
for(i = 0; i < 11; i++) if (opts->payload == 1) //hide the sb/rc behind the payload printer, won't be useful to most people right now
{ {
sbrc_hex = sbrc_hex << 1; fprintf (stderr, "\n %s", KCYN);
sbrc_hex |= sbrc_return[i] & 1;
}
if (opts->payload == 1) //hide the sb/rc behind the payload printer, won't be useful to most people
{
fprintf (stderr, "%s", KCYN);
if (power == 0) fprintf (stderr, " SB: "); if (power == 0) fprintf (stderr, " SB: ");
if (power == 1) fprintf (stderr, " RC: "); if (power == 1) fprintf (stderr, " RC: ");
for(i = 0; i < 11; i++) 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, "%d", sbrc_return[i]);
fprintf (stderr, " - %03X; ", sbrc_hex); }
fprintf (stderr, " - %03X", sbrc_hex);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
// if (crc_okay == 0) //forego this since the crc can vary or not be used at all if (crc_okay == 0)
// {
// fprintf (stderr, "%s", KRED);
// fprintf (stderr, " (CRC ERR)");
// fprintf (stderr, "%s", KNRM);
// fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
// }
fprintf (stderr, "\n");
}
uint8_t sbrc_opcode = sbrc_hex & 0x7; //opcode and alg the same bits, but the alg is present when CRC is bad (I know they are limited on bits, but I hate that idea)
uint8_t alg = sbrc_hex & 0x7; //SEE: https://patents.google.com/patent/EP2347540B1/en
uint8_t key = (sbrc_hex >> 3) & 0xFF;
uint8_t txi_delay = (sbrc_hex >> 3) & 0x1F; //middle five are the 'delay' value on a TXI system
//Note: AES-256 Key 1 will pass a CRC3 due to its bit arrangement vs the CRC poly 1101
// if (irr_err == 0 && sbrc_hex = 0xD) crc3_okay = 0; //SB: 00000001101
//NOTE: on above, I belive that we need to check by opcode as well, as a CRC3 can have multiple collisions
//so we need to exclude op/alg 0 and 3 from the check (does algID 0x03/0x23 even exist?)
//Kirisun Placeholder
if (opts->dmr_le == 3)
{
if (irr_err != 0)
{ {
uint32_t sbrcpl = 0; fprintf (stderr, "%s", KRED);
for(i = 0; i < 32; i++) fprintf (stderr, " (CRC ERR)");
{ fprintf (stderr, "%s", KNRM);
sbrcpl = sbrcpl << 1; if (opts->payload == 1) fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
sbrcpl |= sbrc_interleaved[i] & 1;
}
if (opts->payload == 0) fprintf (stderr, "\n");
fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
}
else if (irr_err == 0 && sbrc_hex != 0) //first batch had sbrc_hex 0x5F1, second batch had 0x5E1 instead, what changed?
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot %d", state->currentslot+1);
fprintf (stderr, " DMR LE SB Kirisun Encryption Identifier;");
fprintf (stderr, "%s ", KNRM);
if (state->currentslot == 0)
{
if (state->payload_algid == 0 && state->dmr_so & 0x40)
state->payload_algid = 0x35; //placeholder value
}
else
{
if (state->payload_algidR == 0 && state->dmr_soR & 0x40)
state->payload_algidR = 0x35; //placeholder value
}
} }
} }
//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;
if (opts->dmr_le == 1) //this pattern seems to fit the multiple cap+ systems I've observed
{ //test on site '7' to make sure (works on 1 and 4)
if (irr_err != 0) // uint8_t cap_site = (sbrc_hex >> 4) & 0x7;
{
uint32_t sbrcpl = 0;
for(i = 0; i < 32; i++)
{
sbrcpl = sbrcpl << 1;
sbrcpl |= sbrc_interleaved[i] & 1;
}
if (opts->payload == 0) fprintf (stderr, "\n");
fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
}
if (irr_err == 0)
{
if (sbrc_hex == 0 && crc7_okay == 0) ; //NULL Single Burst, do nothing (changed to allow the 0 value of RC below to trigger)
//else if (placeholder for future conditions) // if (opts->payload == 1)
// { // {
// placeholder for future conditions // 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
// {
else if (crc7_okay == 1) // if (slot == 0)
{ // {
//decode the reverse channel information ETSI TS 102 361-4 V1.12.1 (2023-07) p 103 Table 6.32: MS Reverse Channel (RC) Command Information Elements // //key and alg only present SOME times, not all,
if (opts->payload == 0) fprintf (stderr, "\n"); // //also, intermixed with other signalling
fprintf (stderr, "%s", KCYN); // //needs more study first!
sbrc_hex = sbrc_hex >> 7; //set value to its 4-bit form // if (state->dmr_so & 0x40 && key != 0 && state->payload_keyid == 0)
if (sbrc_hex == 0) fprintf (stderr, " RC: Increase Power By One Step;"); // {
else if (sbrc_hex == 1) fprintf (stderr, " RC: Decrease Power By One Step;"); // if (opts->payload == 1)
else if (sbrc_hex == 2) fprintf (stderr, " RC: Set Power To Highest;"); // {
else if (sbrc_hex == 3) fprintf (stderr, " RC: Set Power To Lowest;"); // fprintf (stderr, "%s ", KYEL);
else if (sbrc_hex == 4) fprintf (stderr, " RC: Cease Transmission Command;"); // fprintf (stderr, "\n Slot 1");
else if (sbrc_hex == 5) fprintf (stderr, " RC: Cease Transmission Request;"); // fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
else fprintf (stderr, " RC: Reserved %02X;", sbrc_hex); // fprintf (stderr, "%s ", KNRM);
fprintf (stderr, "%s", KNRM); // }
// if (opts->payload == 1)
fprintf (stderr, "\n"); // //needs more study before assignment
} // // state->payload_keyid = key;
// // state->payload_algid = alg + 0x20; //assuming DMRA approved alg values (moto patent)
// }
// }
//if the call is interruptable (TXI) and the crc3 is okay and TXI Opcode // if (slot == 1)
else if (crc3_okay == 1 && (sbrc_opcode == 0 || sbrc_opcode == 3) ) // {
{ // if (state->dmr_soR & 0x40 && key != 0 && state->payload_keyidR == 0)
//opcodes -- 0 (NULL), BR Delay (3) // {
if (opts->payload == 0) fprintf (stderr, "\n"); // if (opts->payload == 1)
fprintf (stderr, "%s", KCYN); // {
fprintf (stderr, " TXI Op: %X -", sbrc_opcode); // fprintf (stderr, "%s ", KYEL);
if (sbrc_opcode == 0) fprintf (stderr, " Null; "); // fprintf (stderr, "\n Slot 2");
else if (sbrc_opcode == 3) // fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
{ // fprintf (stderr, "%s ", KNRM);
if (txi_delay != 0) // }
fprintf (stderr, " BR Delay: %d - %d ms;", txi_delay, txi_delay * 30); //could also indicate number of superframes until next VC6 pre-emption
else fprintf (stderr, "BR Delay: Irrelevant / Send at any time;"); // //needs more study before assignment
// // state->payload_keyidR = key;
// // state->payload_algidR = alg + 0x20; //assuming DMRA approved alg values (moto patent)
// }
// }
//alignment of inbound backwards channel and outbound burst // }
if (txi_delay == 2) fprintf (stderr, " SF3, Burst E;"); //E is inbound
if (txi_delay == 4) fprintf (stderr, " SF3, Burst D;"); //D is inbound
if (txi_delay == 6) fprintf (stderr, " SF3, Burst C;"); //C is inbound
if (txi_delay == 8) fprintf (stderr, " SF3, Burst B;"); //B is inbound
}
else fprintf (stderr, " Unk; ");
fprintf (stderr, "%s", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n"); //only during payload // }
// }
}
else if (sbrc_opcode != 0 && sbrc_opcode != 3) //all that should be left in this field is the potential ENC identifiers
{
if (slot == 0) //may not need the state->errs anymore //&& state->errs < 3
{
if (state->dmr_so & 0x40 && key != 0 && alg != 0)
{
//if we aren't forcing a particular alg or privacy key set
if (state->forced_alg_id == 0)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR LE SB ALG ID: %02X; KEY ID: %02X;", alg + 0x20, key);
if (alg == 1) fprintf (stderr, " RC4;");
if (alg == 2) fprintf (stderr, " DES;");
if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;");
fprintf (stderr, "%s ", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
if (state->payload_keyid != key)
state->payload_keyid = key;
if (state->payload_algid != alg)
state->payload_algid = alg + 0x20; //assuming DMRA approved alg values (moto patent)
}
}
}
if (slot == 1) //may not need the state->errs anymore //&& state->errsR < 3
{
if (state->dmr_soR & 0x40 && key != 0 && alg != 0)
{
//if we aren't forcing a particular alg or privacy key set
if (state->forced_alg_id == 0)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR LE SB ALG ID: %02X; KEY ID: %02X;", alg + 0x20, key);
fprintf (stderr, "%s ", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
if (state->payload_keyidR != key)
state->payload_keyidR = key;
if (state->payload_algidR != alg)
state->payload_algidR = alg + 0x20; //assuming DMRA approved alg values (moto patent)
}
}
}
}
}
}
SBRC_END: SBRC_END:
//'DSP' output to file -- SB and RC //'DSP' output to file -- only RC, or RC and SB?
if (opts->use_dsp_output == 1) if (power == 1 && opts->use_dsp_output == 1 && sbrc_hex != 0) //if not NULL
{ {
FILE * pFile; //file pointer FILE * pFile; //file pointer
pFile = fopen (opts->dsp_out_file, "a"); pFile = fopen (opts->dsp_out_file, "a");
fprintf (pFile, "\n%d 99 ", slot+1); //'99' is SB and RC designation value fprintf (pFile, "\n%d 99 ", slot+1); //'99' is RC designation value
for (i = 0; i < 12; i++) //48 bits (includes CC, PPI, LCSS, and QR) int k = 0;
// for (i = 2; i < 10; i++) //32 bits (only SB/RC Data and its PC/H) for (i = 0; i < 24; i++) //12 bytes, SB or RC
{ {
uint8_t sbrc_nib = (state->dmr_embedded_signalling[slot][5][(i*4)+0] << 3) | (state->dmr_embedded_signalling[slot][5][(i*4)+1] << 2) | (state->dmr_embedded_signalling[slot][5][(i*4)+2] << 1) | (state->dmr_embedded_signalling[slot][5][(i*4)+3] << 0); //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); fprintf (pFile, "%X", sbrc_nib);
} }
fclose (pFile); fclose (pFile);
} }
} }
uint8_t crc3(uint8_t bits[], unsigned int len)
{
uint8_t crc=0;
unsigned int K = 3;
//x^3+x+1
uint8_t poly[4] = {1,1,0,1};
uint8_t buf[256];
if (len+K > sizeof(buf)) {
return 0;
}
memset (buf, 0, sizeof(buf));
for (unsigned int i=0; i<len; i++){
buf[i] = bits[i];
}
for (unsigned int i=0; i<len; i++)
if (buf[i])
for (unsigned int j=0; j<K+1; j++)
buf[i+j] ^= poly[j];
for (unsigned int i=0; i<K; i++){
crc = (crc << 1) + buf[len + i];
}
return crc;
}
uint8_t crc4(uint8_t bits[], unsigned int len)
{
uint8_t crc=0;
unsigned int K = 4;
//x^4+x+1
uint8_t poly[5] = {1,0,0,1,1};
uint8_t buf[256];
if (len+K > sizeof(buf)) {
return 0;
}
memset (buf, 0, sizeof(buf));
for (unsigned int i=0; i<len; i++){
buf[i] = bits[i];
}
for (unsigned int i=0; i<len; i++)
if (buf[i])
for (unsigned int j=0; j<K+1; j++)
buf[i+j] ^= poly[j];
for (unsigned int i=0; i<K; i++){
crc = (crc << 1) + buf[len + i];
}
return crc ^ 0xF; //invert
}

View File

@@ -9,42 +9,43 @@
#include "dsd.h" #include "dsd.h"
#include "dmr_const.h" #include "dmr_const.h"
// #define PRINT_AMBE72 //enable to view 72-bit AMBE codewords
//A subroutine for processing MS voice //A subroutine for processing MS voice
void dmrMS (dsd_opts * opts, dsd_state * state) void dmrMS (dsd_opts * opts, dsd_state * state)
{ {
char * timestr = getTimeC(); int i, j, k, l, dibit;
int *dibit_p;
int i, j, dibit;
char ambe_fr[4][24]; char ambe_fr[4][24];
char ambe_fr2[4][24]; char ambe_fr2[4][24];
char ambe_fr3[4][24]; char ambe_fr3[4][24];
char ambe_fr4[4][24]; char ambe_fr4[4][24];
//memcpy of ambe_fr for late entry //memcpy of ambe_fr for late entry
uint8_t m1[4][24]; char m1[4][24];
uint8_t m2[4][24]; char m2[4][24];
uint8_t m3[4][24]; char m3[4][24];
const int *w, *x, *y, *z; const int *w, *x, *y, *z;
char sync[25];
uint8_t syncdata[48]; char syncdata[48];
memset (syncdata, 0, sizeof(syncdata)); char EmbeddedSignalling[16];
uint8_t emb_pdu[16];
memset (emb_pdu, 0, sizeof(emb_pdu));
//cach //cach
char cachdata[25]; char cachdata[25];
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
};
//cach tact bits //cach tact bits
uint8_t tact_bits[7]; uint8_t tact_bits[7];
uint8_t tact_okay = 0;
uint8_t emb_ok = 0; uint8_t emb_ok = 0;
UNUSED(emb_ok); uint8_t tact_okay = 0;
uint8_t cach_err = 0;
uint8_t EmbeddedSignallingOk = 0;
uint8_t internalslot; uint8_t internalslot;
uint8_t vc; uint8_t vc;
@@ -53,26 +54,40 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
uint8_t cc = 25; uint8_t cc = 25;
uint8_t power = 9; //power and pre-emption indicator uint8_t power = 9; //power and pre-emption indicator
uint8_t lcss = 9; uint8_t lcss = 9;
UNUSED2(cc, lcss);
//dummy bits to pass to dburst for link control //dummy bits to pass to dburst for link control
uint8_t dummy_bits[196]; uint8_t dummy_bits[196];
memset (dummy_bits, 0, sizeof (dummy_bits)); memset (dummy_bits, 0, sizeof (dummy_bits));
//add time to mirror printFrameSync
time_t now;
char * getTime(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;
}
vc = 2; vc = 2;
short int skipcount = 0;
//Hardset variables for MS/Mono //Hardset variables for MS/Mono
state->currentslot = 0; //0 state->currentslot = 0; //0
//Note: Manual dibit inversion required here since I didn't seperate inverted return from normal return in framesync, //Note: Manual dibit inversion required here since I didn't seperate inverted return from normal return in framesync,
//so getDibit doesn't know to invert it before it gets here //so getDibit doesn't know to invert it before it gets here
for (j = 0; j < 6; j++) { for (j = 0; j < 6; j++) {
state->dmrburstL = 16; state->dmrburstL = 16;
memset (ambe_fr, 0, sizeof(ambe_fr));
memset (ambe_fr2, 0, sizeof(ambe_fr2));
memset (ambe_fr3, 0, sizeof(ambe_fr3));
for(i = 0; i < 12; i++) for(i = 0; i < 12; i++)
{ {
@@ -151,15 +166,17 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
for(i = 0; i < 24; i++) for(i = 0; i < 24; i++)
{ {
dibit = getDibit(opts, state); dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2); if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
state->dmr_stereo_payload[i+66] = dibit; 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;
// load the superframe to do embedded signal processing // load the superframe to do embedded signal processing
if(vc > 1) //grab on vc2 values 2-5 B C D E, and F if(vc > 1) //grab on vc1 values 2-5 B C D and E (F for Single/RC)
{ {
state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1 state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1
state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 0 state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 0
@@ -167,27 +184,34 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
} }
//Kirisun Check VC-F for Enc Identifiers -- has bad fec, unless its loaded differently sync[24] = 0;
//SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun
// if (opts->dmr_le == 3 && vc == 6)
// {
// bool kokay = 0;
// // kokay = Golay_24_12_decode(syncdata);
// unsigned long long int kirisun = (unsigned long long int)ConvertBitIntoBytes(&syncdata[0], 48);
// fprintf (stderr, "Kiri: %012llX; Golay Okay: %d; \n", kirisun, kokay);
// }
for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i]; //test for RC sync pattern in MS sourced audio
for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40]; if ( strcmp (sync, DMR_RC_DATA_SYNC) == 0)
emb_ok = -1;
if(QR_16_7_6_decode(emb_pdu))
{ {
emb_ok = 1; state->dmr_ms_rc = 1;
cc = ((emb_pdu[0] << 3) + (emb_pdu[1] << 2) + (emb_pdu[2] << 1) + emb_pdu[3]); fprintf (stderr, "%s ", getTime());
power = emb_pdu[4]; if (opts->inverted_dmr == 0)
lcss = ((emb_pdu[5] << 1) + emb_pdu[6]); {
state->dmr_color_code = state->color_code = cc; fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
}
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
fprintf (stderr, "| RC ");
fprintf (stderr, "\n");
}
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
EmbeddedSignallingOk = -1;
if(QR_16_7_6_decode(EmbeddedSignalling))
{
EmbeddedSignallingOk = 1;
cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
power = EmbeddedSignalling[4];
lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
} }
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling //Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
@@ -249,78 +273,41 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2)); memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
//copy ambe_fr frames first, running process mbe will correct them, //copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when //but this also leads to issues extracting good le mi values when
//we go to do correction on them there too //we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1)); memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2)); memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3)); memcpy (m3, ambe_fr3, sizeof(m3));
if (state->tyt_bp == 1) if (state->dmr_ms_rc == 0)
{ {
tyt16_ambe2_codeword_keystream(state, ambe_fr, 0); if (state->directmode == 0)
tyt16_ambe2_codeword_keystream(state, ambe_fr2, 1); {
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0); processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
else
{
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
} }
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
ambe2_codeword_print_i(opts, ambe_fr3);
#endif
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
//TODO: Consider copying f_l to f_r for left and right channel saturation on MS mode
if (opts->floating_point == 0)
{
// memcpy (state->s_r4, state->s_l4, sizeof(state->s_l4));
if(opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceSS3(opts, state);
}
if (opts->floating_point == 1)
{
// memcpy (state->f_r4, state->f_l4, sizeof(state->f_l4));
if(opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceFS3(opts, state);
}
if (vc == 6) if (vc == 6)
{ {
//this needs to run prior to embedded link control
if (state->payload_algid == 0x02)
hytera_enhanced_alg_refresh(state);
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB); dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
//check the single burst/reverse channel opportunity //check the single burst/reverse channel opportunity
dmr_sbrc (opts, state, power); dmr_sbrc (opts, state, power);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
//collect the mi fragment //collect the mi fragment
if (opts->dmr_le != 2) //if not Hytera Enhanced dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
//errors in ms/mono since we skip the other slot //errors in ms/mono since we skip the other slot
// cach_err = dmr_cach (opts, state, cachdata); // cach_err = dmr_cach (opts, state, cachdata);
@@ -329,16 +316,17 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
state->last_vc_sync_time = time(NULL); state->last_vc_sync_time = time(NULL);
vc++; vc++;
//reset emb components //reset emb components
cc = 25; cc = 25;
power = 9; //power and pre-emption indicator power = 9; //power and pre-emption indicator
lcss = 9; lcss = 9;
//this is necessary because we need to skip and collect dibits, not just skip them //this is necessary because we need to skip and collect dibits, not just skip them
if (vc > 6) goto END; if (vc > 6) goto END;
skipDibit (opts, state, 144); //skip to next tdma channel skipDibit (opts, state, 144); //skip to next tdma channel
state->dmr_ms_rc = 0;
//since we are in a loop, run ncursesPrinter here //since we are in a loop, run ncursesPrinter here
if (opts->use_ncurses_terminal == 1) if (opts->use_ncurses_terminal == 1)
@@ -346,10 +334,6 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
ncursesPrinter(opts, state); ncursesPrinter(opts, state);
} }
//slot 1
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
} // end loop } // end loop
END: END:
@@ -370,52 +354,69 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
state->dmr_stereo = 0; state->dmr_stereo = 0;
state->dmr_ms_mode = 0; state->dmr_ms_mode = 0;
state->dmr_ms_rc = 0;
state->directmode = 0; //flag off state->directmode = 0; //flag off
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
//reset static ks counter
state->static_ks_counter[0] = 0;
} }
//collect buffered 1st half and get 2nd half voice payload and then jump to full MS Voice decoding. //collect buffered 1st half and get 2nd half voice payload and then jump to full MS Voice decoding.
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state) void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
{ {
int i, j, k, l, dibit;
char * timestr = getTimeC();
//reset static ks counter
state->static_ks_counter[0] = 0;
int i, dibit;
int *dibit_p; int *dibit_p;
char ambe_fr[4][24]; char ambe_fr[4][24];
char ambe_fr2[4][24]; char ambe_fr2[4][24];
char ambe_fr3[4][24]; char ambe_fr3[4][24];
char ambe_fr4[4][24]; char ambe_fr4[4][24]; //shim for crappy tdma direct, need more samples to see if this is really a necessary shim or not
memset (ambe_fr, 0, sizeof(ambe_fr));
memset (ambe_fr2, 0, sizeof(ambe_fr2));
memset (ambe_fr3, 0, sizeof(ambe_fr3));
//memcpy of ambe_fr for late entry //memcpy of ambe_fr for late entry
uint8_t m1[4][24]; char m1[4][24];
uint8_t m2[4][24]; char m2[4][24];
uint8_t m3[4][24]; char m3[4][24];
const int *w, *x, *y, *z; const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
//cach //cach
char cachdata[25]; char cachdata[25];
UNUSED(cachdata); 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
};
state->dmrburstL = 16; int mutecurrentslot;
int msMode;
char cc[4];
char EmbeddedSignalling[16];
uint8_t emb_ok = 0;
uint8_t tact_okay = 0;
uint8_t cach_err = 0;
uint8_t EmbeddedSignallingOk = 0;
//add time to mirror sync
time_t now;
char * getTime(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;
}
state->dmrburstL = 16;
state->currentslot = 0; //force to slot 0 state->currentslot = 0; //force to slot 0
dibit_p = state->dmr_payload_p - 90; dibit_p = state->dmr_payload_p - 90;
@@ -550,15 +551,13 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
fclose (pFile); fclose (pFile);
} }
fprintf (stderr, "%s ", timestr); fprintf (stderr, "%s ", getTime());
if (opts->inverted_dmr == 0) if (opts->inverted_dmr == 0)
{ {
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO "); fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
} }
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO "); else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
if (state->dmr_color_code != 16)
fprintf (stderr, "| Color Code=%02d ", state->dmr_color_code);
else fprintf (stderr, "| Color Code=XX ");
fprintf (stderr, "| VC* "); fprintf (stderr, "| VC* ");
fprintf (stderr, "\n"); fprintf (stderr, "\n");
@@ -567,72 +566,31 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2)); memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
//copy ambe_fr frames first, running process mbe will correct them, //copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when //but this also leads to issues extracting good le mi values when
//we go to do correction on them there too //we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1)); memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2)); memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3)); memcpy (m3, ambe_fr3, sizeof(m3));
if (state->tyt_bp == 1) if (state->directmode == 0)
{ {
tyt16_ambe2_codeword_keystream(state, ambe_fr, 0); processMbeFrame (opts, state, NULL, ambe_fr, NULL);
tyt16_ambe2_codeword_keystream(state, ambe_fr2, 1); processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0); processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
} }
else
if (state->csi_ee == 1)
{ {
csi72_ambe2_codeword_keystream(state, ambe_fr); processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio
csi72_ambe2_codeword_keystream(state, ambe_fr2); processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
csi72_ambe2_codeword_keystream(state, ambe_fr3); processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
ambe2_codeword_print_i(opts, ambe_fr3);
#endif
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
//TODO: Consider copying f_l to f_r for left and right channel saturation on MS mode
if (opts->floating_point == 0)
{
// memcpy (state->s_r4, state->s_l4, sizeof(state->s_l4));
if(opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceSS3(opts, state);
}
if (opts->floating_point == 1)
{
// memcpy (state->f_r4, state->f_l4, sizeof(state->f_l4));
if(opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceFS3(opts, state);
} }
//collect the mi fragment //collect the mi fragment
if (opts->dmr_le != 2) //if not Hytera Enhanced dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
//errors due to skipping other slot //errors due to skipping other slot
// cach_err = dmr_cach (opts, state, cachdata); // cach_err = dmr_cach (opts, state, cachdata);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
skipDibit (opts, state, 144); //skip to next TDMA slot skipDibit (opts, state, 144); //skip to next TDMA slot
dmrMS (opts, state); //bootstrap into full TDMA frame dmrMS (opts, state); //bootstrap into full TDMA frame
@@ -642,13 +600,28 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
//simplied to a simple data collector, and then passed on to dmr_data_sync for the usual processing //simplied to a simple data collector, and then passed on to dmr_data_sync for the usual processing
void dmrMSData (dsd_opts * opts, dsd_state * state) void dmrMSData (dsd_opts * opts, dsd_state * state)
{ {
int i, b, c;
char * timestr = getTimeC();
int i;
int dibit; int dibit;
int *dibit_p; int *dibit_p;
//add time to mirror sync
time_t now;
char * getTime(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;
}
//CACH + First Half Payload + Sync = 12 + 54 + 24 //CACH + First Half Payload + Sync = 12 + 54 + 24
dibit_p = state->dmr_payload_p - 90; dibit_p = state->dmr_payload_p - 90;
for (i = 0; i < 90; i++) //90 for (i = 0; i < 90; i++) //90
@@ -657,6 +630,7 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
dibit_p++; dibit_p++;
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3; if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
state->dmr_stereo_payload[i] = dibit; state->dmr_stereo_payload[i] = dibit;
} }
for (i = 0; i < 54; i++) for (i = 0; i < 54; i++)
@@ -664,17 +638,16 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
dibit = getDibit(opts, state); dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3; if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
state->dmr_stereo_payload[i+90] = dibit; state->dmr_stereo_payload[i+90] = dibit;
} }
fprintf (stderr, "%s ", timestr); fprintf (stderr, "%s ", getTime());
if (opts->inverted_dmr == 0) if (opts->inverted_dmr == 0)
{ {
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO "); fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
} }
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO "); else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
if (state->dmr_color_code != 16)
fprintf (stderr, "| Color Code=%02d ", state->dmr_color_code);
else fprintf (stderr, "| Color Code=XX ");
sprintf(state->slot1light, "%s", ""); sprintf(state->slot1light, "%s", "");
sprintf(state->slot2light, "%s", ""); sprintf(state->slot2light, "%s", "");
@@ -690,23 +663,16 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
state->directmode = 0; //flag off state->directmode = 0; //flag off
//should just be loaded in the dmr_payload_buffer instead now //should just be loaded in the dmr_payload_buffer instead now
//but we want to run getDibit so the buffer has actual good values in it
for (i = 0; i < 144; i++) //66 for (i = 0; i < 144; i++) //66
{ {
dibit = getDibit(opts, state); dibit = getDibit(opts, state);
state->dmr_stereo_payload[i] = 1; //set to one so first frame will fail intentionally instead of zero fill state->dmr_stereo_payload[i] = dibit;
} }
//CACH + First Half Payload = 12 + 54 //CACH + First Half Payload = 12 + 54
for (i = 0; i < 66; i++) //66 for (i = 0; i < 66; i++) //66
{ {
dibit = getDibit(opts, state); dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+66] = 1; ////set to one so first frame will fail intentionally instead of zero fill state->dmr_stereo_payload[i] = dibit;
}
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -7,283 +7,75 @@
* LWVMOBILE * LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition * 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
#include "dsd.h" #include "dsd.h"
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors) void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
{ {
uint8_t MFID = PI_BYTE[1]; if((IrrecoverableErrors == 0))
if(IrrecoverableErrors == 0)
{ {
//update cc amd vc sync time for trunking purposes (particularly Con+) if (state->currentslot == 0)
if (opts->p25_is_tuned == 1)
{ {
state->last_vc_sync_time = time(NULL); state->payload_algid = PI_BYTE[0];
state->last_cc_sync_time = time(NULL); 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 (MFID == 0x0A && CRCCorrect == 1) //Kirisun if (state->currentslot == 1)
{ {
uint8_t so = PI_BYTE[2]; //in VLC and PI, this byte is 0x40, so thinking this could be SVC_OPT
uint8_t alg = PI_BYTE[0]; //observed 0x36 and 0x37 for Kirisun Advanced and Universal Privacy
// uint8_t kid = PI_BYTE[2]; //user info conveyed there is only 16 allotments for Key, and keys are channel specific, so no key id value
uint32_t target = ((unsigned long long int)PI_BYTE[7] << 16) | ((unsigned long long int)PI_BYTE[8] << 8) | ((unsigned long long int)PI_BYTE[9] << 0);
//TODO: Use ALG and TGT value here to produce a key id via hashing it state->payload_algidR = PI_BYTE[0];
uint8_t hash = alg * target % 256; //more complex hash later (honestly, probably not) state->payload_keyidR = PI_BYTE[2];
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
//MI only appears to be 32-bit if (state->payload_algidR < 0x26)
uint32_t mi = ((uint32_t)PI_BYTE[3] << 24) | ((uint32_t)PI_BYTE[4] << 16) |
((uint32_t)PI_BYTE[5] << 8) | ((uint32_t)PI_BYTE[6] << 0);
if (state->currentslot == 0)
{ {
state->dmr_so = so; fprintf (stderr, "%s ", KYEL);
state->payload_algid = alg; fprintf (stderr, "\n Slot 2");
state->payload_keyid = hash; 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);
state->payload_mi = mi; fprintf (stderr, "%s ", KNRM);
} if (state->payload_algidR != 0x21)
else {
{ fprintf (stderr, "\n");
state->dmr_soR = so; LFSR64 (state);
state->payload_algidR = alg; }
state->payload_keyidR = hash;
state->payload_miR = mi;
} }
fprintf (stderr, "%s ", KYEL); if (state->payload_algidR >= 0x26)
fprintf (stderr, "\n Slot %d", state->currentslot+1); {
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08X;", alg, hash, mi); state->payload_algidR = 0;
state->payload_keyidR = 0;
fprintf (stderr, " Kirisun "); state->payload_miR = 0;
if (alg == 0x36) }
fprintf (stderr, "Advanced;");
else if (alg == 0x37)
fprintf (stderr, "Universal;");
else fprintf (stderr, "Encryption;");
fprintf (stderr, "%s", KNRM);
//disable late entry for DMRA, Flag 3 for future check of VC-F 48-bit region with Golay 24,12 Encoding
//SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun
//tested patent above with information provided, VC-F had a SB, but the 48-bit
//full value yielded bad Golay results, and user submitted info contradicts this patent for samples provided
//Late Entry MI does appear to work, however, and reports a good CRC for that as well,
//reverse engineered LFSR for Kirisun lines up with the late entry for 32-bit MI values.
opts->dmr_le = 3;
} }
if (MFID == 0x68) //Hytera Enhanced
{
if (state->currentslot == 0)
{
state->dmr_so |= 0x40; //OR the enc bit onto the SO
state->payload_algid = PI_BYTE[0];
state->payload_keyid = PI_BYTE[2];
state->payload_mi = ((unsigned long long int)PI_BYTE[3] << 32ULL) | ((unsigned long long int)PI_BYTE[4] << 24) |
((unsigned long long int)PI_BYTE[5] << 16) | ((unsigned long long int)PI_BYTE[6] << 8) | ((unsigned long long int)PI_BYTE[7] << 0);
}
else
{
state->dmr_soR |= 0x40; //OR the enc bit onto the SO
state->payload_algidR = PI_BYTE[0];
state->payload_keyidR = PI_BYTE[2];
state->payload_miR = ((unsigned long long int)PI_BYTE[3] << 32ULL) | ((unsigned long long int)PI_BYTE[4] << 24) |
((unsigned long long int)PI_BYTE[5] << 16) | ((unsigned long long int)PI_BYTE[6] << 8) | ((unsigned long long int)PI_BYTE[7] << 0);
}
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot %d", state->currentslot+1);
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(40): %02X%02X%02X%02X%02X;",
PI_BYTE[0], PI_BYTE[2], PI_BYTE[3], PI_BYTE[4], PI_BYTE[5], PI_BYTE[6], PI_BYTE[7]);
//PI_BYTE[9] is a checksum of the other bytes combined
uint8_t checksum = 0;
for (int i = 0; i < 9; i++)
{
checksum += PI_BYTE[i];
checksum &= 0xFF;
}
checksum = ~checksum & 0xFF;
checksum++;
//debug
// fprintf (stderr, " CHK: %02X / %02X;", checksum, PI_BYTE[9]);
if (checksum == PI_BYTE[9])
{
fprintf (stderr, " Hytera Enhanced; ");
if (state->currentslot == 0 && state->R != 0)
fprintf (stderr, "Key: %010llX; ", state->R);
if (state->currentslot == 1 && state->RR != 0)
fprintf (stderr, "Key: %010llX; ", state->RR);
//disable late entry for DMRA (hopefully, there aren't any systems running both DMRA and Hytera Enhanced mixed together)
opts->dmr_le = 2;
// fprintf (stderr, " (Checksum Okay);");
}
else
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (Checksum Err);");
}
fprintf (stderr, "%s", KNRM);
}
else if (MFID == 0x10) //DMRA
{
if (state->currentslot == 0)
{
state->payload_algid = PI_BYTE[0];
state->payload_keyid = PI_BYTE[2];
state->payload_mi = ((unsigned long long int)PI_BYTE[3] << 24ULL) | ((unsigned long long int)PI_BYTE[4] << 16ULL) | ((unsigned long long int)PI_BYTE[5] << 8ULL) | ((unsigned long long int)PI_BYTE[6] << 0ULL);
if (state->payload_algid < 0x26)
{
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot 1");
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08llX;", state->payload_algid, state->payload_keyid, state->payload_mi);
//check for any values that aren't 0x2X but just 0x0X
//going to be very generic here to avoid any particular vendors using 0x2X and not 0x0X
if (state->payload_algid & 0x20)
fprintf (stderr, " DMRA");
else fprintf (stderr, " DMRA Compatible");
if ((state->payload_algid & 0x07) == 0x01)
{
fprintf (stderr, " RC4;");
state->payload_algid = 0x21;
}
else if ((state->payload_algid & 0x07) == 0x02)
{
fprintf (stderr, " DES;");
state->payload_algid = 0x22;
}
else if ((state->payload_algid & 0x07) == 0x04)
{
fprintf (stderr, " AES-128;");
state->payload_algid = 0x24;
}
else if ((state->payload_algid & 0x07) == 0x05)
{
fprintf (stderr, " AES-256;");
state->payload_algid = 0x25;
}
fprintf (stderr, "%s ", KNRM);
//expand the 32-bit MI to a 64-bit DES IV
if (state->payload_algid == 0x22)
{
fprintf (stderr, "\n");
LFSR64 (state);
}
//expand the 32-bit MI to a 128-bit AES IV
if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{
fprintf (stderr, "\n");
LFSR128d (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 = ((unsigned long long int)PI_BYTE[3] << 24ULL) | ((unsigned long long int)PI_BYTE[4] << 16ULL) | ((unsigned long long int)PI_BYTE[5] << 8ULL) | ((unsigned long long int)PI_BYTE[6] << 0ULL);
if (state->payload_algidR < 0x26)
{
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot 2");
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08llX;", state->payload_algidR, state->payload_keyidR, state->payload_miR);
//check for any values that aren't 0x2X but just 0x0X
//going to be very generic here to avoid any particular vendors using 0x2X and not 0x0X
if (state->payload_algidR & 0x20)
fprintf (stderr, " DMRA");
else fprintf (stderr, " DMRA Compatible");
if ((state->payload_algidR & 0x07) == 0x01)
{
fprintf (stderr, " RC4;");
state->payload_algidR = 0x21;
}
else if ((state->payload_algidR & 0x07) == 0x02)
{
fprintf (stderr, " DES;");
state->payload_algidR = 0x22;
}
else if ((state->payload_algidR & 0x07) == 0x04)
{
fprintf (stderr, " AES-128;");
state->payload_algidR = 0x24;
}
else if ((state->payload_algidR & 0x07) == 0x05)
{
fprintf (stderr, " AES-256;");
state->payload_algidR = 0x25;
}
fprintf (stderr, "%s ", KNRM);
//expand the 32-bit MI to a 64-bit DES IV
if (state->payload_algidR == 0x22)
{
fprintf (stderr, "\n");
LFSR64 (state);
}
//expand the 32-bit MI to a 128-bit AES IV
if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{
fprintf (stderr, "\n");
LFSR128d (state);
}
}
if (state->payload_algidR >= 0x26)
{
state->payload_algidR = 0;
state->payload_keyidR = 0;
state->payload_miR = 0;
}
}
} //end DMRA
} }
} }
void LFSR(dsd_state * state) void LFSR(dsd_state * state)
{ {
unsigned long long int lfsr = 0; int lfsr = 0;
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
lfsr = state->payload_mi; lfsr = state->payload_mi;
@@ -295,263 +87,70 @@ void LFSR(dsd_state * state)
for(cnt=0;cnt<32;cnt++) for(cnt=0;cnt<32;cnt++)
{ {
// Polynomial is C(x) = x^32 + x^4 + x^2 + 1 // Polynomial is C(x) = x^32 + x^4 + x^2 + 1
unsigned long long int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 0x1; int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 0x1;
lfsr = (lfsr << 1) | bit; lfsr = (lfsr << 1) | (bit);
} }
lfsr &= 0xFFFFFFFF;
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1"); fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid); fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
fprintf (stderr, " MI(32): %08llX;", lfsr); fprintf(stderr, " MI: 0x%08X", lfsr);
fprintf (stderr, " RC4;");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->payload_mi = lfsr; state->payload_mi = lfsr;
} }
if (state->currentslot == 1) if (state->currentslot == 1)
{ {
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2"); fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR); fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
fprintf(stderr, " MI(32): %08llX;", lfsr); fprintf(stderr, " MI: 0x%08X", lfsr);
fprintf (stderr, " RC4;");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->payload_miR = lfsr; state->payload_miR = lfsr;
} }
} }
//Expand a 32-bit MI into a 64-bit IV for DES
void LFSR64(dsd_state * state) void LFSR64(dsd_state * state)
{ {
{ {
unsigned long long int lfsr = 0; unsigned long long int lfsr = 0;
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
lfsr = (uint64_t) state->payload_mi; lfsr = (uint64_t) state->payload_mi;
} }
else lfsr = (uint64_t) state->payload_miR; else lfsr = (uint64_t) state->payload_miR;
uint8_t cnt = 0; uint8_t cnt = 0;
for(cnt=0;cnt<32;cnt++) for(cnt=0;cnt<32;cnt++)
{ {
unsigned long long int bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1; unsigned long long int bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
lfsr = (lfsr << 1) | bit; lfsr = (lfsr << 1) | bit;
} }
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1"); fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid); fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
fprintf (stderr, " MI(64): %016llX;", lfsr); fprintf (stderr, " MI: 0x%016llX", lfsr);
fprintf (stderr, " DES;");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->payload_mi = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification state->payload_mi = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
state->payload_miP = lfsr; }
state->DMRvcL = 0;
}
if (state->currentslot == 1) if (state->currentslot == 1)
{ {
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2"); fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR); fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
fprintf (stderr, " MI(64): %016llX;", lfsr); fprintf (stderr, " MI: 0x%016llX", lfsr);
fprintf (stderr, " DES;");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->payload_miR = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification state->payload_miR = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
state->payload_miN = lfsr; }
state->DMRvcR = 0;
}
} }
} }
//Expand a 32-bit MI into a 128-bit IV for AES
void LFSR128d(dsd_state * state)
{
unsigned long long int lfsr = 0;
if (state->currentslot == 0)
lfsr = state->payload_mi;
else lfsr = state->payload_miR;
unsigned long long int next_mi;
//start packing aes_iv
if (state->currentslot == 0)
{
state->aes_iv[0] = (lfsr >> 24) & 0xFF;
state->aes_iv[1] = (lfsr >> 16) & 0xFF;
state->aes_iv[2] = (lfsr >> 8 ) & 0xFF;
state->aes_iv[3] = (lfsr >> 0 ) & 0xFF;
}
else if (state->currentslot == 1)
{
state->aes_ivR[0] = (lfsr >> 24) & 0xFF;
state->aes_ivR[1] = (lfsr >> 16) & 0xFF;
state->aes_ivR[2] = (lfsr >> 8 ) & 0xFF;
state->aes_ivR[3] = (lfsr >> 0 ) & 0xFF;
}
int cnt = 0; int x = 32;
unsigned long long int bit;
for(cnt=0;cnt<96;cnt++)
{
//32,22,2,1
bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
lfsr = (lfsr << 1) | bit;
//continue packing aes_iv
if (state->currentslot == 0)
state->aes_iv[x/8] = (state->aes_iv[x/8] << 1) + bit;
else if (state->currentslot == 1)
state->aes_ivR[x/8] = (state->aes_ivR[x/8] << 1) + bit;
x++;
}
//assign the next 32-bit short MI from 4,5,6,7 so it'll match up with OTA late entry
if (state->currentslot == 0)
next_mi = ((unsigned long long int)state->aes_iv[4] << 24) | ((unsigned long long int)state->aes_iv[5] << 16) |
((unsigned long long int)state->aes_iv[6] << 8) | ((unsigned long long int)state->aes_iv[7] << 0);
if (state->currentslot == 1)
next_mi = ((unsigned long long int)state->aes_ivR[4] << 24) | ((unsigned long long int)state->aes_ivR[5] << 16) |
((unsigned long long int)state->aes_ivR[6] << 8) | ((unsigned long long int)state->aes_ivR[7] << 0);
if (state->currentslot == 0)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X; MI(128): ", state->payload_algid, state->payload_keyid);
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", state->aes_iv[x]);
fprintf (stderr, "%s", KNRM);
fprintf (stderr, ";");
if (state->payload_algid == 0x24)
fprintf (stderr, " AES-128;");
else fprintf (stderr, " AES-256;");
state->payload_mi = next_mi;
state->DMRvcL = 0;
}
if (state->currentslot == 1)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X; MI(128): ", state->payload_algidR, state->payload_keyidR);
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", state->aes_ivR[x]);
fprintf (stderr, "%s", KNRM);
fprintf (stderr, ";");
if (state->payload_algidR == 0x24)
fprintf (stderr, " AES-128;");
else fprintf (stderr, " AES-256;");
state->payload_miR = next_mi;
state->DMRvcR = 0;
}
}
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len)
{
for (uint8_t i = 0; i < len; i++)
{
uint8_t bit = (mi[i] >> 7) & 1;
mi[i] <<= 1;
if (bit) mi[i] ^= taps[i%len];
mi[i] |= bit;
}
unsigned long long int mi_value = 0;
for (uint8_t i = 0; i < len; i++)
{
mi_value <<= 8;
mi_value |= mi[i];
}
//debug
// fprintf (stderr, " Next MI: %010llX \n", mi_value);
return mi_value;
}
void hytera_enhanced_alg_refresh(dsd_state * state)
{
uint8_t mi[5]; memset (mi, 0, sizeof(mi));
unsigned long long int mi_value = 0;
if (state->currentslot == 0)
mi_value = state->payload_mi;
else mi_value = state->payload_miR;
//load mi_value into mi array
mi[0] = ((mi_value & 0xFF00000000) >> 32UL);
mi[1] = ((mi_value & 0xFF000000) >> 24);
mi[2] = ((mi_value & 0xFF0000) >> 16);
mi[3] = ((mi_value & 0xFF00) >> 8);
mi[4] = ((mi_value & 0xFF) >> 0);
//calculate the next MI value
uint8_t taps[5]; memset(taps, 0, sizeof(taps));
//the tap values
taps[0] = 0x12;
taps[1] = 0x24;
taps[2] = 0x48;
taps[3] = 0x22;
taps[4] = 0x14;
mi_value = hytera_lfsr(mi, taps, 5);
if (state->currentslot == 0)
state->payload_mi = mi_value;
else state->payload_miR = mi_value;
}
uint32_t kirisun_lfsr(unsigned long long int mi)
{
uint32_t taps = 0xD459C4F1;
uint32_t lfsr = (uint32_t)mi;
uint32_t new_mi = 0;
for (int i = 0; i < 4; i++)
{
uint8_t byte = 0;
for (int j = 0; j < 8; j++)
{
uint32_t temp = (lfsr << 1);
uint8_t msb = (lfsr >> 31) & 1;
if (msb)
{
byte |= (1 << j);
lfsr = temp ^ taps;
}
else lfsr = temp; //was previously temp ^ 1, but that isn't a primitive
}
new_mi <<=8;
new_mi |= byte;
}
return new_mi;
}

View File

@@ -10,7 +10,7 @@
#include "dsd.h" #include "dsd.h"
//modified to accept variable payload size and len //modified to accept variable payload size and len
uint16_t ComputeCrcCCITT16d(const uint8_t * buf, uint32_t len) uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len)
{ {
uint32_t i; uint32_t i;
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */ uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */

View File

@@ -53,10 +53,15 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
uint32_t CCH_EmergencyPriority[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; uint32_t CCH_EmergencyPriority[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
uint32_t CCH_Reserved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; uint32_t CCH_Reserved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
uint32_t CCH_SlowData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; uint32_t CCH_SlowData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
uint32_t *errs;
uint32_t *errs2;
uint8_t AmbeBitDescrambled[49] = {0};
uint32_t ScramblerKey = 0;
uint32_t PartOfSuperFrame = 0; uint32_t PartOfSuperFrame = 0;
uint32_t VoiceFrameFlag = 1; /* We consider the current frame as a voice frame */
uint32_t AttachedDataFlag = 0;
uint8_t CalledID[8] = {0}; uint8_t CalledID[8] = {0};
uint8_t CallingID[8] = {0}; uint8_t CallingID[8] = {0};
UNUSED(PartOfSuperFrame);
/* First CCH (Control CHannel) - 72 bit */ /* First CCH (Control CHannel) - 72 bit */
k = 0; k = 0;
@@ -376,7 +381,7 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
//check other as well before assigning //check other as well before assigning
if(state->dPMRVoiceFS2Frame.CalledIDOk) sprintf (state->dpmr_target_id, "%s", CalledID); if(state->dPMRVoiceFS2Frame.CallingIDOk) sprintf (state->dpmr_target_id, "%s", CalledID);
} }
else else
@@ -412,7 +417,7 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
{ {
fprintf (stderr, "%s", KRED); fprintf (stderr, "%s", KRED);
fprintf(stderr, " Src=(CRC ERR) Channel Code =(CRC ERR)"); fprintf(stderr, " Src=(CRC ERR) Channel Code =(CRC ERR)");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
if (state->dPMRVoiceFS2Frame.Version[0] == 3) if (state->dPMRVoiceFS2Frame.Version[0] == 3)
@@ -439,7 +444,7 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
//reset scrambler key seed on frame 0 //reset scrambler key seed on frame 0
if (state->dPMRVoiceFS2Frame.FrameNumbering[o] == 0) state->payload_miN = 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)
{ {
@@ -448,14 +453,14 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
} }
//this is used so we can simply pass the voice here to the //this is used so we can simply pass the voice here to the
//nxdn scrambler by telling mbe these are nxdn frames and to //nxdn scrambler by telling mbe these are nxdn frames and to
//apply the same descramble method to them if necessary //apply the same descramble method to them if necessary
int realsynctype = state->synctype; int realsynctype = state->synctype;
// fprintf (stderr, "\nCommunication Mode: %d\n", state->dPMRVoiceFS2Frame.CommunicationMode[o]); // 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, "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]); // 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) ||
@@ -469,30 +474,22 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
state->nxdn_cipher_type = 0x01; //turn on nxdn scrambler cipher 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 state->dmr_encL = 1; //flag on enc bit here so we can mute if no key provided
} }
//flag back off if key is provided //flag back off if key is provided
if (state->R != 0) state->dmr_encL = 0; if (state->R != 0) state->dmr_encL = 0;
//print Current Frame Number if payload verbosity enabled
if (opts->payload == 1)
fprintf (stderr, "\n FN %d/4", state->dPMRVoiceFS2Frame.FrameNumbering[o]+1);
//There are 4 AMBE voice frames per TCH //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);
if (opts->floating_point == 0) }
playSynthesizedVoiceMS(opts, state);
if (opts->floating_point == 1)
playSynthesizedVoiceFM(opts, state);
}
//set the correct sync type again and flag off the cipher //set the correct sync type again and flag off the cipher
state->synctype = realsynctype; state->synctype = realsynctype;
state->nxdn_cipher_type = 0; state->nxdn_cipher_type = 0;
} }
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");

View File

@@ -1,825 +0,0 @@
/*-------------------------------------------------------------------------------
* dsd_alias.c
* Talker Alias Handling for Various Protocols and Vendors
*
* LWVMOBILE
* 2025-02 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//Motorola P25 OTA Alias Decoding ripped/demystified from Ilya Smirnov's SDRTrunk Voodoo Code
uint8_t moto_alias_lut[256] = {
0xD2, 0xF6, 0xD4, 0x2B, 0x63, 0x49, 0x94, 0x5E, 0xA7, 0x5C, 0x70, 0x69, 0xF7, 0x08, 0xB1, 0x7D,
0x38, 0xCF, 0xCC, 0xD8, 0x51, 0x8F, 0xD5, 0x93, 0x6A, 0xF3, 0xEF, 0x7E, 0xFB, 0x64, 0xF4, 0x35,
0x27, 0x07, 0x31, 0x14, 0x87, 0x98, 0x76, 0x34, 0xCA, 0x92, 0x33, 0x1B, 0x4F, 0x8C, 0x09, 0x40,
0x32, 0x36, 0x77, 0x12, 0xD3, 0xC3, 0x01, 0xAB, 0x72, 0x81, 0x95, 0xC9, 0xC0, 0xE9, 0x65, 0x52,
0x24, 0x30, 0x1C, 0xDB, 0x88, 0xE8, 0x97, 0x9D, 0x58, 0x26, 0x04, 0x39, 0xAC, 0x2A, 0x9E, 0xAA,
0x25, 0xD7, 0xCE, 0xEB, 0x96, 0xF5, 0x0E, 0x8D, 0xDC, 0xA9, 0x2F, 0xDD, 0x1F, 0xEA, 0x91, 0xB7,
0xD6, 0x89, 0x8B, 0xD1, 0xB0, 0x99, 0x13, 0x7A, 0xE7, 0x9A, 0xB5, 0x86, 0xFF, 0x46, 0x85, 0xB2,
0x73, 0xDA, 0xBF, 0xD0, 0x71, 0xCB, 0x4D, 0x80, 0x15, 0x67, 0x16, 0x1A, 0x20, 0x8E, 0x45, 0x3E,
0xF2, 0x2E, 0x66, 0x90, 0x74, 0x8A, 0x6F, 0x78, 0xBB, 0x53, 0x03, 0x11, 0x68, 0xCD, 0x44, 0x17,
0x28, 0x5F, 0x1E, 0x84, 0x75, 0x79, 0x6E, 0x9B, 0x2C, 0xBE, 0x62, 0x2D, 0xF1, 0x7C, 0xB8, 0x83,
0xD9, 0x4E, 0x6D, 0x02, 0x61, 0x3D, 0xA8, 0x06, 0xB9, 0xF8, 0x9C, 0x37, 0x3A, 0x23, 0xC1, 0x50,
0xED, 0x9F, 0xAF, 0x3B, 0xBD, 0x82, 0xBA, 0xA0, 0xDF, 0xC2, 0x47, 0x22, 0xF0, 0xEE, 0xA1, 0xFE,
0xA2, 0x10, 0x5B, 0x48, 0x57, 0xA3, 0x05, 0x60, 0x7B, 0x0D, 0xF9, 0x6C, 0xB3, 0x56, 0x4C, 0xBC,
0x29, 0xA4, 0x0F, 0xEC, 0xB6, 0xA5, 0xA6, 0x3C, 0x7F, 0x6B, 0xB4, 0x21, 0xAD, 0xAE, 0xC4, 0xC8,
0xC5, 0x5D, 0xDE, 0xE0, 0x1D, 0x19, 0x4B, 0xC6, 0x0C, 0x3F, 0x5A, 0xC7, 0xE1, 0x59, 0x55, 0x54,
0x4A, 0x43, 0x42, 0xE2, 0xE3, 0xFA, 0x00, 0xE4, 0xE5, 0x18, 0x41, 0x0B, 0x0A, 0xE6, 0xFC, 0xFD
};
void apx_embedded_alias_test_phase1 (dsd_opts * opts, dsd_state * state)
{
uint64_t temp_a = 0x15902D2806010005; uint64_t temp_b = 0xDE; //72 bits, so break into segments
uint8_t lcw[72]; memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x1790010BEE0740F0; temp_b = 0x4E;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x17900200DD2D2168; temp_b = 0x1A;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x17900301B52FFBFB; temp_b = 0xFE;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x1790040E53FE86BE; temp_b = 0xF7;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x17900508FD5AB910; temp_b = 0xB2;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x1790060376F9D800; temp_b = 0x00;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
//Harris Phase 1 GPS
state->lastsrc = 1000;
temp_a = 0x2AA41D4C24262328; temp_b = 0xAF;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x2BA44E0DB2660108; temp_b = 0x14;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
state->lastsrc = 0;
}
void apx_embedded_alias_header_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
UNUSED(opts);
uint8_t ta_len = (uint8_t)ConvertBitIntoBytes(&lc_bits[32], 8); //len in blocks of associated talker alias
uint8_t sn = (uint8_t)ConvertBitIntoBytes(&lc_bits[56], 4);
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " BN: 0/%d;", ta_len);
//use dmr_pdu_sf for storage, store entire header (will be used to verify complete reception of full alias)
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot])); //reset storage for header and blocks
memcpy (state->dmr_pdu_sf[slot], lc_bits, 72*sizeof(uint8_t));
}
void apx_embedded_alias_blocks_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
UNUSED(opts);
uint8_t bn = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8); //current block number
uint8_t sn = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 4); //is a static value on all block sequences
uint8_t ta_len = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][32], 8); //len in blocks pulled from stored header
uint16_t header = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][0], 16); //header check, should be 0x1590
if (ta_len == 0 || header != 0x1590) //checkdown, make sure we have an up to date header for this with a good len value
{
fprintf (stderr, " Missing Header");
fprintf (stderr, " BN: %d/??;", bn);
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " Partial: ");
for (uint8_t i = 7; i < 18; i++)
fprintf (stderr, "%0X", (uint8_t)ConvertBitIntoBytes(&lc_bits[0+(i*4)], 4));
//clear out now stale storage
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot]));
}
else //good len and header stored
{
//sanity check, bn cannot equal zero (this shouldn't happen, but bad decode could occur)
if (bn == 0) bn = 1;
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " BN: %d/%d;", bn, ta_len);
//use dmr_pdu_sf for storage, store data relevant portion at ptr of (bn-1) * 44 + 72 offset for header
memcpy(state->dmr_pdu_sf[slot]+(((bn-1)*44)+72), lc_bits+28, 44*sizeof(uint8_t));
if (ta_len == bn) //this is the last block, proceed to decoding
{
//Calculate variable len bits determined by non-zero octets
int16_t num_bits = 56; //starting value is the static bit count on the fqsuid
// //evaluate the storage and determine how many octets/bits are present at this point (fall back to this if needed)
// for (int16_t i = 0; i < 368; i++) //(3072-128)/8
// {
// uint8_t byte = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][72+56+(i*8)], 8);
// if (byte == 0)
// break;
// else
// num_bits += 8;
// }
//evaluate the storage and determine how many octets/bits are present at this point (expanded to two octets each, CRC with a 00xx pattern failed this)
for (int16_t i = 0; i < 184; i++) //(3072-128)/16
{
uint16_t bytes = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][72+56+(i*16)], 16);
if (bytes == 0)
break;
else
num_bits += 16;
}
//pass to alias decoder
apx_embedded_alias_decode (opts, state, slot, num_bits, state->dmr_pdu_sf[slot]);
//clear out now stale storage
memset (state->dmr_pdu_sf[slot], 0, sizeof(state->dmr_pdu_sf[slot]));
}
}
}
void apx_embedded_alias_header_phase2 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
UNUSED(opts);
uint8_t ta_len = (uint8_t)ConvertBitIntoBytes(&lc_bits[40], 8);
uint8_t sn = (uint8_t)ConvertBitIntoBytes(&lc_bits[64], 4);
uint8_t bn = (uint8_t)ConvertBitIntoBytes(&lc_bits[56], 8);
fprintf (stderr, " SN: %X;", sn); //NOTE: vPDU header is also a partial block, and has a block num and SN value in it
fprintf (stderr, " BN: %d/%d;", bn, ta_len);
//bit array to rearrange input lc_bits from phase 2 input to match the phase 1 header and block handling
uint8_t bits[136]; memset(bits, 0, sizeof(bits));
memcpy (bits, lc_bits, 2*8*sizeof(uint8_t)); //header 0x9190
memcpy (bits+16, lc_bits+24, 4*8*sizeof(uint8_t)); //BN, SN, etc
memcpy (bits+56, lc_bits+56, 10*8*sizeof(uint8_t)); //adding 8 bits of extra padding here
int16_t alias_st = 136; //start of the encoded alias (the copied size of this header, basically)
//debug, dump header arranged at this end
// fprintf (stderr, " Header: ");
// for (int16_t i = 0; i < alias_st/8; i++) //double check and adjust
// fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(&bits[0+(i*8)], 8));
//use dmr_pdu_sf for storage, store entire header (will be used to verify complete reception of full alias)
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot])); //reset storage for header and blocks
memcpy (state->dmr_pdu_sf[slot], bits, alias_st*sizeof(uint8_t)); //this header block has 128 bits of relevant data (through the fqsuid)
}
void apx_embedded_alias_blocks_phase2 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
UNUSED(opts);
int16_t rel_bits = 100; //number of relevant bits in each block
int16_t rel_st = 36; //start of relevant bits in this block
int16_t alias_st = 136; //start of the encoded alias
uint8_t bn = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 8); //current block number
uint8_t sn = (uint8_t)ConvertBitIntoBytes(&lc_bits[32], 4); //is a static value on all block sequences
uint8_t ta_len = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][32], 8); //len in blocks pulled from stored header
uint16_t header = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][0], 16); //header check, should be 0x9190
if (ta_len == 0 || header != 0x9190)
{
fprintf (stderr, " Missing Header");
fprintf (stderr, " BN: %d/??;", bn);
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " Partial: ");
for (uint8_t i = 9; i < 32; i++) //double check and adjust
fprintf (stderr, "%0X", (uint8_t)ConvertBitIntoBytes(&lc_bits[0+(i*4)], 4));
//clear out now stale storage
memset (state->dmr_pdu_sf[slot], 0, sizeof(state->dmr_pdu_sf[slot]));
}
else //good len and header stored
{
//sanity check, bn cannot equal zero (this shouldn't happen, but bad decode could occur)
if (bn == 0) bn = 1;
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " BN: %d/%d;", bn, ta_len);
//use dmr_pdu_sf for storage, store data relevant portion at ptr calculated below
memcpy(state->dmr_pdu_sf[slot]+(alias_st+((bn-1)*rel_bits)), lc_bits+rel_st, rel_bits*sizeof(uint8_t)); //Fix this value when samples arrive
//debug, dump accumulated data at this end
// fprintf (stderr, " Accumulated: ");
// for (int16_t i = 0; i < (alias_st+(rel_bits*bn))/8; i++) //double check and adjust
// fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][0+(i*8)], 8));
if (ta_len == bn) //this is the last block, proceed to decoding
{
//Calculate variable len bits determined by non-zero octets
int16_t num_bits = 56; //starting value is the static bit count on the fqsuid
// //evaluate the storage and determine how many octets/bits are present at this point (fall back to this if needed)
// for (int16_t i = 0; i < 368; i++) //(3072-128)/8
// {
// uint8_t byte = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][72+56+(i*8)], 8);
// if (byte == 0)
// break;
// else
// num_bits += 8;
// }
//evaluate the storage and determine how many octets/bits are present at this point (expanded to two octets each, CRC with a 00xx pattern failed this)
for (int16_t i = 0; i < 184; i++) //(3072-128)/16
{
uint16_t bytes = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][72+56+(i*16)], 16);
if (bytes == 0)
break;
else
num_bits += 16;
}
//pass to alias decoder
apx_embedded_alias_decode (opts, state, slot, num_bits, state->dmr_pdu_sf[slot]);
//clear out now stale storage
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot]));
}
}
}
void apx_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t num_bits, uint8_t * input)
{
UNUSED(opts);
UNUSED(state);
UNUSED(slot);
//debug, dump completed data set
// fprintf (stderr, "\n Full: ");
// for (int16_t i = 0; i < (72+num_bits)/4; i++)
// fprintf (stderr, "%X", (uint8_t)ConvertBitIntoBytes(&input[0+(i*4)], 4));
//extract CRC
uint16_t crc_ext = (uint16_t)ConvertBitIntoBytes(&input[(72+num_bits-16)], 16);
//compute CRC
uint16_t crc_cmp = ComputeCrcCCITT16d(&input[72], num_bits-16);
//print comparison
// fprintf (stderr, " CRC EXT: %04X CMP: %04X;", crc_ext, crc_cmp);
if (crc_ext != crc_cmp)
fprintf (stderr, " Alias CRC Error;");
// else fprintf (stderr, " Alias Okay;");
//start decoding the alias
if (crc_ext == crc_cmp)
{
//extract fully qualified SUID
uint32_t wacn = (uint32_t)ConvertBitIntoBytes(&input[72], 20);
uint32_t sys = (uint32_t)ConvertBitIntoBytes(&input[92], 12);
uint32_t rid = (uint32_t)ConvertBitIntoBytes(&input[104], 24);
//print fully qualified SUID
fprintf (stderr, "\n FQ-SUID: %05X.%03X.%06X (%d);", wacn, sys, rid, rid);
//WIP: Working, needs more samples to verify various num_bits values
uint16_t ptr = 128; //starting point of encoded alias
uint8_t encoded[200]; memset(encoded, 0, sizeof(encoded));
uint8_t decoded[200]; memset(decoded, 0, sizeof(decoded));
uint16_t num_bytes = (num_bits / 8) - 9; //subtract 2 CRC and 7 FQSUID
//sanity check
if (num_bytes <= 0) num_bytes = 1;
for (int16_t i = 0; i < num_bytes; i++)
{
encoded[i] = (uint8_t)ConvertBitIntoBytes(&input[ptr], 8);
ptr += 8;
}
uint16_t accumulator = num_bytes;
//Ilya's Voodoo Code
for (uint16_t i = 0; i < num_bytes; i++)
{
// Multiplication step 1
uint16_t accum_mult = accumulator * 293 + 0x72E9;
// Lookup table step
uint8_t lut = moto_alias_lut[encoded[i]];
uint8_t mult1 = lut - (accum_mult >> 8);
// Incrementing step
uint8_t mult2 = 1;
uint8_t shortstop = accum_mult | 0x1;
uint8_t increment = shortstop << 1;
//clang warning -- warning: result of comparison of constant -1 with expression of type 'uint8_t' (aka 'unsigned char') is always true [-Wtautological-constant-out-of-range-compare]
// while(mult2 != -1 && shortstop != 1) //clang warning can't be -1 if uint8_t (set to 255 instead?)
while(shortstop != 1) //this one tests out okay, so may use it instead
{
shortstop += increment;
mult2 += 2;
}
// Multiplication step 2
decoded[i] = mult1 * mult2;
// Update the accumulator
accumulator += encoded[i] + 1;
}
// //debug, dump just the encoded alias portion
// fprintf (stderr, "\n Encoded: ");
// for (int16_t i = 0; i < num_bytes; i++)
// fprintf (stderr, "%02X", encoded[i]);
// //debug, dump the decoded payload as hex octets
// fprintf (stderr, "\n Decoded: ");
// for (int16_t i = 0; i < num_bytes; i++)
// fprintf (stderr, "%02X", decoded[i]);
//dump the decoded payload as long chars
// fprintf (stderr, "\n");
fprintf (stderr, " Alias: ");
for (int16_t i = 0; i < num_bytes/2; i++)
fprintf (stderr, "%lc", ((decoded[(i*2)+0])<<8) | ((decoded[(i*2)+1])<<0) );
apx_embedded_alias_dump (opts, state, slot, num_bytes, input, decoded);
}
}
void apx_embedded_alias_dump (dsd_opts * opts, dsd_state * state, uint8_t slot, uint16_t num_bytes, uint8_t * input, uint8_t * decoded)
{
char str[50]; memset(str, 0, sizeof(str));
char fqs[50]; memset(fqs, 0, sizeof(fqs));
//check num_bytes, if greter than 100, then set to 100
if (num_bytes >= 98) num_bytes = 98;
for (int16_t i = 0; i < num_bytes/2; i++)
{
if (decoded[(i*2)+1] == 0x2C) //remove a comma if it exists, change it to a 0x2E dot
str[i] = 0x2E;
else if (decoded[(i*2)+1] > 0x1F && decoded[(i*2)+1] < 0x7F) //may not need or use this restriction
str[i] = decoded[(i*2)+1];
else str[i] = 0x20; //space
}
//flag to indicate this already exists in import or group struct
uint8_t wr = 0;
//fully qualified SUID
uint32_t wacn = (uint32_t)ConvertBitIntoBytes(&input[72], 20);
uint32_t sys = (uint32_t)ConvertBitIntoBytes(&input[92], 12);
uint32_t rid = (uint32_t)ConvertBitIntoBytes(&input[104], 24);
sprintf (fqs, " FQ-SUID: %05X:%03X.%06X (%d);", wacn, sys, rid, rid);
if (rid != 0 && state->event_history_s[slot].Event_History_Items[0].source_id == rid)
{
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s; ", str);
strcat (state->event_history_s[slot].Event_History_Items[0].alias, fqs);
}
for (int16_t i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == rid)
{
wr = 1; //already in there, so no need to assign it
break;
}
}
if (wr == 0) //not already in there, so save it there now
{
state->group_array[state->group_tally].groupNumber = rid;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "D");
sprintf (state->group_array[state->group_tally].groupName, "%s", str);
state->group_tally++;
//if we have an opened group file, let's write what info we found into it
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,D,", rid); //may want to not use this one
fprintf (pFile, "%s", str);
fprintf (pFile, ",FQS:%05X.%03X.%06X(%d),Moto\n", wacn, sys, rid, rid);
fclose (pFile);
}
}
}
//end Motorola P25 OTA Alias Decoding
//TODO: Migrate Other OTA Alias functions here
void l3h_embedded_alias_blocks_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
uint8_t op = (uint8_t)ConvertBitIntoBytes(&lc_bits[0], 8);
uint8_t ptr = op-0x32;
uint8_t bytes[7]; memset(bytes, 0, sizeof(bytes));
for (uint8_t i = 0; i < 7; i++)
bytes[i] = (uint8_t)ConvertBitIntoBytes(&lc_bits[16+(i*8)], 8);
//use +4 offset to match the MAC vPDU since that was already worked out long ago
memcpy(state->dmr_pdu_sf[slot]+4+(ptr*7), bytes, sizeof(bytes));
//to be tested
if (ptr == 4) //is there always 4 blocks, or is it a variable amount?
l3h_embedded_alias_decode(opts, state, slot, 4+28, state->dmr_pdu_sf[slot]);
}
void l3h_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t len, uint8_t * input)
{
//storage info for storing to groupName, if not available
char str[40]; memset (str, 0, sizeof(str));
char ttemp[40]; memset (ttemp, 0, sizeof(ttemp));
uint8_t wr = 0;
uint32_t tsrc = 0, ttg = 0;
if (slot == 0 && state->lastsrc != 0) tsrc = state->lastsrc;
if (slot == 1 && state->lastsrcR != 0) tsrc = state->lastsrcR;
if (slot == 0 && state->lasttg != 0) ttg = state->lasttg;
if (slot == 1 && state->lasttgR != 0) ttg = state->lasttgR;
int8_t ptr = 0;
if (tsrc != 0)
fprintf (stderr, " TG: %d; SRC: %d; Talker Alias: ", ttg, tsrc);
else fprintf (stderr, " TG: UNK; SRC: UNK; Talker Alias: ");
for (int8_t i = 4; i <= len; i++)
{
if ( (input[i] > 0x19) && (input[i] < 0x7F) )
fprintf (stderr, "%c", (char)input[i]);
else fprintf (stderr, " ");
if (input[i] == 0x2C) //remove a comma if it exists, change it to a 0x2E dot
ttemp[ptr] = 0x2E;
else if ( (input[i] > 0x19) && (input[i] < 0x7F) )
ttemp[ptr] = input[i];
else if (input[i] != 0)
ttemp[ptr] = 0x20; //space
ptr++;
}
//assign completed talker to a more useful string instead
snprintf (str, ptr+1, "%s", ttemp);
if (state->event_history_s[slot].Event_History_Items[0].source_id == tsrc && tsrc != 0)
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s", str);
//The Duke Energy system may relay two src values, may be a good idea to pick one and stick with it
if (tsrc != 0)
{
for (int16_t i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == tsrc)
{
wr = 1; //already in there, so no need to assign it
break;
}
}
if (wr == 0) //not already in there, so save it there now
{
state->group_array[state->group_tally].groupNumber = tsrc;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "D");
sprintf (state->group_array[state->group_tally].groupName, "%s", str);
state->group_tally++;
//if we have an opened group file, let's write what info we found into it
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,D,", tsrc);
fprintf (pFile, "%s", str);
fprintf (pFile, ",TG:%d,SYS:%03llX,RFSS:%lld,SITE:%lld,Harris\n", ttg, state->p2_sysid, state->p2_rfssid, state->p2_siteid);
fclose (pFile);
}
}
}
//debug
// fprintf (stderr, "\n WR: %d TG: %d SRC: %d Res: %d Len: %d STR: %s", wr, ttg, tsrc, res, len, str);
//reset storage
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot]));
}
void tait_iso7_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t len, uint8_t * input)
{
UNUSED(slot);
uint8_t alias[24]; memset(alias, 0, sizeof(alias));
for (uint8_t i = 0; i < len; i++)
{
alias[i] = (uint8_t)ConvertBitIntoBytes(&input[16+(i*7)], 7);
fprintf (stderr, "%c", alias[i]);
if (alias[i] == 0x2C) //change a comma to a dot
alias[i] = 0x2E;
else if (alias[i] < 0x20) //change any garble / control chars to a space
alias[i] = 0x20;
}
//flag to indicate this already exists in import or group struct
uint8_t wr = 0;
uint32_t rid = state->lastsrc;
uint16_t nac = state->nac;
if (state->event_history_s[slot].Event_History_Items[0].source_id == rid)
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s", alias);
if (rid != 0)
{
for (int16_t i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == rid)
{
wr = 1; //already in there, so no need to assign it
break;
}
}
if (wr == 0) //not already in there, so save it there now
{
state->group_array[state->group_tally].groupNumber = rid;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "D");
sprintf (state->group_array[state->group_tally].groupName, "%s", alias);
state->group_tally++;
//if we have an opened group file, let's write what info we found into it
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,D,", rid); //may want to not use this one
fprintf (pFile, "%s,", alias);
fprintf (pFile, "%03X,", nac); //if we find this on a trunking system, may want to add the site and rfss id
fprintf (pFile, "%s", ",Tait\n");
fclose (pFile);
}
}
}
}
void dmr_talker_alias_lc_header (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
uint8_t format = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 2);
uint8_t block_len = (uint8_t)ConvertBitIntoBytes(&lc_bits[18], 5);
uint8_t char_size = 0;
if (format == 0) char_size = 7;
else if (format == 1 || format == 2) char_size = 8;
else char_size = 16;
state->dmr_alias_format[slot] = format;
state->dmr_alias_block_len[slot] = block_len; //data len, see below
state->dmr_alias_char_size[slot] = char_size;
//load into dmr_pdu_sf as bit wise values for this since iso7 has 49 bits in this header, otherwise, load with 48?
if (char_size == 7)
memcpy(state->dmr_pdu_sf[slot], lc_bits+23, 49*sizeof(uint8_t));
else if (char_size == 8)
memcpy(state->dmr_pdu_sf[slot], lc_bits+24, 48*sizeof(uint8_t));
else if (char_size == 16)
memcpy(state->dmr_pdu_sf[slot], lc_bits+24, 48*sizeof(uint8_t));
//TEST: The Block Len (Data Lan) value is, according to my interpretation, the number of encoded
//character units in the alias, test to verify, but fall back to the ptr index if necessary
//this is referenced in 5.4.3 ETSI TS 102 361-2 V2.5.1 (2023-05) 7.2.19 Talker Alias Data Length
fprintf (stderr, " Slot %d - Talker Alias LC Header; Format %d; Char Len: %d; Char Size: %d;", slot, format, block_len, char_size);
//Decode the header's alias portion
fprintf (stderr, "\n");
dmr_talker_alias_lc_decode(opts, state, slot, 0, char_size, block_len);
}
void dmr_talker_alias_lc_blocks (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t block_num, uint8_t * lc_bits)
{
UNUSED(opts); //delete if we don't use this, but may want it if we dump alias to a file later on
uint8_t block_len = state->dmr_alias_block_len[slot];
uint8_t char_size = state->dmr_alias_char_size[slot];
uint16_t ptr = 0;
uint16_t end = block_len;
//Note: The Joann sample only carries block 5, and no header on block 4
//another radio on that ham setup also has a broken alias similar, but
//the other talkers have the 04 and the sample has a good decode otherwise
//debug broken aliasing on ham radio
// if (char_size == 0) //Enable this is you need to check alias even with missing header (set 7, 8, or 16)
// char_size = 8;
/*
22:54:51 Sync: +DMR slot1 [SLOT2] | Color Code=01 | VC6
Slot 1 - Talker Alias Block Num: 1; Valid Block; Talker Alias: Joann
DMR PDU Payload [05][00][4A][6F][61][6E][6E][00][FF]
*/
//set the pointer to the current index of the dmr_pdu_sf
if (char_size == 16)
ptr = 48 + (block_num * 56);
else if (char_size == 8)
ptr = 48 + (block_num * 56);
else if (char_size == 7)
ptr = 49 + (block_num * 56);
if (char_size == 0) //unset, no header received
fprintf (stderr, " Slot %d - Talker Alias Block Num: %d; Invalid Header;", slot, block_num+1);
else if (block_num > 3) //invalid block (data error)
fprintf (stderr, " Slot %d - Talker Alias Block Num: %d; Invalid Block;", slot, block_num+1);
else //valid header present, continue
{
if (char_size == 7)
{
memcpy(state->dmr_pdu_sf[slot]+ptr, lc_bits+16, 56*sizeof(uint8_t));
ptr += 56;
if (block_len == 0)
end = ptr / 7;
else end = block_len;
}
else if (char_size == 8)
{
memcpy(state->dmr_pdu_sf[slot]+ptr, lc_bits+16, 56*sizeof(uint8_t));
ptr += 56;
if (block_len == 0)
end = ptr / 8;
else end = block_len;
}
else if (char_size == 16)
{
memcpy(state->dmr_pdu_sf[slot]+ptr, lc_bits+16, 56*sizeof(uint8_t));
ptr += 56;
if (block_len == 0)
end = ptr / 16;
else end = block_len;
}
dmr_talker_alias_lc_decode (opts, state, slot, block_num+1, char_size, end);
}
}
//Decode partial or completed alias
void dmr_talker_alias_lc_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t block_num, uint8_t char_size, uint16_t end)
{
UNUSED(opts);
uint16_t i = 0;
fprintf (stderr, " Slot %d - Talker Alias Block Num: %d; Valid Block;", slot, block_num+1);
fprintf (stderr, " Talker Alias: ");
char alias_string[500]; memset (alias_string, 0, sizeof(alias_string));
sprintf (alias_string, "%s", "");
if (char_size == 7)
{
for (i = 0; i < end; i++)
{
uint8_t character = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][(i*7)], 7);
char ch[2]; ch[0] = character; ch[1] = 0;
if (character >= 0x20 && character <= 0x7E) //Standard ASCII Set
{
fprintf (stderr, "%c", character);
strcat (alias_string, ch);
}
// else if (character == 0)
// {
// strcat (alias_string, " ");
// fprintf (stderr, " ");
// break;
// }
else
{
strcat (alias_string, " ");
fprintf (stderr, " ");
}
}
}
else if (char_size == 8)
{
for (i = 0; i < end; i++)
{
uint8_t character = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][(i*8)], 8);
char ch[2]; ch[0] = character; ch[1] = 0;
// if (character >= 0x20 && character <= 0x7E) //Standard ASCII Set
if (character >= 0x20 && character != 0x7F) //allow some extended UTF diacritical characters as well
{
fprintf (stderr, "%c", character);
strcat (alias_string, ch);
}
// else if (character == 0)
// {
// strcat (alias_string, " ");
// fprintf (stderr, " ");
// break;
// }
else
{
strcat (alias_string, " ");
fprintf (stderr, " ");
}
}
}
else if (char_size == 16)
{
int ptr = 0;
setlocale(LC_ALL, ""); //needed when encoded alias contains Chinese (or probably any non-roman charset that isn't default on users terminal)
for (i = 0; i < end; i++)
{
uint16_t character = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][(i*16)], 16);
if (character >= 0x20 && character != 0x7F)
fprintf (stderr, "%lc", character);
else fprintf (stderr, " ");
//Encode to UTF-8
ptr += utf8_encode(character, alias_string+ptr);
//debug
// fprintf (stderr, " [%04X], ", character);
}
}
//assign to string for event history and ncurses display
uint32_t source = 0;
if (slot == 0)
source = state->lastsrc;
else source = state->lastsrcR;
if (state->event_history_s[slot].Event_History_Items[0].source_id == source)
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s; ", alias_string);
sprintf (state->generic_talker_alias[slot], "Talker Alias: %s; ", alias_string);
}

View File

@@ -21,16 +21,14 @@ pa_sample_spec ss;
pa_sample_spec tt; pa_sample_spec tt;
pa_sample_spec zz; pa_sample_spec zz;
pa_sample_spec cc; pa_sample_spec cc;
pa_sample_spec ff; //float
//pulse audio buffer attributes (input latency)
pa_buffer_attr lt;
void closePulseOutput (dsd_opts * opts) void closePulseOutput (dsd_opts * opts)
{ {
pa_simple_free (opts->pulse_digi_dev_out); pa_simple_free (opts->pulse_digi_dev_out);
if (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) //EDACS analog calls and/or monitoring source analog audio if (opts->dmr_stereo == 1)
pa_simple_free (opts->pulse_raw_dev_out); {
pa_simple_free (opts->pulse_digi_dev_outR);
}
} }
void closePulseInput (dsd_opts * opts) void closePulseInput (dsd_opts * opts)
@@ -41,66 +39,33 @@ void closePulseInput (dsd_opts * opts)
void openPulseOutput(dsd_opts * opts) void openPulseOutput(dsd_opts * opts)
{ {
char * dev = NULL;
if (opts->pa_output_idx[0] != 0)
dev = opts->pa_output_idx;
int err = 0;
ss.format = PA_SAMPLE_S16NE; ss.format = PA_SAMPLE_S16NE;
ss.channels = opts->pulse_raw_out_channels; ss.channels = opts->pulse_raw_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse
ss.rate = opts->pulse_raw_rate_out; ss.rate = opts->pulse_raw_rate_out; //48000
tt.format = PA_SAMPLE_S16NE; tt.format = PA_SAMPLE_S16NE;
tt.channels = opts->pulse_digi_out_channels; tt.channels = opts->pulse_digi_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse
tt.rate = opts->pulse_digi_rate_out; tt.rate = opts->pulse_digi_rate_out; //48000, switches to 8000 when using RTL dongle
ff.format = PA_SAMPLE_FLOAT32NE; if (opts->monitor_input_audio == 1)
ff.channels = opts->pulse_digi_out_channels;
ff.rate = opts->pulse_digi_rate_out;
//reconfigured to open when using edacs or raw analog monitor so we can have a analog audio out that runs at 48k1 and not 8k1 float/short
if (opts->frame_provoice == 1 || opts->monitor_input_audio == 1)
opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME3", PA_STREAM_PLAYBACK, dev, "Analog", &ss, 0, NULL, &err);
if (err != 0)
{ {
fprintf (stderr, "Err: %d; %s; ", err, pa_strerror(err)); opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME3", PA_STREAM_PLAYBACK, NULL, "Raw Audio Out", &ss, NULL, NULL, NULL);
#ifdef __CYGWIN__
fprintf (stderr, "Please make sure the Pulse Audio Server Backend is running first.");
#endif
exit(0);
} }
pa_channel_map* fl = 0; //NULL and 0 are same in this context //tt
pa_channel_map* ss = 0; //NULL and 0 are same in this context pa_channel_map* left = 0; //NULL and 0 are same in this context
pa_channel_map* right = 0; //NULL and 0 are same in this context
if (opts->floating_point == 0 && opts->analog_only == 0) if (opts->dmr_stereo == 0)
{ {
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, dev, opts->output_name, &tt, ss, NULL, &err); opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, opts->output_name, &tt, left, NULL, NULL);
if (err != 0)
{
fprintf (stderr, "Err: %d; %s; ", err, pa_strerror(err));
#ifdef __CYGWIN__
fprintf (stderr, "Please make sure the Pulse Audio Server Backend is running first.");
#endif
exit(0);
}
} }
if (opts->floating_point == 1 && opts->analog_only == 0) if (opts->dmr_stereo == 1)
{ {
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, dev, opts->output_name, &ff, fl, NULL, &err); opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME1", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 1", &tt, left, NULL, NULL);
opts->pulse_digi_dev_outR = pa_simple_new(NULL, "DSD-FME2", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 2", &tt, right, NULL, NULL);
if (err != 0)
{
fprintf (stderr, "Err: %d; %s; ", err, pa_strerror(err));
#ifdef __CYGWIN__
fprintf (stderr, "Please make sure the Pulse Audio Server Backend is running first.");
#endif
exit(0);
}
} }
} }
@@ -108,212 +73,14 @@ void openPulseOutput(dsd_opts * opts)
void openPulseInput(dsd_opts * opts) void openPulseInput(dsd_opts * opts)
{ {
char * dev = NULL;
if (opts->pa_input_idx[0] != 0)
dev = opts->pa_input_idx;
int err = 0;
cc.format = PA_SAMPLE_S16NE; cc.format = PA_SAMPLE_S16NE;
cc.channels = opts->pulse_digi_in_channels; cc.channels = opts->pulse_digi_in_channels;
cc.rate = opts->pulse_digi_rate_in; //48000 cc.rate = opts->pulse_digi_rate_in; //48000
//adjust input latency settings (defaults are all -1 to let server set these automatically, but without pavucontrol open, this is approx 2s) opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, NULL, opts->output_name, &cc, NULL, NULL, NULL);
//setting fragsize to 960 for 48000 input seems to do the trick to allow a much faster latency without underrun, may need adjusting if
//modifying the input rate (will only apply IF using pulse audio input, and not any other input method)
//TODO: set fragsize vs expected input rate if required in the future
//NOTE: If users report any underrun conditions, then either change 960 back to -1, or pass NULL instead of &lt
//https://freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html
//for now, only going to modify the fragsize if using the encoder, else, let the pa server set it automatically
// if (opts->m17encoder == 1)
// lt.fragsize = 960*5;
// else lt.fragsize = -1;
//test doing it universally, fall back to above if needed
lt.fragsize = 960*5;
lt.maxlength = -1;
lt.prebuf = -1;
lt.tlength = -1;
// if (opts->m17encoder == 1)
// opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME4", PA_STREAM_RECORD, dev, "M17 Voice Input", &cc, NULL, &lt, &err);
// else opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, &lt, &err);
opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, &lt, &err);
if (err != 0)
{
fprintf (stderr, "Err: %d; %s; ", err, pa_strerror(err));
#ifdef __CYGWIN__
fprintf (stderr, "Please make sure the Pulse Audio Server Backend is running first.");
#endif
exit(0);
}
//debug
// if (opts->m17encoder == 1)
// {
// unsigned long long int latency = pa_simple_get_latency (opts->pulse_digi_dev_in, NULL);
// fprintf (stderr, "Pulse Audio Input Latency: %05lld;", latency);
// }
} }
void parse_pulse_input_string (dsd_opts * opts, char * input)
{
char * curr;
curr = strtok(input, ":");
if (curr != NULL)
{
strncpy (opts->pa_input_idx, curr, 99);
opts->pa_input_idx[99] = 0;
fprintf (stderr, "\n");
fprintf (stderr, "Pulse Input Device: %s; ", opts->pa_input_idx);
fprintf (stderr, "\n");
}
}
void parse_pulse_output_string (dsd_opts * opts, char * input)
{
char * curr;
curr = strtok(input, ":");
if (curr != NULL)
{
strncpy (opts->pa_output_idx, curr, 99);
opts->pa_output_idx[99] = 0;
fprintf (stderr, "\n");
fprintf (stderr, "Pulse Output Device: %s; ", opts->pa_output_idx);
fprintf (stderr, "\n");
}
}
#ifdef __APPLE__
// macOS does not support OSS (/dev/dsp).
// Provide a stub so callers can link while using Pulse/CoreAudio backends.
void openOSSOutput(dsd_opts *opts)
{
(void)opts;
}
#else
void openOSSOutput (dsd_opts * opts)
{
int fmt;
int speed = 48000;
if (opts->audio_in_type == 5) //if((strncmp(opts->audio_in_dev, "/dev/dsp", 8) == 0)) or 'split' == 0
{
if((strncmp(opts->audio_out_dev, "/dev/dsp", 8) == 0))
{
fprintf (stderr, "OSS Output %s.\n", opts->audio_out_dev);
opts->audio_out_fd = open (opts->audio_out_dev, O_RDWR);
if (opts->audio_out_fd == -1)
{
fprintf (stderr, "Error, couldn't open #1 %s\n", opts->audio_out_dev);
opts->audio_out = 0;
exit(1);
}
fmt = 0;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
{
fprintf (stderr, "ioctl reset error \n");
}
fmt = speed;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
{
fprintf (stderr, "ioctl speed error \n");
}
fmt = 0; //this seems okay to be 1 or 0, not sure what the difference really is (works in stereo on 0)
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
{
fprintf (stderr, "ioctl stereo error \n");
}
fmt = AFMT_S16_LE;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
{
fprintf (stderr, "ioctl setfmt error \n");
}
opts->audio_out_type = 5; //5 for 1 channel - 48k OSS 16-bit short output (matching with input)
opts->pulse_digi_rate_out = 48000; //this is used to force to upsample and also allow source audio monitor conditional check
opts->pulse_digi_out_channels = 1; //this is used to allow source audio monitor conditional check
opts->audio_gain = 0;
}
}
if (opts->audio_in_type != 5 || opts->split == 1) //split == 1
{
if((strncmp(opts->audio_out_dev, "/dev/dsp", 8) == 0))
{
fprintf (stderr, "OSS Output %s.\n", opts->audio_out_dev);
opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY);
if (opts->audio_out_fd == -1)
{
fprintf (stderr, "Error, couldn't open %s\n", opts->audio_out_dev);
opts->audio_out = 0;
exit(1);
}
//Setup the device. Note that it's important to set the sample format, number of channels and sample rate exactly in this order. Some devices depend on the order.
fmt = 0;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
{
fprintf (stderr, "ioctl reset error \n");
}
fmt = AFMT_S16_LE; //Sample Format
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
{
fprintf (stderr, "ioctl setfmt error \n");
}
fmt = opts->pulse_digi_out_channels; //number of channels //was 2
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_CHANNELS, &fmt) < 0)
{
fprintf (stderr, "ioctl channel error \n");
}
//if using split with OSS output, and using EDACS w/ Analog, we have to use 48k
if (opts->frame_provoice == 1)
opts->pulse_digi_rate_out = 48000;
speed = opts->pulse_digi_rate_out; //since we have split input/output, we want to mirror pulse rate out
fmt = speed; //output rate
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
{
fprintf (stderr, "ioctl speed error \n");
}
if (opts->pulse_digi_out_channels == 2)
fmt = 1;
else fmt = 0;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
{
fprintf (stderr, "ioctl stereo error \n");
}
//TODO: Multiple output returns based on 8k/1, 8k/2, or maybe 48k/1? (2,3,5)??
if (opts->pulse_digi_out_channels == 2)
opts->audio_out_type = 2; //2 for 2 channel 8k OSS 16-bit short output
else if (opts->frame_m17 == 1)
opts->audio_out_type = 2;
else opts->audio_out_type = 5;
//debug
fprintf (stderr, "Using OSS Output with %dk/%d channel configuration.\n", opts->pulse_digi_rate_out, opts->pulse_digi_out_channels);
}
}
}
#endif
void void
processAudio (dsd_opts * opts, dsd_state * state) processAudio (dsd_opts * opts, dsd_state * state)
{ {
@@ -405,7 +172,8 @@ processAudio (dsd_opts * opts, dsd_state * state)
// copy audio data to output buffer and upsample if necessary // copy audio data to output buffer and upsample if necessary
state->audio_out_temp_buf_p = state->audio_out_temp_buf; state->audio_out_temp_buf_p = state->audio_out_temp_buf;
//we only want to upsample when using sample rates greater than 8k for output //we only want to upsample when using sample rates greater than 8k for output,
//hard set to 8k for RTL mono and MBE playback, otherwise crackling may occur.
if (opts->pulse_digi_rate_out > 8000) if (opts->pulse_digi_rate_out > 8000)
{ {
for (n = 0; n < 160; n++) for (n = 0; n < 160; n++)
@@ -429,8 +197,6 @@ processAudio (dsd_opts * opts, dsd_state * state)
*state->audio_out_float_buf_p = -32768.0F; *state->audio_out_float_buf_p = -32768.0F;
} }
*state->audio_out_buf_p = (short) *state->audio_out_float_buf_p; *state->audio_out_buf_p = (short) *state->audio_out_float_buf_p;
//tap the pointer here and store the short upsample buffer samples
state->s_lu[n] = (short) *state->audio_out_float_buf_p;
state->audio_out_buf_p++; state->audio_out_buf_p++;
state->audio_out_float_buf_p++; state->audio_out_float_buf_p++;
} }
@@ -449,10 +215,6 @@ processAudio (dsd_opts * opts, dsd_state * state)
*state->audio_out_temp_buf_p = -32768.0F; *state->audio_out_temp_buf_p = -32768.0F;
} }
*state->audio_out_buf_p = (short) *state->audio_out_temp_buf_p; *state->audio_out_buf_p = (short) *state->audio_out_temp_buf_p;
//tap the pointer here and store the short buffer samples
state->s_l[n] = (short) *state->audio_out_temp_buf_p;
//debug
// fprintf (stderr, " %d", state->s_l[n]);
state->audio_out_buf_p++; state->audio_out_buf_p++;
state->audio_out_temp_buf_p++; state->audio_out_temp_buf_p++;
state->audio_out_idx++; state->audio_out_idx++;
@@ -553,6 +315,7 @@ processAudioR (dsd_opts * opts, dsd_state * state)
// copy audio data to output buffer and upsample if necessary // copy audio data to output buffer and upsample if necessary
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR; state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
//we only want to upsample when using sample rates greater than 8k for output, //we only want to upsample when using sample rates greater than 8k for output,
//hard set to 8k for RTL mono and MBE playback, otherwise crackling may occur.
if (opts->pulse_digi_rate_out > 8000) if (opts->pulse_digi_rate_out > 8000)
{ {
for (n = 0; n < 160; n++) for (n = 0; n < 160; n++)
@@ -576,8 +339,6 @@ processAudioR (dsd_opts * opts, dsd_state * state)
*state->audio_out_float_buf_pR = -32768.0F; *state->audio_out_float_buf_pR = -32768.0F;
} }
*state->audio_out_buf_pR = (short) *state->audio_out_float_buf_pR; *state->audio_out_buf_pR = (short) *state->audio_out_float_buf_pR;
//tap the pointer here and store the short upsample buffer samples
state->s_ru[n] = (short) *state->audio_out_float_buf_pR;
state->audio_out_buf_pR++; state->audio_out_buf_pR++;
state->audio_out_float_buf_pR++; state->audio_out_float_buf_pR++;
} }
@@ -596,8 +357,6 @@ processAudioR (dsd_opts * opts, dsd_state * state)
*state->audio_out_temp_buf_pR = -32768.0F; *state->audio_out_temp_buf_pR = -32768.0F;
} }
*state->audio_out_buf_pR = (short) *state->audio_out_temp_buf_pR; *state->audio_out_buf_pR = (short) *state->audio_out_temp_buf_pR;
//tap the pointer here and store the short buffer samples
state->s_r[n] = (short) *state->audio_out_temp_buf_pR;
state->audio_out_buf_pR++; state->audio_out_buf_pR++;
state->audio_out_temp_buf_pR++; state->audio_out_temp_buf_pR++;
state->audio_out_idxR++; state->audio_out_idxR++;
@@ -662,47 +421,12 @@ void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
} }
//short Mono to Stereo version for new static .wav files in stereo format for TDMA void writeRawSample (dsd_opts * opts, dsd_state * state, short sample)
void writeSynthesizedVoiceMS (dsd_opts * opts, dsd_state * state)
{ {
int n; int n;
short aout_buf[160]; short aout_buf[160];
short *aout_buf_p; short *aout_buf_p;
aout_buf_p = aout_buf;
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
for (n = 0; n < 160; n++)
{
if (*state->audio_out_temp_buf_p > (float) 32767)
{
*state->audio_out_temp_buf_p = (float) 32767;
}
else if (*state->audio_out_temp_buf_p < (float) -32768)
{
*state->audio_out_temp_buf_p = (float) -32768;
}
*aout_buf_p = (short) *state->audio_out_temp_buf_p;
aout_buf_p++;
state->audio_out_temp_buf_p++;
}
short ss[320];
for (n = 0; n < 160; n++)
{
ss[(n*2)+0] = aout_buf[n];
ss[(n*2)+1] = aout_buf[n];
}
sf_write_short(opts->wav_out_f, ss, 320);
}
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample)
{
UNUSED(state);
//short aout_buf[160];
//sf_write_short(opts->wav_out_raw, aout_buf, 160); //sf_write_short(opts->wav_out_raw, aout_buf, 160);
//only write if actual audio, truncate silence //only write if actual audio, truncate silence
@@ -717,46 +441,24 @@ void
playSynthesizedVoice (dsd_opts * opts, dsd_state * state) playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
{ {
ssize_t result; ssize_t result;
UNUSED(result);
//don't synthesize voice if slot is turned off
if (opts->slot1_on == 0)
{
//clear any previously buffered audio
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
state->audio_out_buf_p = state->audio_out_buf + 100;
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
memset (state->audio_out_buf, 0, 100 * sizeof (short));
state->audio_out_idx2 = 0;
state->audio_out_idx = 0;
goto end_psv;
}
if (state->audio_out_idx > opts->delay) if (state->audio_out_idx > opts->delay)
{ {
if (opts->audio_out_type == 5 || opts->audio_out_type == 1) //OSS // output synthesized speech to sound card
if(opts->audio_out_type == 2)
{
//go F yourself PA
}
else
{ {
//OSS 48k/1 //Test just sending it straight on since I think I've figured out the STDIN and RTL for Stereo
result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2)); 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.
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 if (opts->audio_out_type == 8) //UDP Audio Out -- Forgot some things still use this for now
{
udp_socket_blaster (opts, state, (state->audio_out_idx * 2), (state->audio_out_buf_p - state->audio_out_idx));
state->audio_out_idx = 0;
}
else state->audio_out_idx = 0; //failsafe for audio_out == 0
} }
end_psv:
if (state->audio_out_idx2 >= 800000) if (state->audio_out_idx2 >= 800000)
{ {
state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
@@ -772,28 +474,19 @@ void
playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state) playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
{ {
ssize_t result; ssize_t result;
UNUSED(result);
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 == 5) //OSS if(opts->audio_out_type == 2)
{
//go F yourself PA
}
else
{ {
//OSS 48k/1 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.
result = write (opts->audio_out_fd, (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 if (opts->audio_out_type == 8) //UDP Audio Out -- Not sure how this would handle, but R never gets called anymore, so just here for symmetry
{
udp_socket_blaster (opts, state, (state->audio_out_idxR * 2), (state->audio_out_buf_pR - state->audio_out_idxR));
state->audio_out_idxR = 0;
}
else state->audio_out_idxR = 0; //failsafe for audio_out == 0
} }
@@ -810,8 +503,6 @@ playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
void void
openAudioOutDevice (dsd_opts * opts, int speed) openAudioOutDevice (dsd_opts * opts, int speed)
{ {
UNUSED(speed);
//converted to handle any calls to use portaudio //converted to handle any calls to use portaudio
if(strncmp(opts->audio_out_dev, "pa:", 3) == 0) if(strncmp(opts->audio_out_dev, "pa:", 3) == 0)
{ {
@@ -826,7 +517,7 @@ openAudioOutDevice (dsd_opts * opts, int speed)
} }
else else
{ {
// struct stat stat_buf; struct stat stat_buf;
// if(stat(opts->audio_out_dev, &stat_buf) != 0 && strncmp(opts->audio_out_dev, "pulse", 5 != 0)) //HERE // if(stat(opts->audio_out_dev, &stat_buf) != 0 && strncmp(opts->audio_out_dev, "pulse", 5 != 0)) //HERE
// { // {
// fprintf (stderr,"Error, couldn't open %s\n", opts->audio_out_dev); // fprintf (stderr,"Error, couldn't open %s\n", opts->audio_out_dev);
@@ -852,7 +543,7 @@ openAudioInDevice (dsd_opts * opts)
//if no extension set, give default of .wav -- bugfix for github issue #105 //if no extension set, give default of .wav -- bugfix for github issue #105
// if (extension == NULL) extension = ".wav"; // if (extension == NULL) extension = ".wav";
// get info of device/file // get info of device/file
if (strncmp(opts->audio_in_dev, "-", 1) == 0) if (strncmp(opts->audio_in_dev, "-", 1) == 0)
{ {
@@ -870,12 +561,6 @@ openAudioInDevice (dsd_opts * opts)
exit(1); exit(1);
} }
} }
else if (strncmp(opts->audio_in_dev, "m17udp", 6) == 0)
{
opts->audio_in_type = 9; //NULL audio device
}
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0) else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
{ {
opts->audio_in_type = 8; opts->audio_in_type = 8;
@@ -885,50 +570,37 @@ openAudioInDevice (dsd_opts * opts)
opts->audio_in_file_info->seekable=0; opts->audio_in_file_info->seekable=0;
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE; opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0); opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
if(opts->tcp_file_in == NULL) if(opts->tcp_file_in == NULL)
{ {
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL)); fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
exit(1); exit(1);
} }
} }
else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
{
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);
// else if (strncmp(opts->audio_in_dev, "udp", 3) == 0) if(opts->udp_file_in == NULL)
// { {
// opts->audio_in_type = 6; fprintf(stderr, "Error, couldn't open UDP with libsndfile: %s\n", sf_strerror(NULL));
// opts->audio_in_file_info = calloc(1, sizeof(SF_INFO)); exit(1);
// 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)
{ {
#ifdef USE_RTLSDR
opts->audio_in_type = 3; opts->audio_in_type = 3;
#elif AERO_BUILD
opts->audio_in_type = 5;
sprintf (opts->audio_in_dev, "/dev/dsp");
#else
opts->audio_in_type = 0;
sprintf (opts->audio_in_dev, "pulse");
#endif
} }
else if(strncmp(opts->audio_in_dev, "pulse", 5) == 0) else if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
{ {
opts->audio_in_type = 0; opts->audio_in_type = 0;
} }
else if((strncmp(opts->audio_in_dev, "/dev/dsp", 8) == 0))
{
opts->audio_in_type = 5;
}
//if no extension set, treat as named pipe or extensionless wav file -- bugfix for github issue #105 //if no extension set, treat as named pipe or extensionless wav file -- bugfix for github issue #105
else if (extension == NULL) else if (extension == NULL)
@@ -948,68 +620,7 @@ openAudioInDevice (dsd_opts * opts)
} }
} }
//test .rrc files with hardset wav file settings else if (strncmp(extension, ".bin", 3) == 0)
else if (strncmp(extension, ".rrc", 4) == 0)
{
//debug
fprintf (stderr, "Opening M17 .rrc headless wav file\n");
opts->audio_in_type = 2;
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
opts->audio_in_file_info->samplerate = 48000;
opts->audio_in_file_info->channels = 1;
opts->audio_in_file_info->seekable = 0;
opts->audio_in_file_info->format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
if(opts->audio_in_file == NULL)
{
fprintf(stderr, "Error, couldn't open %s with libsndfile: %s\n", opts->audio_in_dev, sf_strerror(NULL));
exit(1);
}
}
//Open .raw files as a float input type
else if (strncmp(extension, ".raw", 4) == 0)
{
struct stat stat_buf;
if (stat(opts->audio_in_dev, &stat_buf) != 0)
{
fprintf (stderr,"Error, couldn't open raw (float) 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 = 44; //float symbol input
}
else
{
opts->audio_in_type = 0;
}
}
//Open .raw files as a float input type
else if (strncmp(extension, ".sym", 4) == 0)
{
struct stat stat_buf;
if (stat(opts->audio_in_dev, &stat_buf) != 0)
{
fprintf (stderr,"Error, couldn't open sym (float) 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 = 44; //float symbol input
}
else
{
opts->audio_in_type = 0;
}
}
else if (strncmp(extension, ".bin", 4) == 0)
{ {
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)
@@ -1040,8 +651,8 @@ openAudioInDevice (dsd_opts * opts)
{ {
opts->audio_in_type = 2; //two now, seperating STDIN and wav files opts->audio_in_type = 2; //two now, seperating STDIN and wav files
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO)); 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->samplerate=opts->wav_sample_rate;
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_info->channels = 1;
opts->audio_in_file_info->seekable=0; opts->audio_in_file_info->seekable=0;
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE; opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
@@ -1056,11 +667,9 @@ openAudioInDevice (dsd_opts * opts)
} }
//open pulse audio if no bin or wav //open pulse audio if no bin or wav
else //seems this condition is never met else
{ {
//opts->audio_in_type = 5; //not sure if this works or needs to openPulse here opts->audio_in_type = 0; //not sure if this works or needs to openPulse here
fprintf(stderr, "Error, couldn't open input file.\n");
exit(1);
} }
} }
if (opts->split == 1) if (opts->split == 1)

File diff suppressed because it is too large Load Diff

View File

@@ -118,8 +118,6 @@ print_datascope(dsd_opts* opts, dsd_state* state, int* sbuf2)
static void static void
use_symbol (dsd_opts* opts, dsd_state* state, int symbol) use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
{ {
UNUSED(symbol);
int i; int i;
int sbuf2[128]; int sbuf2[128];
int lmin, lmax, lsum; int lmin, lmax, lsum;
@@ -219,7 +217,8 @@ invert_dibit(int dibit)
return -1; return -1;
} }
int digitize (dsd_opts* opts, dsd_state* state, int symbol) static 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) || (state->synctype == 37)) if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37))
@@ -266,39 +265,30 @@ int digitize (dsd_opts* opts, dsd_state* state, int symbol)
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) || else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) || (state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
(state->synctype == 17) || (state->synctype == 29) || (state->synctype == 31) || (state->synctype == 17) || (state->synctype == 29) || (state->synctype == 36) )
(state->synctype == 77) || (state->synctype == 87) ||(state->synctype == 36) ||
(state->synctype == 99) )
{ {
// 1 -P25p1 // 1 -P25p1
// 3 -X2-TDMA (inverted signal voice frame) // 3 -X2-TDMA (inverted signal voice frame)
// 5 -X2-TDMA (inverted signal data frame) // 5 -X2-TDMA (inverted signal data frame)
// 9 -M17 LSR // 9 -NXDN (inverted voice frame)
// 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 -M17 STR // 17 -NXDN (inverted data frame)
// 29 -NXDN (inverted FSW) // 29 -NXDN (inverted FSW)
// 31 -YSF // 36 -P25p2
// 36 -P25p2
// 77 -M17 BRT
// 87 -M17 PKT
// 99 -M17 Preamble
int valid; int valid;
int dibit; int dibit;
valid = 0; valid = 0;
UNUSED(opts); //disabling again, causing issues with trunking
if (state->synctype == 1 && opts->p25_trunk == 1)
//testing again, either on Voice channels only (when tuned) or with trunk disabled {
// if (state->synctype == 1 && (opts->p25_is_tuned == 1 || opts->p25_trunk == 0) && opts->use_heuristics == 1) // Use the P25 heuristics if available
if (state->synctype == 1 && opts->use_heuristics == 1) // valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
{ }
// Use the P25p1 heuristics if available
valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
}
if (valid == 0) if (valid == 0)
{ {
@@ -334,7 +324,7 @@ int digitize (dsd_opts* opts, dsd_state* state, int symbol)
state->dibit_buf_p++; state->dibit_buf_p++;
//dmr buffer //dmr buffer
*state->dmr_payload_p = invert_dibit(dibit); *state->dmr_payload_p = invert_dibit(dibit);
state->dmr_payload_p++; state->dmr_payload_p++;
//dmr buffer end //dmr buffer end
@@ -345,29 +335,24 @@ int digitize (dsd_opts* opts, dsd_state* state, int symbol)
// 0 +P25p1 // 0 +P25p1
// 2 +X2-TDMA (non inverted signal data frame) // 2 +X2-TDMA (non inverted signal data frame)
// 4 +X2-TDMA (non inverted signal voice frame) // 4 +X2-TDMA (non inverted signal voice frame)
// 8 +M17 LSF // 8 +NXDN (non inverted voice frame)
// 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 +M17 STR // 16 +NXDN (non inverted data frame)
// 28 +NXND (FSW) // 28 +NXND (FSW)
// 30 +YSF
// 35 +p25p2 // 35 +p25p2
// 76 +M17 BRT
// 86 +M17 PKT
// 98 +M17 Preamble
int valid; int valid;
int dibit; int dibit;
valid = 0; valid = 0;
//testing again, either on Voice channels only (when tuned) or with trunk disabled //disabling again, causing issues with trunking
// if (state->synctype == 0 && (opts->p25_is_tuned == 1 || opts->p25_trunk == 0) && opts->use_heuristics == 1) if (state->synctype == 0 && opts->p25_trunk == 1)
if (state->synctype == 0 && opts->use_heuristics == 1) {
{ // Use the P25 heuristics if available
// Use the P25p1 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)
{ {
@@ -449,18 +434,18 @@ get_dibit_and_analog_signal (dsd_opts* opts, dsd_state* state, int* out_analog_s
dibit = digitize (opts, state, symbol); dibit = digitize (opts, state, symbol);
if (opts->audio_in_type == 4) if (opts->audio_in_type == 4)
{ {
//assign dibit from last symbol/dibit read from capture bin //assign dibit from last symbol/dibit read from capture bin
dibit = state->symbolc; dibit = state->symbolc;
if (state->use_throttle == 1) if (state->use_throttle == 1)
{ {
usleep(0); usleep(0);
} }
} }
//symbol/dibit file capture/writing //symbol/dibit file capture/writing
if (opts->symbol_out_f) if (opts->symbol_out == 1)
{ {
//fprintf (stderr, "%d", dibit); //fprintf (stderr, "%d", dibit);
fputc (dibit, opts->symbol_out_f); fputc (dibit, opts->symbol_out_f);
@@ -497,7 +482,6 @@ skipDibit (dsd_opts * opts, dsd_state * state, int count)
short sample; short sample;
int i; int i;
UNUSED(sample);
for (i = 0; i < (count); i++) for (i = 0; i < (count); i++)
{ {

View File

@@ -1,870 +0,0 @@
/*-------------------------------------------------------------------------------
* dsd_events.c
* DSD-FME event history init, watchdog, push, and related functions
*
*
* LWVMOBILE
* 2025-05 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "git_ver.h"
#include "synctype.h"
//init each event history struct passed into here
void init_event_history (Event_History_I * event_struct, uint8_t start, uint8_t stop)
{
for (uint8_t i = start; i < stop; i++)
{
event_struct->Event_History_Items[i].write = 0;
event_struct->Event_History_Items[i].color_pair = 4;
event_struct->Event_History_Items[i].systype = -1;
event_struct->Event_History_Items[i].subtype = -1;
event_struct->Event_History_Items[i].sys_id1 = 0;
event_struct->Event_History_Items[i].sys_id2 = 0;
event_struct->Event_History_Items[i].sys_id3 = 0;
event_struct->Event_History_Items[i].sys_id4 = 0;
event_struct->Event_History_Items[i].sys_id5 = 0;
event_struct->Event_History_Items[i].gi = 0;
event_struct->Event_History_Items[i].enc = 0;
event_struct->Event_History_Items[i].enc_alg = 0;
event_struct->Event_History_Items[i].enc_key = 0;
event_struct->Event_History_Items[i].mi = 0;
event_struct->Event_History_Items[i].svc = 0;
event_struct->Event_History_Items[i].source_id = 0;
event_struct->Event_History_Items[i].target_id = 0;
sprintf (event_struct->Event_History_Items[i].src_str, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].tgt_str, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].t_name, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].s_name, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].t_mode, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].s_mode, "%s", "BUMBLEBEETUNA");
event_struct->Event_History_Items[i].channel = 0;
event_struct->Event_History_Items[i].event_time = 0;
memset (event_struct->Event_History_Items[i].pdu, 0, sizeof(event_struct->Event_History_Items[0].pdu));
sprintf (event_struct->Event_History_Items[i].sysid_string, "%s", "");
sprintf (event_struct->Event_History_Items[i].alias, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].gps_s, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].text_message, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].event_string, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].internal_str, "%s", "BUMBLEBEETUNA");
}
}
void push_event_history (Event_History_I * event_struct)
{
//Fixed, had it going in the wrong direction first time
for (uint8_t i = 254; i >= 1; i--)
{
event_struct->Event_History_Items[i].write = event_struct->Event_History_Items[i-1].write;
event_struct->Event_History_Items[i].color_pair = event_struct->Event_History_Items[i-1].color_pair;
event_struct->Event_History_Items[i].systype = event_struct->Event_History_Items[i-1].systype;
event_struct->Event_History_Items[i].subtype = event_struct->Event_History_Items[i-1].subtype;
event_struct->Event_History_Items[i].sys_id1 = event_struct->Event_History_Items[i-1].sys_id1;
event_struct->Event_History_Items[i].sys_id2 = event_struct->Event_History_Items[i-1].sys_id2;
event_struct->Event_History_Items[i].sys_id3 = event_struct->Event_History_Items[i-1].sys_id3;
event_struct->Event_History_Items[i].sys_id4 = event_struct->Event_History_Items[i-1].sys_id4;
event_struct->Event_History_Items[i].sys_id5 = event_struct->Event_History_Items[i-1].sys_id5;
event_struct->Event_History_Items[i].gi = event_struct->Event_History_Items[i-1].gi;
event_struct->Event_History_Items[i].enc = event_struct->Event_History_Items[i-1].enc;
event_struct->Event_History_Items[i].enc_alg = event_struct->Event_History_Items[i-1].enc_alg;
event_struct->Event_History_Items[i].enc_key = event_struct->Event_History_Items[i-1].enc_key;
event_struct->Event_History_Items[i].mi = event_struct->Event_History_Items[i-1].mi;
event_struct->Event_History_Items[i].svc = event_struct->Event_History_Items[i-1].svc;
event_struct->Event_History_Items[i].source_id = event_struct->Event_History_Items[i-1].source_id;
event_struct->Event_History_Items[i].target_id = event_struct->Event_History_Items[i-1].target_id;
sprintf (event_struct->Event_History_Items[i].src_str, "%s", event_struct->Event_History_Items[i-1].src_str);
sprintf (event_struct->Event_History_Items[i].tgt_str, "%s", event_struct->Event_History_Items[i-1].tgt_str);
sprintf (event_struct->Event_History_Items[i].t_name, "%s", event_struct->Event_History_Items[i-1].t_name);
sprintf (event_struct->Event_History_Items[i].s_name, "%s", event_struct->Event_History_Items[i-1].s_name);
sprintf (event_struct->Event_History_Items[i].t_mode, "%s", event_struct->Event_History_Items[i-1].t_mode);
sprintf (event_struct->Event_History_Items[i].s_mode, "%s", event_struct->Event_History_Items[i-1].s_mode);
event_struct->Event_History_Items[i].channel = event_struct->Event_History_Items[i-1].channel;
event_struct->Event_History_Items[i].event_time = event_struct->Event_History_Items[i+1].event_time;
memcpy (event_struct->Event_History_Items[i].pdu, event_struct->Event_History_Items[i-1].pdu, sizeof(event_struct->Event_History_Items[0].pdu));
sprintf (event_struct->Event_History_Items[i].sysid_string, "%s", event_struct->Event_History_Items[i-1].sysid_string);
sprintf (event_struct->Event_History_Items[i].alias, "%s", event_struct->Event_History_Items[i-1].alias);
sprintf (event_struct->Event_History_Items[i].gps_s, "%s", event_struct->Event_History_Items[i-1].gps_s);
sprintf (event_struct->Event_History_Items[i].text_message, "%s", event_struct->Event_History_Items[i-1].text_message);
sprintf (event_struct->Event_History_Items[i].event_string, "%s", event_struct->Event_History_Items[i-1].event_string);
sprintf (event_struct->Event_History_Items[i].internal_str, "%s", event_struct->Event_History_Items[i-1].internal_str);
}
}
void write_event_to_log_file (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t swrite, char * event_string) //pass completed event string here that is in the struct
{
//open log file
FILE * event_log_file;
event_log_file = fopen(opts->event_out_file, "a");
fprintf (event_log_file, "%s ", event_string);
if (swrite == 1)
fprintf (event_log_file, "Slot %d; ", slot+1);
fprintf (event_log_file,"\n");
char text_string[2000]; sprintf (text_string, "%s", "BUMBLEBEETUNA");
if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].text_message, 13) != 0)
fprintf (event_log_file, "%s \n", state->event_history_s[slot].Event_History_Items[0].text_message);
if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].alias, 13) != 0)
fprintf (event_log_file, " Talker Alias: %s \n", state->event_history_s[slot].Event_History_Items[0].alias);
if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].gps_s, 13) != 0)
fprintf (event_log_file, " GPS: %s \n", state->event_history_s[slot].Event_History_Items[0].gps_s);
if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].internal_str, 13) != 0)
fprintf (event_log_file, " DSD-FME: %s \n", state->event_history_s[slot].Event_History_Items[0].internal_str);
//flush and close log file
fflush (event_log_file);
fclose (event_log_file);
}
// run once per loop to check for and push and update event history
void watchdog_event_history (dsd_opts * opts, dsd_state * state, uint8_t slot)
{
//create a pointer to the current slot event history
Event_History_I * event_struct = &state->event_history_s[slot];
//is this a TDMA slot (append Slot value to end of written event history)
uint8_t swrite = 0;
//who is currently talking
uint32_t source_id = 0;
//last values pulled from the event history
uint32_t last_source_id = event_struct->Event_History_Items[0].source_id;
if (slot == 0)
source_id = state->lastsrc;
else
source_id = state->lastsrcR;
//if DMR BS or P25P2, then flag the swrite, so write can append slot value to event history log //|| state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 MS
if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13)
swrite = 1;
else if (state->lastsynctype == 35 || state->lastsynctype == 36)
swrite = 1;
if (slot == 0) //BUGFIX: generic catch on FDMA systems so that we don't write duplicate data to slot 2 event history
{
//NXDN RID (TODO: Changeover to lastsrc later on)
if (state->lastsynctype == 28 || state->lastsynctype == 29)
source_id = state->nxdn_last_rid;
if (state->lastsynctype == 30 || state->lastsynctype == 31) //YSF Fusion
{
source_id = 0;
if (strncmp(state->ysf_src, " ", 10) != 0) //if this field does not have ten spaces in it
{
for (uint8_t i = 0; i < 11; i++)
source_id += state->ysf_src[i]; //convert to sum value to make a distinct enough src value
}
}
if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17) //M17 STR
{
source_id = (uint32_t)state->m17_src;
}
if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR
{
source_id = 0;
for (uint8_t i = 0; i < 12; i++)
source_id += state->dstar_src[i]; //convert to sum value to make a distinct enough src value
//need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history
if (strncmp(state->dstar_src, " ", 8) == 0)
source_id = 0;
}
if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR
{
source_id = 0;
for (uint8_t i = 0; i < 20; i++)
source_id += state->dpmr_caller_id[i]; //convert to sum value to make a distinct enough src value
//need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history
if (strncmp(state->dpmr_caller_id, " ", 6) == 0)
source_id = 0;
}
if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls
{
source_id = 0;
if (opts->p25_is_tuned == 1)
source_id = state->lastsrc;
}
}
//call alert beep when new call detected
if (last_source_id == 0 && source_id != 0 && opts->call_alert == 1)
beeper (opts, state, slot, 40, 86, 3);
if (source_id != last_source_id && last_source_id != 0)
{
if (opts->event_out_file[0] != 0)
write_event_to_log_file(opts, state, slot, swrite, event_struct->Event_History_Items[0].event_string);
event_struct->Event_History_Items[0].write = 1; //written, or pushed at this point
if (opts->static_wav_file == 0)
{
if (slot == 0 && opts->wav_out_f != NULL)
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, event_struct);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
}
else if (slot == 1 && opts->wav_out_fR != NULL)
{
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, event_struct);
opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0);
}
}
push_event_history (event_struct);
init_event_history (event_struct, 0, 1);
//clear out some strings and things
memset(state->ysf_txt, 0, sizeof(state->ysf_txt));
memset(state->dstar_gps, 0, sizeof(state->dstar_gps));
memset(state->dstar_txt, 0, sizeof(state->dstar_txt));
state->gi[slot] = -1; //return to an unset value
//end of voice call alert
if (opts->call_alert == 1)
beeper (opts, state, slot, 40, 86, 3);
}
}
//similar to above, but constantly testing and checking the most recent event only
//this will hopefully be more useful when dealing with an ongoing event with
//features that update over time with embedded signalling, etc
void watchdog_event_current (dsd_opts * opts, dsd_state * state, uint8_t slot)
{
//create a pointer to the current slot event history
Event_History_I * event_struct = &state->event_history_s[slot];
//ncurses color pairs
uint8_t color_pair = 4; //default voice color (unknown gi)
if (state->gi[slot] == 1)
color_pair = 4; //default private voice color
else if (state->gi[slot] == 0)
color_pair = 4; //default group voice color
//TODO: Flesh out more later on.
uint32_t source_id = 0;
uint32_t target_id = 0;
char src_str[200]; memset (src_str, 0, sizeof(src_str));
sprintf (src_str, "%s", "BUMBLEBEETUNA");
char tgt_str[200]; memset (tgt_str, 0, sizeof(tgt_str));
sprintf (tgt_str, "%s", "BUMBLEBEETUNA");
//group import items
char t_name[200]; memset (t_name, 0, sizeof(t_name));
sprintf (t_name, "%s", "BUMBLEBEETUNA");
char s_name[200]; memset (s_name, 0, sizeof(s_name));
sprintf (s_name, "%s", "BUMBLEBEETUNA");
char t_mode[200]; memset (t_mode, 0, sizeof(t_mode));
sprintf (t_mode, "%s", "BUMBLEBEETUNA");
char s_mode[200]; memset (s_mode, 0, sizeof(s_mode));
sprintf (s_mode, "%s", "BUMBLEBEETUNA");
uint16_t svc_opts = 0;
uint8_t subtype = 0;
uint8_t mfid = 0;
uint32_t sys_id1 = 0;
uint32_t sys_id2 = 0;
uint32_t sys_id3 = 0;
uint32_t sys_id4 = 0;
uint32_t sys_id5 = 0;
uint32_t channel = 0;
uint8_t enc = 0;
uint8_t alg_id = 0;
uint16_t key_id = 0;
unsigned long long int mi;
char sysid_string[200]; memset(sysid_string, 0, sizeof(sysid_string));
sprintf (sysid_string, "%s", "");
if (slot == 0)
{
source_id = state->lastsrc;
target_id = state->lasttg;
subtype = state->dmrburstL;
mfid = state->dmr_fid;
svc_opts = state->dmr_so;
enc = (svc_opts >> 6) & 1;
alg_id = state->payload_algid;
key_id = (uint16_t)state->payload_keyid;
mi = state->payload_mi;
}
else
{
source_id = state->lastsrcR;
target_id = state->lasttgR;
subtype = state->dmrburstR;
mfid = state->dmr_fidR;
svc_opts = state->dmr_soR;
enc = (svc_opts >> 6) & 1;
alg_id = state->payload_algidR;
key_id = (uint16_t)state->payload_keyidR;
mi = state->payload_miR;
}
//if P25 (if not P25, then these will all be zero anyways)
sys_id1 = state->p2_wacn;
sys_id2 = state->p2_sysid;
if (state->nac != 0)
sys_id3 = state->nac; //same as state->p2_cc, but zeroes out when no signal or error
else sys_id3 = state->p2_cc;
sys_id4 = state->p2_rfssid;
sys_id5 = state->p2_siteid;
if (sys_id1)
sprintf (sysid_string, "P25_%05X%03X%03X_%d_%d", sys_id1, sys_id2, sys_id3, sys_id4, sys_id5);
else sprintf (sysid_string, "P25_%03X", sys_id3);
if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13 ||
state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 )
{
sys_id1 = state->dmr_t3_syscode;
sys_id2 = state->dmr_color_code;
if (sys_id1)
sprintf (sysid_string, "DMR_%X_CC_%d", sys_id1, sys_id2);
else sprintf (sysid_string, "DMR_CC_%d", sys_id2);
}
if (slot == 0) //BUGFIX: generic catch on FDMA systems so that we don't write duplicate data to slot 2 event history
{
//NXDN RID (TODO: Changeover to lastsrc and lasttg later on)
if (state->lastsynctype == 28 || state->lastsynctype == 29)
{
source_id = state->nxdn_last_rid;
target_id = state->nxdn_last_tg;
if (state->nxdn_cipher_type != 0)
enc = 1;
alg_id = state->nxdn_cipher_type;
key_id = state->nxdn_key;
sys_id1 = state->nxdn_location_site_code;
sys_id2 = state->nxdn_location_sys_code;
sys_id3 = state->nxdn_last_ran; //might be an issue on conventional systems that have a different RAN on the tx_rel or idle data bursts
if (sys_id1)
sprintf (sysid_string, "NXDN_%d_%d_RAN_%d", sys_id2, sys_id1, sys_id3);
else sprintf (sysid_string, "NXDN_RAN_%d", sys_id3);
}
if (state->lastsynctype == 30 || state->lastsynctype == 31) //YSF Fusion
{
source_id = 0;
if (strncmp(state->ysf_src, " ", 10) != 0) //if this field does not have ten spaces in it
{
for (uint8_t i = 0; i < 11; i++)
source_id += state->ysf_src[i]; //convert to sum value to make a distinct enough src value
}
//WIP: If Text, compile it here (still having issues with an empty txt string making a line break)
uint8_t k = 0; char ysf_emp[21][21]; memset(ysf_emp, 0, sizeof(ysf_emp));
if (memcmp(ysf_emp, state->ysf_txt, sizeof(state->ysf_txt)) != 0)
{
for (uint8_t i = 4; i < 8; i++)
{
for (uint8_t j = 0; j < 20; j++)
{
if (state->ysf_txt[i][j] != 0x2A)
event_struct->Event_History_Items[0].text_message[k++] = state->ysf_txt[i][j];
else event_struct->Event_History_Items[0].text_message[k++] = 0x20; //space
}
event_struct->Event_History_Items[0].text_message[k] = 0; //terminate
}
}
else sprintf (event_struct->Event_History_Items[0].text_message, "%s", "BUMBLEBEETUNA");
sprintf (sysid_string, "%s", "YSF");
char temp_str[20];
memset(temp_str, 0, sizeof(temp_str));
//set src string as a non-spaced non-garbo char string
for (uint8_t i = 0; i < 10; i++)
{
if (state->ysf_src[i] == 0x20) //spaces to underscore
temp_str[i] = 0x5F;
else if (state->ysf_src[i] > 0x20 && state->ysf_src[i] < 0x7F) //copy normal ascii range characters
temp_str[i] = state->ysf_src[i];
else if (state->ysf_src[i] == 0) break; //hit the terminator, so stop
else temp_str[i] = 0x5F; //unknown to underscore
}
sprintf (src_str, "%s", temp_str);
//same for tgt str
memset(temp_str, 0, sizeof(temp_str));
for (uint8_t i = 0; i < 10; i++)
{
if (state->ysf_tgt[i] == 0x20) //spaces to underscore
temp_str[i] = 0x5F;
else if (state->ysf_tgt[i] > 0x20 && state->ysf_tgt[i] < 0x7F) //copy normal ascii range characters
temp_str[i] = state->ysf_tgt[i];
else if (state->ysf_tgt[i] == 0) break; //hit the terminator, so stop
else temp_str[i] = 0x5F; //unknown to underscore
}
sprintf (tgt_str, "%s", temp_str);
}
if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17) //M17 STR
{
target_id = (uint32_t)state->m17_dst;
source_id = (uint32_t)state->m17_src;
sys_id1 = state->m17_can;
sprintf (sysid_string, "M17_CAN_%d", sys_id1);
sprintf (src_str, "%s", state->m17_src_csd);
sprintf (tgt_str, "%s", state->m17_dst_csd);
}
if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR
{
source_id = 0;
for (uint8_t i = 0; i < 12; i++)
source_id += state->dstar_src[i]; //convert to sum value to make a distinct enough src value
//need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history
if (strncmp(state->dstar_src, " ", 8) == 0)
source_id = 0;
sprintf (sysid_string, "%s", "DSTAR");
char temp_str[20];
memset(temp_str, 0, sizeof(temp_str));
//set src string as a non-spaced non-garbo char string
for (uint8_t i = 0; i < 12; i++)
{
if (state->dstar_src[i] == 0x20) //spaces to underscore
temp_str[i] = 0x5F;
else if (state->dstar_src[i] > 0x20 && state->dstar_src[i] < 0x7F) //copy normal ascii range characters
temp_str[i] = state->dstar_src[i];
else if (state->dstar_src[i] == 0) break; //hit the terminator, so stop
else temp_str[i] = 0x5F; //unknown to underscore
}
sprintf (src_str, "%s", temp_str);
//same for tgt str
memset(temp_str, 0, sizeof(temp_str));
for (uint8_t i = 0; i < 8; i++)
{
if (state->dstar_dst[i] == 0x20) //spaces to underscore
temp_str[i] = 0x5F;
else if (state->dstar_dst[i] > 0x20 && state->dstar_dst[i] < 0x7F) //copy normal ascii range characters
temp_str[i] = state->dstar_dst[i];
else if (state->dstar_dst[i] == 0) break; //hit the terminator, so stop
else temp_str[i] = 0x5F; //unknown to underscore
}
sprintf (tgt_str, "%s", temp_str);
}
if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR
{
source_id = 0;
for (uint8_t i = 0; i < 20; i++)
source_id += state->dpmr_caller_id[i]; //convert to sum value to make a distinct enough src value
//need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history
if (strncmp(state->dpmr_caller_id, " ", 6) == 0)
source_id = 0;
sprintf (sysid_string, "DPMR_CC_%d", state->dpmr_color_code);
sprintf (src_str, "%s", state->dpmr_caller_id);
sprintf (tgt_str, "%s", state->dpmr_target_id);
}
if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls
{
source_id = 0;
if (opts->p25_is_tuned == 1)
{
source_id = state->lastsrc;
channel = state->edacs_tuned_lcn;
}
sys_id1 = state->edacs_site_id;
sys_id2 = state->edacs_area_code;
sys_id3 = state->edacs_sys_id;
svc_opts = state->edacs_vc_call_type;
char sup_str[200]; memset (sup_str, 0, sizeof(sup_str));
sprintf (sup_str, "%s", "_");
if (svc_opts & 0x02)
strcat (sup_str, "Digital_");
else strcat (sup_str, "Analog_");
if (svc_opts & 0x04)
strcat (sup_str, "Emergency_");
if (svc_opts & 0x08)
strcat (sup_str, "Group_");
if (svc_opts & 0x10)
strcat (sup_str, "I_");
if (svc_opts & 0x20)
strcat (sup_str, "ALL_");
if (svc_opts & 0x40)
strcat (sup_str, "INTER_");
if (svc_opts & 0x80)
strcat (sup_str, "TEST_");
if (svc_opts & 0x100)
strcat (sup_str, "AGENCY_");
if (svc_opts & 0x200)
strcat (sup_str, "FLEET_");
if (svc_opts & 0x01)
strcat (sup_str, "Voice_");
strcat (sup_str, "Call");
sprintf (sysid_string, "EDACS_SITE_%03d", sys_id1);
strcat (sysid_string, sup_str);
if (state->ea_mode == 0)
{
int afs = state->lasttg;
sprintf(src_str, "%s", ""); sprintf(tgt_str, "%s", "");
int a = (afs >> state->edacs_a_shift) & state->edacs_a_mask;
int f = (afs >> state->edacs_f_shift) & state->edacs_f_mask;
int s = afs & state->edacs_s_mask;
sprintf (tgt_str, "%03d_AFS_%02d_%02d%01d", afs, a, f, s);
if (state->lastsrc != 0x800 && state->lastsrc != 0)
sprintf (src_str, "LID_%d", state->lastsrc);
else sprintf (src_str, "LID_UNK");
}
}
}
//if we have a group_array import, search and load it here
//will search and load both target values, and src values if available
uint8_t t_name_loaded = 0;
uint8_t s_name_loaded = 0;
if (target_id != 0)
{
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == target_id)
{
sprintf (t_name, "%s", state->group_array[i].groupName);
sprintf (t_mode, "%s", state->group_array[i].groupMode);
t_name_loaded = 1;
break;
}
}
}
if (source_id != 0) //&& state->gi[slot] == 1
{
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == source_id)
{
sprintf (s_name, "%s", state->group_array[i].groupName);
sprintf (s_mode, "%s", state->group_array[i].groupMode);
s_name_loaded = 1;
break;
}
}
}
//system type string (P25, DMR, etc)
const char * sys_string = "Digital";
if (state->lastsynctype != -1)
sys_string = SyncTypes[state->lastsynctype];
//date and time strings
char * timestr = getTimeN(time(NULL));
char * datestr = getDateN(time(NULL));
if (source_id != 0)
{
event_struct->Event_History_Items[0].write = 0;
state->event_history_s[slot].Event_History_Items[0].color_pair = color_pair;
if (state->lastsynctype != -1)
event_struct->Event_History_Items[0].systype = state->lastsynctype;
else event_struct->Event_History_Items[0].systype = 39; //generic digital call
event_struct->Event_History_Items[0].subtype = subtype; //voice
event_struct->Event_History_Items[0].gi = state->gi[slot]; //need this add this to link control messages
event_struct->Event_History_Items[0].sys_id1 = sys_id1;
event_struct->Event_History_Items[0].sys_id2 = sys_id2;
event_struct->Event_History_Items[0].sys_id3 = sys_id3;
event_struct->Event_History_Items[0].sys_id4 = sys_id4;
event_struct->Event_History_Items[0].sys_id5 = sys_id5;
event_struct->Event_History_Items[0].enc = enc;
event_struct->Event_History_Items[0].enc_alg = alg_id;
event_struct->Event_History_Items[0].enc_key = key_id;
event_struct->Event_History_Items[0].mi = mi;
event_struct->Event_History_Items[0].svc = svc_opts;
event_struct->Event_History_Items[0].source_id = source_id;
event_struct->Event_History_Items[0].target_id = target_id;
event_struct->Event_History_Items[0].channel = channel; //need to add this to trunking messages, if tuned from call grant
if (opts->playfiles == 0) //if playing back .mbe files with a time in it, don't set this
event_struct->Event_History_Items[0].event_time = time(NULL);
sprintf (event_struct->Event_History_Items[0].sysid_string, "%s", sysid_string);
sprintf (event_struct->Event_History_Items[0].src_str, "%s", src_str);
sprintf (event_struct->Event_History_Items[0].tgt_str, "%s", tgt_str);
sprintf (event_struct->Event_History_Items[0].t_name, "%s", t_name);
sprintf (event_struct->Event_History_Items[0].s_name, "%s", s_name);
sprintf (event_struct->Event_History_Items[0].t_mode, "%s", t_mode);
sprintf (event_struct->Event_History_Items[0].s_mode, "%s", s_mode);
}
//Craft an event string for ncurses event history, and a more complex string for logging
char event_string[2000]; memset(event_string, 0, sizeof(event_string));
//WIP: Seperate Voice Call Event Strings when SRC/TGT values are numerical,
//and a seperate one for when they are string values (M17, YSF, DSTAR, and dPMR, or use special formatting)
if (state->lastsynctype == 30 || state->lastsynctype == 31) //YSF Fusion //TODO: Data calls dumping a lot of events as VOICE
{
//TODO: See if we can add some decoded data as well in the future to an event string
sprintf (event_string, "%s %s %s TGT: %s SRC: %s ", datestr, timestr, sys_string, state->ysf_tgt, state->ysf_src);
}
else if (state->lastsynctype == 16 || state->lastsynctype == 17) //M17
{
//TODO: See if we can add some decoded data as well in the future to an event string
if (state->m17_dst == 0xFFFFFFFFFFFF)
sprintf (event_string, "%s %s %s TGT: %s SRC: %s CAN: %02d;", datestr, timestr, sys_string, "BROADCAST", state->m17_src_str, state->m17_can);
else
sprintf (event_string, "%s %s %s TGT: %s SRC: %s CAN: %02d;", datestr, timestr, sys_string, state->m17_dst_str, state->m17_src_str, state->m17_can);
}
else if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR
{
//TODO: See if we can add some decoded data as well in the future to an event string
sprintf (event_string, "%s %s %s TGT: %s SRC: %s ", datestr, timestr, sys_string, state->dstar_dst, state->dstar_src);
}
else if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR
{
//TODO: See if we can add some decoded data as well in the future to an event string
sprintf (event_string, "%s %s %s CC: %02d; TGT: %s; SRC: %s; ", datestr, timestr, sys_string, state->dpmr_color_code, state->dpmr_target_id, state->dpmr_caller_id);
if (state->dPMRVoiceFS2Frame.Version[0] == 3)
strcat (event_string, "Scrambler Enc; ");
}
else if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls
{
svc_opts = state->edacs_vc_call_type;
char sup_str[200]; memset (sup_str, 0, sizeof(sup_str));
sprintf (sup_str, "%s", "");
if (svc_opts & 0x02)
strcat (sup_str, "Digital ");
else strcat (sup_str, "Analog ");
if (svc_opts & 0x04)
strcat (sup_str, "Emergency ");
if (svc_opts & 0x08)
strcat (sup_str, "Group ");
if (svc_opts & 0x10)
strcat (sup_str, "I ");
if (svc_opts & 0x20)
strcat (sup_str, "ALL ");
if (svc_opts & 0x40)
strcat (sup_str, "INTER ");
if (svc_opts & 0x80)
strcat (sup_str, "TEST ");
if (svc_opts & 0x100)
strcat (sup_str, "AGENCY ");
if (svc_opts & 0x200)
strcat (sup_str, "FLEET ");
if (svc_opts & 0x01)
strcat (sup_str, "Voice ");
strcat (sup_str, "Call");
if (state->ea_mode == 1)
{
sprintf (event_string, "%s %s %s TGT: %07d; SRC: %07d; LCN: %02d; SITE: %d:%d.%04X; %s; ", datestr, timestr, sys_string, target_id, source_id, channel, sys_id1, sys_id2, sys_id3, sup_str);
}
else
{
int afs = state->lasttg;
int a = (afs >> state->edacs_a_shift) & state->edacs_a_mask;
int f = (afs >> state->edacs_f_shift) & state->edacs_f_mask;
int s = afs & state->edacs_s_mask;
char afs_str[8];
getAfsString(state, afs_str, a, f, s);
char lid_str[20]; memset(lid_str, 0, sizeof(lid_str));
sprintf(lid_str, "%s", "");
if (state->lastsrc != 0 && state->lastsrc != 0x800)
sprintf (lid_str, "LID: %05d;", state->lastsrc);
else sprintf (lid_str, "LID: __UNK;");
sprintf (event_string, "%s %s %s AFS: %s (%04d); %s LCN: %02d; Site: %d; %s; ", datestr, timestr, sys_string, afs_str, afs, lid_str, channel, sys_id1, sup_str);
}
}
else if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13 ||
state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 ) //DMR
{
if (sys_id1)
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; CC: %02d; SYS: %X; ", datestr, timestr, sys_string, target_id, source_id, sys_id2, sys_id1);
else
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; CC: %02d; ", datestr, timestr, sys_string, target_id, source_id, sys_id2);
if (enc)
strcat(event_string, "ENC; ");
if (alg_id != 0)
{
char ess_str[30];
sprintf (ess_str, "ALG: %02X; KID: %02X; ", alg_id, key_id);
strcat(event_string, ess_str);
}
//monitor for misc link control that may set a SO without having SO inside of it,
//those could cause misc issues here, will need to observe and make adjustments
if (svc_opts & 0x80)
strcat (event_string, "Emergency; ");
if (svc_opts & 0x08)
strcat (event_string, "Broadcast; ");
if (svc_opts & 0x04)
strcat (event_string, "OVCM; ");
if (state->gi[slot] == 0)
strcat (event_string, "Group; ");
else if (state->gi[slot] == 1)
strcat (event_string, "Private; ");
if (mfid == 0x10)
{
if (svc_opts & 0x20)
strcat (event_string, "TXI; ");
else if (svc_opts & 0x10)
strcat (event_string, "TXI; "); //this is the svc opt bit that tells you when the next VC6 will be pre-empted, but not helpful here
if (svc_opts & 0x03)
strcat (event_string, "PRIORITY; "); //need to break this apart into each one, but need to double check the decoded value is accurate
}
}
else if (state->lastsynctype == 0 || state->lastsynctype == 1 || state->lastsynctype == 35 || state->lastsynctype == 36)
{
if (sys_id1)
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; NAC: %03X; NET_STS: %05X:%03X:%d.%d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3, sys_id1, sys_id2, sys_id4, sys_id5);
else
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; NAC: %03X; ", datestr, timestr, sys_string, target_id, source_id, sys_id3);
if (alg_id != 0 && alg_id != 0x80)
{
char ess_str[30];
sprintf (ess_str, "ENC; ALG: %02X; KID: %04X; ", alg_id, key_id);
strcat(event_string, ess_str);
}
if (svc_opts & 0x80)
strcat (event_string, "Emergency; ");
if (state->gi[slot] == 0)
strcat (event_string, "Group; ");
else if (state->gi[slot] == 1)
strcat (event_string, "Private; ");
}
else if (state->lastsynctype == 28 || state->lastsynctype == 29)
{
if (sys_id1)
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; RAN: %02d; SYS: %d.%d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3, sys_id2, sys_id1);
else
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; RAN: %02d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3);
if (enc)
strcat(event_string, "ENC; ");
if (alg_id != 0)
{
char ess_str[30];
sprintf (ess_str, "ALG: %d; KID: %02X; ", alg_id, key_id);
strcat(event_string, ess_str);
}
if (state->gi[slot] == 0)
strcat (event_string, "Group; ");
else if (state->gi[slot] == 1)
strcat (event_string, "Private; ");
}
if (t_name_loaded)
{
char group[420];
sprintf (group, "TName: %s; Mode: %s; ", t_name, t_mode);
strcat (event_string, group);
}
if (s_name_loaded)
{
char private[420];
sprintf (private, "SName: %s; Mode: %s; ", s_name, s_mode);
strcat (event_string, private);
}
sprintf (event_struct->Event_History_Items[0].event_string, "%s", event_string);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
void watchdog_event_datacall (dsd_opts * opts, dsd_state * state, uint32_t src, uint32_t dst, char * data_string, uint8_t slot)
{
UNUSED(opts);
state->event_history_s[slot].Event_History_Items[0].write = 0;
if (state->event_history_s[slot].Event_History_Items[0].color_pair == 4) //if not set previously by specific decoder //don't touch this one
state->event_history_s[slot].Event_History_Items[0].color_pair = 4; //default data color //you can change this one
state->event_history_s[slot].Event_History_Items[0].systype = state->lastsynctype;
state->event_history_s[slot].Event_History_Items[0].subtype = 6; //data
state->event_history_s[slot].Event_History_Items[0].gi = state->gi[slot];
state->event_history_s[slot].Event_History_Items[0].enc = 0;
state->event_history_s[slot].Event_History_Items[0].enc_alg = 0;
state->event_history_s[slot].Event_History_Items[0].enc_key = 0;
state->event_history_s[slot].Event_History_Items[0].mi = 0;
state->event_history_s[slot].Event_History_Items[0].svc = 0;
state->event_history_s[slot].Event_History_Items[0].source_id = src;
state->event_history_s[slot].Event_History_Items[0].target_id = dst;
state->event_history_s[slot].Event_History_Items[0].channel = 0;
state->event_history_s[slot].Event_History_Items[0].event_time = time(NULL);
//date and time strings //getTimeN(time(NULL)); //getDateN(time(NULL));
char * timestr = getTimeN(time(NULL));
char * datestr = getDateN(time(NULL));
char event_string[2000]; memset (event_string, 0, sizeof(event_string));
sprintf (event_string, "%s %s ", datestr, timestr);
strcat (event_string, data_string); //may need to check for potential overflow of this
sprintf (state->event_history_s[slot].Event_History_Items[0].event_string, "%s", event_string); //could change this to a strncpy to prevent potential overflow
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
//call alert on data calls
if (opts->call_alert)
beeper (opts, state, slot, 80, 20, 3);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +1,8 @@
#include "dsd.h"
//M17 Filter -- RRC Alpha = 0.5 S/s 48000 with 81 taps
#define M17ZEROS 80
float m17gain = 3.16227766f; //sqrt of 10.0f
static float m17xv[81];
//sample RRC filter for 48kHz sample rate
//alpha=0.5, span=8, sps=10, gain=sqrt(sps)
float m17coeffs[81] = //replaced old ones with ones from libm17
{
-0.003195702904062073f, -0.002930279157647190f, -0.001940667871554463f,
-0.000356087678023658f, 0.001547011339077758f, 0.003389554791179751f,
0.004761898604225673f, 0.005310860846138910f, 0.004824746306020221f,
0.003297923526848786f, 0.000958710871218619f, -0.001749908029791816f,
-0.004238694106631223f, -0.005881783042101693f, -0.006150256456781309f,
-0.004745376707651645f, -0.001704189656473565f, 0.002547854551539951f,
0.007215575568844704f, 0.011231038205363532f, 0.013421952197060707f,
0.012730475385624438f, 0.008449554307303753f, 0.000436744366018287f,
-0.010735380379191660f, -0.023726883538258272f, -0.036498030780605324f,
-0.046500883189991064f, -0.050979050575999614f, -0.047340680079891187f,
-0.033554880492651755f, -0.008513823955725943f, 0.027696543159614194f,
0.073664520037517042f, 0.126689053778116234f, 0.182990955139333916f,
0.238080025892859704f, 0.287235637987091563f, 0.326040247765297220f,
0.350895727088112619f, 0.359452932027607974f, 0.350895727088112619f,
0.326040247765297220f, 0.287235637987091563f, 0.238080025892859704f,
0.182990955139333916f, 0.126689053778116234f, 0.073664520037517042f,
0.027696543159614194f, -0.008513823955725943f, -0.033554880492651755f,
-0.047340680079891187f, -0.050979050575999614f, -0.046500883189991064f,
-0.036498030780605324f, -0.023726883538258272f, -0.010735380379191660f,
0.000436744366018287f, 0.008449554307303753f, 0.012730475385624438f,
0.013421952197060707f, 0.011231038205363532f, 0.007215575568844704f,
0.002547854551539951f, -0.001704189656473565f, -0.004745376707651645f,
-0.006150256456781309f, -0.005881783042101693f, -0.004238694106631223f,
-0.001749908029791816f, 0.000958710871218619f, 0.003297923526848786f,
0.004824746306020221f, 0.005310860846138910f, 0.004761898604225673f,
0.003389554791179751f, 0.001547011339077758f, -0.000356087678023658f,
-0.001940667871554463f, -0.002930279157647190f, -0.003195702904062073f
};
// Original DMR filter // Original DMR filter
#define NZEROS 60 #define NZEROS 60
float ngain = 7.423339364f; float ngain = 7.423339364f;
static float xv[NZEROS+1]; static float xv[NZEROS+1];
float xcoeffs[61] = float xcoeffs[] =
{ -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f, { -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f,
-0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f, -0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f,
+0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f, +0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f,
@@ -64,10 +24,10 @@ float xcoeffs[61] =
//reverted to original NXDN filters, do better with LICH/Data handling //reverted to original NXDN filters, do better with LICH/Data handling
// NXDN48 filter - Original Version // NXDN48 filter - Original Version
#define NXZEROS 134 #define NXZEROS 134
float nxgain = 15.95930463f; float nxgain = 15.95930463f;
static float nxv[NXZEROS+1]; static float nxv[NXZEROS+1];
float nxcoeffs[135] = float nxcoeffs[] =
{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f, { +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
+0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f, +0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
-0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f, -0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
@@ -114,18 +74,18 @@ const float dmrgain = 6.82973073748f;
static float dxv[DMRNZEROS+1]; static float dxv[DMRNZEROS+1];
// DMR filter F4EXB - root raised cosine alpha=0.7 Ts = 6650 S/s Fc = 48kHz // DMR filter F4EXB - root raised cosine alpha=0.7 Ts = 6650 S/s Fc = 48kHz
const float dmrcoeffs[61] = const float dmrcoeffs[] =
{0.0301506278, 0.0269200615, 0.0159662432, -0.0013114705, -0.0216605133, {0.0301506278, 0.0269200615, 0.0159662432, -0.0013114705, -0.0216605133,
-0.0404938748, -0.0528141756, -0.0543747957, -0.0428325003, -0.0186176083, -0.0404938748, -0.0528141756, -0.0543747957, -0.0428325003, -0.0186176083,
0.0147202645, 0.0508418571, 0.0816392577, 0.0988113688, 0.0957187780, 0.0147202645, 0.0508418571, 0.0816392577, 0.0988113688, 0.0957187780,
0.0691512084, 0.0206194642, -0.0431564563, -0.1107569268, -0.1675773224, 0.0691512084, 0.0206194642, -0.0431564563, -0.1107569268, -0.1675773224,
-0.1981519842, -0.1889130786, -0.1308939560, -0.0218608492, 0.1325685970, -0.1981519842, -0.1889130786, -0.1308939560, -0.0218608492, 0.1325685970,
0.3190962499, 0.5182530574, 0.7070497652, 0.8623526878, 0.9644213921, 0.3190962499, 0.5182530574, 0.7070497652, 0.8623526878, 0.9644213921,
1.0000000000, 0.9644213921, 0.8623526878, 0.7070497652, 0.5182530574, 1.0000000000, 0.9644213921, 0.8623526878, 0.7070497652, 0.5182530574,
0.3190962499, 0.1325685970, -0.0218608492, -0.1308939560, -0.1889130786, 0.3190962499, 0.1325685970, -0.0218608492, -0.1308939560, -0.1889130786,
-0.1981519842, -0.1675773224, -0.1107569268, -0.0431564563, 0.0206194642, -0.1981519842, -0.1675773224, -0.1107569268, -0.0431564563, 0.0206194642,
0.0691512084, 0.0957187780, 0.0988113688, 0.0816392577, 0.0508418571, 0.0691512084, 0.0957187780, 0.0988113688, 0.0816392577, 0.0508418571,
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 // dPMR filter, version of F4EXB
@@ -138,7 +98,7 @@ static float dpmrxv[NXZEROS+1];
const float dpmrgain = 14.6083498224f; const float dpmrgain = 14.6083498224f;
// dPMR filter - root raised cosine alpha=0.2 Ts = 3325 S/s Fc = 48 kHz - zero at boundaries appears to be slightly better // dPMR filter - root raised cosine alpha=0.2 Ts = 3325 S/s Fc = 48 kHz - zero at boundaries appears to be slightly better
const float dpmrcoeffs[135] = const float dpmrcoeffs[] =
{-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816, {-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816,
0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307, 0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307,
0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763, 0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763,
@@ -187,18 +147,13 @@ dmr_filter(short sample)
short short
nxdn_filter(short sample) nxdn_filter(short sample)
{ {
return dsd_input_filter(sample, 4); return dsd_input_filter(sample, 2);
} }
short short
dpmr_filter(short sample) dpmr_filter(short sample)
{ {
return dsd_input_filter(sample, 4); return dsd_input_filter(sample, 4);
}
short m17_filter (short sample)
{
return dsd_input_filter(sample, 5);
} }
@@ -234,18 +189,10 @@ dsd_input_filter(short sample, int mode)
coeffs = (float *)dpmrcoeffs; coeffs = (float *)dpmrcoeffs;
zeros = DPMRNZEROS; zeros = DPMRNZEROS;
break; break;
case 5:
gain = m17gain;
v = m17xv;
coeffs = (float *)m17coeffs;
zeros = M17ZEROS;
break;
default: default:
return sample; return sample;
} }
for (i = 0; i < zeros; i++) for (i = 0; i < zeros; i++)
v[i] = v[i+1]; v[i] = v[i+1];
@@ -256,13 +203,4 @@ dsd_input_filter(short sample, int mode)
sum += (coeffs[i] * v[i]); sum += (coeffs[i] * v[i]);
return (short)(sum / gain); // filtered sample out return (short)(sum / gain); // filtered sample out
}
void init_rrc_filter_memory()
{
memset (m17xv, 0, 81*sizeof(float));
memset (xv, 0, 61*sizeof(float));
memset (dxv, 0, 61*sizeof(float));
memset (nxv, 0, 135*sizeof(float));
memset (dpmrxv, 0, 135*sizeof(float));
} }

View File

@@ -20,27 +20,35 @@
#define NULL 0 #define NULL 0
#endif #endif
#include "p25p1_check_nid.h"
void void
printFrameInfo (dsd_opts * opts, dsd_state * state) printFrameInfo (dsd_opts * opts, dsd_state * state)
{ {
UNUSED(opts); int level;
fprintf (stderr, "%s", KCYN); level = (int) state->max / 164;
if (state->p2_wacn != 0) if (opts->verbose > 0)
fprintf (stderr, "WACN: %05llX; ", state->p2_wacn); {
if (state->p2_sysid != 0) //fprintf (stderr,"inlvl: %2i%% ", level);
fprintf (stderr, "SYS: %03llX; ", state->p2_sysid); }
if (state->p2_cc != 0) if (state->nac != 0)
fprintf (stderr, "NAC/CC: %03llX; ", state->p2_cc); {
else fprintf (stderr, "NAC: %03X; ", state->nac); fprintf (stderr, "%s", KCYN);
fprintf (stderr,"nac: [%4X] ", state->nac);
fprintf (stderr, "%s", KNRM);
}
if (state->p2_rfssid != 0) if (opts->verbose > 1)
fprintf (stderr, "RFSS: %03lld; ", state->p2_rfssid); {
if (state->p2_siteid != 0) fprintf (stderr, "%s", KGRN);
fprintf (stderr, "Site: %03lld; ", state->p2_siteid); fprintf (stderr,"src: [%8i] ", state->lastsrc);
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "%s", KGRN);
fprintf (stderr,"tg: [%5i] ", state->lasttg);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
void void
@@ -51,10 +59,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
char duid[3]; char duid[3];
char nac[13]; char nac[13];
int level; int level;
UNUSED2(nac, level);
char status_0; char status_0;
UNUSED(status_0);
char bch_code[63]; char bch_code[63];
int index_bch_code; int index_bch_code;
unsigned char parity; unsigned char parity;
@@ -82,29 +88,55 @@ processFrame (dsd_opts * opts, dsd_state * state)
if ((state->synctype == 28) || (state->synctype == 29)) if ((state->synctype == 28) || (state->synctype == 29))
{ {
//MBEout restored, is not handled internally by nxdn_frame.c //MBEout restored, is not handled internally by nxdn_frame.c
nxdn_frame (opts, state); nxdn_frame (opts, state);
return; return;
} }
//DSTAR
else if ((state->synctype == 6) || (state->synctype == 7)) else if ((state->synctype == 6) || (state->synctype == 7))
{ {
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) state->nac = 0;
openMbeOutFile (opts, state); state->lastsrc = 0;
sprintf (state->fsubtype, " VOICE "); state->lasttg = 0;
processDSTAR (opts, state); if (opts->errorbars == 1)
return; {
} 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); //dstar ambe files are read back incorrectly, need to change file extension to differentiate when doing a playback
}
sprintf (state->fsubtype, " VOICE ");
processDSTAR (opts, state);
return;
}
else if ((state->synctype == 18) || (state->synctype == 19)) else if ((state->synctype == 18) || (state->synctype == 19))
{ {
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) state->nac = 0;
openMbeOutFile (opts, state); state->lastsrc = 0;
sprintf (state->fsubtype, " DATA "); state->lasttg = 0;
processDSTAR_HD (opts, state); if (opts->errorbars == 1)
return; {
} 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); //dstar ambe files are read back incorrectly, need to change file extension to differentiate when doing a playback
}
sprintf (state->fsubtype, " DATA ");
processDSTAR_HD (opts, state);
return;
}
//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 //print manufacturer strings to branding, disabled 0x10 moto other systems can use that fid set
@@ -112,7 +144,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
if (state->dmr_mfid == 0x10) ; //sprintf (state->dmr_branding, "%s", "Motorola"); if (state->dmr_mfid == 0x10) ; //sprintf (state->dmr_branding, "%s", "Motorola");
else if (state->dmr_mfid == 0x68) sprintf (state->dmr_branding, "%s", " Hytera"); else if (state->dmr_mfid == 0x68) sprintf (state->dmr_branding, "%s", " Hytera");
else if (state->dmr_mfid == 0x58) sprintf (state->dmr_branding, "%s", " Tait"); else if (state->dmr_mfid == 0x58) sprintf (state->dmr_branding, "%s", " Tait");
//disabling these due to random data decodes setting an odd mfid, could be legit, but only for that one packet? //disabling these due to random data decodes setting an odd mfid, could be legit, but only for that one packet?
//or, its just a decode error somewhere //or, its just a decode error somewhere
// else if (state->dmr_mfid == 0x20) sprintf (state->dmr_branding, "%s", "JVC Kenwood"); // else if (state->dmr_mfid == 0x20) sprintf (state->dmr_branding, "%s", "JVC Kenwood");
@@ -145,19 +177,19 @@ processFrame (dsd_opts * opts, dsd_state * state)
{ {
sprintf (state->fsubtype, " VOICE "); sprintf (state->fsubtype, " VOICE ");
if (opts->dmr_stereo == 0 && state->synctype < 32) if (opts->dmr_stereo == 0 && state->synctype < 32) // -T option for DMR (TDMA) stereo
{ {
sprintf (state->slot1light, " slot1 "); sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 "); sprintf (state->slot2light, " slot2 ");
//we can safely open MBE on any MS or mono handling //we can safely open MBE on any MS or mono handling
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state); if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state); dmrMSBootstrap (opts, state);
} }
if (opts->dmr_mono == 1 && state->synctype == 32) if (opts->dmr_mono == 1 && state->synctype == 32)
{ {
//we can safely open MBE on any MS or mono handling //we can safely open MBE on any MS or mono handling
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state); if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state); dmrMSBootstrap (opts, state);
} }
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1 if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
{ {
@@ -166,7 +198,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
{ {
//we can safely open MBE on any MS or mono handling //we can safely open MBE on any MS or mono handling
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state); if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state); //bootstrap into MS Bootstrap (voice only) dmrMSBootstrap (opts, state); //bootstrap into MS Bootstrap (voice only)
} }
else dmrBSBootstrap (opts, state); //bootstrap into BS Bootstrap else dmrBSBootstrap (opts, state); //bootstrap into BS Bootstrap
} }
@@ -175,7 +207,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
{ {
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state); if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state); if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
if (opts->p25_trunk == 0) dmrMSData (opts, state); dmrMSData (opts, state);
} }
else else
{ {
@@ -187,6 +219,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
state->err_str[0] = 0; state->err_str[0] = 0;
sprintf (state->slot1light, " slot1 "); sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 "); sprintf (state->slot2light, " slot2 ");
state->payload_miP = 0; //zero out when switching back to data sync
dmr_data_sync (opts, state); dmr_data_sync (opts, state);
} }
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata //switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
@@ -198,6 +231,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
state->dmr_stereo = 0; //set the state to zero for handling pure data frames state->dmr_stereo = 0; //set the state to zero for handling pure data frames
sprintf (state->slot1light, " slot1 "); sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 "); sprintf (state->slot2light, " slot2 ");
state->payload_miP = 0; //zero out when switching back to data sync
dmr_data_sync (opts, state); dmr_data_sync (opts, state);
} }
} }
@@ -249,23 +283,23 @@ processFrame (dsd_opts * opts, dsd_state * state)
//edacs //edacs
else if ((state->synctype == 37) || (state->synctype == 38)) else if ((state->synctype == 37) || (state->synctype == 38))
{ {
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state); if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
edacs (opts, state); edacs (opts, state);
return; return;
} }
//ysf //ysf
else if ((state->synctype == 30) || (state->synctype == 31)) else if ((state->synctype == 30) || (state->synctype == 31))
{ {
//relocate MBEout to inside frame handling -- Not Working Currently for YSF //Do stuff
//fprintf(stderr, "YSF Sync! \n");
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
//openMbeOutFile (opts, state);
}
//sprintf(state->fsubtype, " VOICE ");
processYSF(opts, state); processYSF(opts, state);
return; return;
} }
//M17 STR
else if (state->synctype == 16)
{
processM17STR(opts, state);
return;
}
//P25 P2 //P25 P2
else if ((state->synctype == 35) || (state->synctype == 36)) else if ((state->synctype == 35) || (state->synctype == 36))
{ {
@@ -390,8 +424,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
if (new_nac != state->nac) { if (new_nac != state->nac) {
// NAC fixed by error correction // NAC fixed by error correction
state->nac = new_nac; state->nac = new_nac;
//apparently, both 0 and 0xFFF can the BCH code on signal drop if (state->p2_hardset == 0 && new_nac != 0)
if (state->p2_hardset == 0 && new_nac != 0 && new_nac != 0xFFF)
{ {
state->p2_cc = new_nac; state->p2_cc = new_nac;
} }
@@ -423,7 +456,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
} }
if (opts->mbe_out_dir[0] != 0) if (opts->mbe_out_dir[0] != 0)
{ {
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state); if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
if (opts->mbe_out_f == NULL) 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);
@@ -571,7 +604,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
if (opts->errorbars == 1) if (opts->errorbars == 1)
{ {
printFrameInfo (opts, state); printFrameInfo (opts, state);
fprintf (stderr," MPDU\n"); //multi block format PDU fprintf (stderr," MBF\n"); //multi block format PDU
} }
if (opts->mbe_out_dir[0] != 0) if (opts->mbe_out_dir[0] != 0)
{ {
@@ -583,7 +616,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
resumeScan (opts, state); resumeScan (opts, state);
} }
state->lastp25type = 4; state->lastp25type = 4;
sprintf (state->fsubtype, " MPDU "); sprintf (state->fsubtype, " MBF ");
processMPDU(opts, state); processMPDU(opts, state);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,837 +0,0 @@
/*-------------------------------------------------------------------------------
* dsd_gps.c
* GPS Handling Functions for Various Protocols
*
* LWVMOBILE
* 2023-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input)
{
//NOTE: This is defined in ETSI TS 102 361-4 V1.12.1 (2023-07) p208
//6.6.11.3.2 USBD Polling Service Poll Response PDU for LIP (That's a mouthful)
int slot = state->currentslot;
//May need to set this in the UDT header, or pass it into this function to be sure
// uint32_t src = 0;
// if (slot == 0)
// src = state->lastsrc;
// else src = state->lastsrcR;
//NOTE: This format is pretty much the same as DMR EMB GPS, but has a few extra elements,
//so I got lazy and just lifted most of the code from there, also assuming same lat/lon calcs
//since those have been tested to work in DMR EMB GPS and units are same in LIP
fprintf (stderr, "Location Information Protocol; ");
// uint8_t service_type = (uint8_t)ConvertBitIntoBytes(&input[0], 4); //checked before arrival here
uint8_t time_elapsed = (uint8_t)ConvertBitIntoBytes(&input[6], 2);
uint8_t lon_sign = input[8];
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&input[9], 24); //8, 25
uint8_t lat_sign = input[33];
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&input[34], 23); //33, 24
uint8_t pos_err = (uint8_t)ConvertBitIntoBytes(&input[57], 2);
uint8_t hor_vel = (uint8_t)ConvertBitIntoBytes(&input[59], 7);
uint8_t dir_tra = (uint8_t)ConvertBitIntoBytes(&input[66], 4);
uint8_t reason = (uint8_t)ConvertBitIntoBytes(&input[70], 3);
uint8_t add_hash = (uint8_t)ConvertBitIntoBytes(&input[73], 8); //MS Source Address Hash
//NOTE: May need to use double instead of float to avoid rounding errors
double latitude, longitude = 0.0f;
double lat_unit = (double)180/ pow (2.0, 24); //180 divided by 2^24 -- 6.3.30
double lon_unit = (double)360/ pow (2.0, 25); //360 divided by 2^25 -- 6.3.50
double lon_sf = 1.0f; //float value we can multiple longitude with
double lat_sf = 1.0f; //float value we can multiple latitude with
char latstr[3];
char lonstr[3];
sprintf (latstr, "%s", "N");
sprintf (lonstr, "%s", "E");
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
//lat and lon calculations
if (lat_sign)
{
lat = 0x800001 - lat;
sprintf (latstr, "%s", "S");
lat_sf = -1.0f;
}
latitude = ((double)lat * lat_unit);
if (lon_sign)
{
lon = 0x1000001 - lon;
sprintf (lonstr, "%s", "W");
lon_sf = -1.0f;
}
longitude = ((double)lon * lon_unit);
//6.3.63 Position Error
//6.3.17 Horizontal velocity
/*
Horizontal velocity shall be encoded for speeds 0 km/h to 28 km/h in 1 km/h steps and
from 28 km/h onwards using equation: v = C × (1 + x)^(K-A) + B where:
*/
float v = 0.0f;
float C = 16.0f;
float x = 0.038f;
float A = 13.0f;
float K = (float)hor_vel;
float B = 0.0f;
if (hor_vel > 28)
v = C * (pow(1+x, K-A)) + B; //I think this pow function is set up correctly
else v = (float)hor_vel;
float dir = (((float)dir_tra + 11.25f)/22.5f); //page 68, Table 6.45
//truncated and rounded forms
int vt = (int)v;
int dt = (int)dir;
//sanity check
if (fabs (latitude) < 90 && fabs(longitude) < 180)
{
fprintf (stderr, "Src(Hash); %03d; Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf); Spd: %d km/h; Dir: %d%s",add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude, vt, dt, deg_glyph);
//6.3.63 Position Error
uint16_t position_error = 2 * pow(10, pos_err); //2 * 10^pos_err
if (pos_err == 0x7 ) fprintf (stderr, "\n Position Error: Unknown or Invalid;");
else fprintf (stderr, "\n Position Error: Less than %dm;", position_error);
//Reason For Sending 6.6.11.3.3 Table 6.80
if (reason) fprintf (stderr, " Reserved: %d;", reason);
else fprintf (stderr, " Request Response; ");
//6.3.78 Time elapsed
if (time_elapsed == 0) fprintf (stderr, " TE: < 5s;");
if (time_elapsed == 1) fprintf (stderr, " TE: < 5m;");
if (time_elapsed == 2) fprintf (stderr, " TE: < 30m;");
if (time_elapsed == 3) fprintf (stderr, " TE: NA or UNK;"); //not applicable or unknown
//save to array for ncurses
if (pos_err != 0x7)
{
sprintf (state->dmr_embedded_gps[slot], "%03d; LIP: %.5lf%s%s %.5lf%s%s; Err: %dm; Spd: %d km/h; Dir: %d%s", add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, position_error, vt, dt, deg_glyph);
}
else sprintf (state->dmr_embedded_gps[slot], "%03d; LIP: %.5lf%s%s %.5lf%s%s Unknown Pos Err; Spd: %d km/h; Dir %d%s", add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, vt, dt, deg_glyph);
//save to event history string
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]);
//save to LRRP report for mapping/logging
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr );
fprintf (pFile, "%08d\t", add_hash);
fprintf (pFile, "%.5lf\t", latitude);
fprintf (pFile, "%.5lf\t", longitude);
fprintf (pFile, "%d\t", vt); //speed in km/h
fprintf (pFile, "%d\t", dt); //direction of travel
fprintf (pFile, "\n");
fclose (pFile);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
}
else fprintf (stderr, " Position Calculation Error;");
}
void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int type)
{
int slot = state->currentslot;
//NOTE: The Only difference between Short (type == 1) and Long Format (type == 2)
//is the utc_ss3 on short vs utc_ss6 and inclusion of COG value on long
//NOTE: MFID Specific Formats are not handled here, unknown, could be added if worked out
//they could share most of the same elements and use the large spare bits in block 2 for extra
// uint8_t mfid = (uint8_t)ConvertBitIntoBytes(&input[88], 8); //on type 3, last octet of first block carries MFID
// uint8_t nmea_c = input[0]; //encrypted -- checked before we get here
uint8_t nmea_ns = input[1]; //north/south (lat sign)
uint8_t nmea_ew = input[2]; //east/west (lon sign)
uint8_t nmea_q = input[3]; //Quality Indicator (no fix or fix valid)
uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[4], 7); //speed in knots (127 = greater than 126 knots)
//Latitude Bits
uint8_t nmea_ndeg = (uint8_t)ConvertBitIntoBytes(&input[11], 7); //Latitude Degrees
uint8_t nmea_nmin = (uint8_t)ConvertBitIntoBytes(&input[18], 6); //Latitude Minutes
uint16_t nmea_nminf = (uint16_t)ConvertBitIntoBytes(&input[24], 14); //Latitude Fractions of Minutes
//Longitude Bits
uint8_t nmea_edeg = (uint8_t)ConvertBitIntoBytes(&input[38], 8); //Longitude Degrees
uint8_t nmea_emin = (uint8_t)ConvertBitIntoBytes(&input[46], 6); //Longitude Minutes
uint16_t nmea_eminf = (uint16_t)ConvertBitIntoBytes(&input[52], 14); //Longitude Fractions of Minutes
//UTC Time and COG
uint8_t nmea_utc_hh = (uint8_t)ConvertBitIntoBytes(&input[66], 5);
uint8_t nmea_utc_mm = (uint8_t)ConvertBitIntoBytes(&input[71], 6);
//seconds and the addition of COG is the difference between short and long formats
uint8_t nmea_utc_ss3 = (uint8_t)ConvertBitIntoBytes(&input[77], 3) * 10; //seconds in 10s
uint8_t nmea_utc_ss6 = (uint8_t)ConvertBitIntoBytes(&input[77], 6); //seconds in 1s
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[103], 9); //course over ground in degrees
//lat and lon conversion
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
float latitude = 0.0f;
float longitude = 0.0f;
// float m_unit = 1.0f / 60.0f; //unit to convert min into decimal value - (1/60)*60 minutes = 1 degree
// float mm_unit = 1.0f / 10000.0f; //unit to convert minf into decimal value - (0000 - 9999) 0.0001×9999 = .9999 minutes, so its sub 1 minute decimal
//testing w/ Harris NMEA like values (ran tests over there with this code, this seems to work on those values)
float m_unit = 1.0f / 60.0f; //unit to convert min into decimal value - (1/60)*60 minutes = 1 degree
float mm_unit = 1.0f / 600000.0f; //unit to convert minf into decimal value - (0000 - 9999) 0.0001×9999 = .9999 minutes, so its sub 1 minute decimal
//speed conversion
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
fmps = (float)nmea_speed * 0.514444; UNUSED(fmps);
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
fkph = (float)nmea_speed * 1.852f;
//calculate decimal representation of latidude and longitude (need some samples to test)
latitude = ( (float)nmea_ndeg + ((float)nmea_nmin*m_unit) + ((float)nmea_nminf*mm_unit) );
longitude = ( (float)nmea_edeg + ((float)nmea_emin*m_unit) + ((float)nmea_eminf*mm_unit) );
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph);
//Speed in Knots
if (nmea_speed > 126)
fprintf (stderr, " SPD > 126 knots or %f kph;", fkph);
else
fprintf (stderr, " SPD: %d knots; %f kph;", nmea_speed, fkph);
//Print UTC Time and COG according to Format Type
if (type == 1)
fprintf (stderr, " FIX: %d; %02d:%02d:%02d UTC; Short Format;", nmea_q, nmea_utc_hh, nmea_utc_mm, nmea_utc_ss3);
if (type == 2)
fprintf (stderr, " FIX: %d; %02d:%02d:%02d UTC; COG: %d%s; Long Format;", nmea_q, nmea_utc_hh, nmea_utc_mm, nmea_utc_ss6, nmea_cog, deg_glyph);
//save to ncurses string
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
//save to event history string
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "(%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
//save to LRRP report for mapping/logging
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
int s = (int)fkph; //rounded interger format for the log report
int a = 0;
if (type == 2)
a = nmea_cog; //long format only
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr ); //could switch to UTC time if desired, but would require local user offset
fprintf (pFile, "%08d\t", src);
fprintf (pFile, "%.6lf\t", latitude);
fprintf (pFile, "%.6lf\t", longitude);
fprintf (pFile, "%d\t ", s);
fprintf (pFile, "%d\t ", a);
fprintf (pFile, "\n");
fclose (pFile);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
}
//restructured the Harris GPS to flow more like the DMR UDT NMEA Format when possible
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot)
{
//GPS working, Time Working (old method), COG and Speed are logical on due west sample
//TODO: Fiddle/Test various ns and ew bits for proper signs in case its located elsewhere
//UPDATE: Found two contiguous bits that always work on all my accumulated samples
//the bits also place it with the 'lone' flagged bit observed prior
uint8_t nmea_ns = input[88]; //north/south (lat sign) //72 previously used bit
uint8_t nmea_ew = input[89]; //east/west (lon sign) //88 previously used bit
uint8_t nmea_q = input[147]; //Quality Indicator (last fix or current fix)
// uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 7); //129,7
uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 6); //this is one bit shorter than called for, but I believe it works
//Latitude Bits
uint8_t nmea_ndeg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //Latitude Degrees
uint8_t nmea_nmin = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //Latitude Minutes
uint16_t nmea_nminf = (uint16_t)ConvertBitIntoBytes(&input[42], 14); //Latitude Fractions of Minutes
//Longitude Bits
uint8_t nmea_edeg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //Longitude Degrees
uint8_t nmea_emin = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //Longitude Minutes
uint16_t nmea_eminf = (uint16_t)ConvertBitIntoBytes(&input[74], 14); //Longitude Fractions of Minutes
//NOTE: Removed UTC hh.mm.ss6 format found in DMR UDT, could not make it work
//with bits already established for time and it works extremely well as is
//Course Over Ground in Degrees (0-360)
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[135], 9) % 360; //course over ground in degrees //working on due west sample without %360
//timestamp (16-bit value w/ appended 17th bit to MSB)
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16);
rtime |= input[144] << 16; //this bit is flagged on after 0xFFFF rollover in the late afternoon
uint32_t thour = rtime / 3600;
uint32_t tmin = (rtime % 3600) / 60;
uint32_t tsec = (rtime % 3600) % 60;
//lat and lon conversion
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
float latitude = 0.0f;
float longitude = 0.0f;
float m_unit = 1.0f / 60.0f;
float mm_unit = 1.0f / 600000.0f;
//speed conversion
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
fmps = (float)nmea_speed * 0.514444; UNUSED(fmps);
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
fkph = (float)nmea_speed * 1.852f; UNUSED(fkph);
//calculate decimal representation of latidude and longitude (need some samples to test)
latitude = ( (float)nmea_ndeg + ((float)nmea_nmin*m_unit) + ((float)nmea_nminf*mm_unit) );
longitude = ( (float)nmea_edeg + ((float)nmea_emin*m_unit) + ((float)nmea_eminf*mm_unit) );
//This is opposite (seemingly) compared to the version specified by DMR UDT NMEA, could still be different bits involved (to be tested)
// if (nmea_ns) latitude *= -1.0f; //1 is South, 0 is North
// if (nmea_ew) longitude *= -1.0f; //1 is West, 0 is East
//DMR UDT NMEA original version (working now with new bit selection 88 and 89 like how DMR UDT NMEA format specifies)
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
//is this from LCW, or MAC?
uint16_t header = (uint16_t)ConvertBitIntoBytes(&input[0], 16);
fprintf (stderr, "\n");
if (header == 0x2AA4)
fprintf (stderr, " SRC: %08d;", src);
else
fprintf (stderr, " VCH: %d - SRC: %08d;", slot, src);
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph);
//Speed in Knots (assuming in knots, and not in mps, or kph)
if (header != 0x2AA4) //can't yet verify this is present or accurate on LCW
{
if (nmea_speed > 126)
fprintf (stderr, " SPD > 126 knots or %f MPH;", fmph); //using MPH here, its Harris, they're in the U.S.
else
fprintf (stderr, " SPD: %d knots; %f MPH;", nmea_speed, fmph);
}
//Course Over Ground (COG)
fprintf (stderr, " COG: %03d%s;", nmea_cog, deg_glyph); //%360
//debug speed
// fprintf (stderr, " RSPD: %02X;", nmea_speed);
//debug cog
// fprintf (stderr, " RCOG: %03X;", nmea_cog >> 0);
//UTC Time
fprintf (stderr, " T: %02d:%02d:%02d UTC;", thour, tmin, tsec);
//Fix Indicator
if (nmea_q) fprintf (stderr, " Current Fix;");
else fprintf (stderr, " Last Fix;");
//save to ncurses string
sprintf (state->dmr_embedded_gps[slot], "(%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
//save to event history string
if (state->event_history_s[slot].Event_History_Items[0].source_id == src && src != 0)
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]);
//save to LRRP report for mapping/logging
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
//rounded interger formats for the log report
int s = (int)fkph;
int a = nmea_cog;
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr ); //could switch to UTC time on PDU if desired
fprintf (pFile, "%08d\t", src);
fprintf (pFile, "%.6lf\t", latitude);
fprintf (pFile, "%.6lf\t", longitude);
fprintf (pFile, "%d\t ", s);
fprintf (pFile, "%d\t ", a);
fprintf (pFile, "\n");
fclose (pFile);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
//NOTE: There seems to be a few more octets left undecoded in this PDU
//not including the CRC obviously, unsure of their meaning, could relate
//to other GPS functions like precision, etc, unknown.
//NOTE2: All these values are just best effort based on observation and
//making map points match using logic and distance over time for speed and course
//without any documentation, any of these values may still be wrong
}
//fallback version (if desired/required)
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input)
{
uint8_t lat_sign, lat_deg, lat_min = 0;
uint8_t lon_sign, lon_deg, lon_min = 0;
uint16_t lat_mmin = 0;
uint16_t lon_mmin = 0;
//potentially in this PDU, but unverifiable without documentation or reasonable
//mathematical proof vs map points and direction of travel / distance over time (assuming the radio is facing that direction)
uint16_t rspeed = (uint16_t)ConvertBitIntoBytes(&input[136], 8); //MSB //136
rspeed = (rspeed << 8) + (uint16_t)ConvertBitIntoBytes(&input[128], 8); //LSB //128
//this works okay for example of driver driving due west at the speed limit of the road (fluke?)
uint16_t rangle = (uint16_t)ConvertBitIntoBytes(&input[120], 8); //120,8
float fspeed = (float)rspeed;
float fangle = (float)rangle;
fspeed /= 255.0f; //unit value of 1 bit
fspeed *= 1.56f; //this is based on an observation of a driver moving approx 210 meters in about 8 seconds and this makes it just under the speed limit on the road there
// fangle *= 360.0f/255.0f; //unit value of 1 bit
fangle *= 2.0f; //may exceed 360 degrees as is (use %)
int s, a = 0;
s = (int)fspeed * 3.6;
a = (int)fangle % 360;
//fix and quality indicators?
uint8_t fix = input[147]; UNUSED(fix);
uint8_t quality = (uint16_t)ConvertBitIntoBytes(&input[148], 3); UNUSED(quality);
//timestamp (16-bit value w/ appended 17th bit to MSB)
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16); //seconds since midnight since last GPS fix (whatever the radio has as internal time)
rtime |= input[144] << 16; //test appending this as bit 17 (observed this bit set after the afternoon 0xFFFF rollover)
uint32_t thour = rtime / 3600;
uint32_t tmin = (rtime % 3600) / 60;
uint32_t tsec = (rtime % 3600) % 60;
float lat_dec = 0.0f;
float lon_dec = 0.0f;
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
//This appears to be similar to the NMEA GPGGA format (DDmm.mm) but
//octets are ordered in least significant to most significant value
lat_mmin = (uint16_t)ConvertBitIntoBytes(&input[40], 16); //? bits required, but grabbing two octets
lat_min = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //6 bits required to get 60
lat_deg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //7 bits required to get 90
lat_sign = input[72]; //64
lon_mmin = (uint16_t)ConvertBitIntoBytes(&input[72], 16); //? bits required, but grabbing two octets
lon_min = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //6 bits required to get 60
lon_deg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //8 bits required to get 180
lon_sign = input[88]; //88, unsure of a correct location, but on the sample with 0 minutes, this lonely bit was flagged on
int src = 0;
if (slot == 0) src = state->lastsrc;
if (slot == 1) src = state->lastsrcR;
//calculate decimal representation (was a pain to figure out the sub minute values)
lat_dec = ( (float)lat_deg + ((float)lat_min/60.0f) + ((float)lat_mmin/600000.0f) );
lon_dec = ( (float)lon_deg + ((float)lon_min/60.0f) + ((float)lon_mmin/600000.0f) );
if (lat_sign)
lat_dec *= -1.0f;
if (lon_sign)
lon_dec *= -1.0f;
//line break
fprintf (stderr, "\n");
fprintf (stderr, " GPS: %f%s, %f%s;", lat_dec, deg_glyph, lon_dec, deg_glyph);
//gps fix time
// fprintf (stderr, " RT: %05X;", rtime); //debug
fprintf (stderr, " LTS: %02d:%02d:%02d UTC;", thour, tmin, tsec); //last time synced to GPS
//speed and direction
// fprintf (stderr, " RSPD: %04X;", rspeed); //debug
// fprintf (stderr, " RANG: %02X;", rangle); //debug
// fprintf (stderr, " MPS: %06.02f;", fspeed);
// fprintf (stderr, " KPH: %06.02f;", fspeed * 3.6f);
// fprintf (stderr, " MPH: %06.02f;", fspeed * 2.2369f);
fprintf (stderr, " DIR: %03d%s;", a, deg_glyph);
// fprintf (stderr, " FIX: %d", fix);
// fprintf (stderr, " QLT: %02d", quality);
// fprintf (stderr, " SRC: %08d;", src);
//save to array for ncurses
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", lat_dec, deg_glyph, lon_dec, deg_glyph);
//save to LRRP report for mapping/logging
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr );
fprintf (pFile, "%08d\t", src);
fprintf (pFile, "%.6lf\t", lat_dec);
fprintf (pFile, "%.6lf\t", lon_dec);
fprintf (pFile, "%d\t ", s);
fprintf (pFile, "%d\t ", a);
fprintf (pFile, "\n");
fclose (pFile);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
//NOTE: Thanks to DSheirer (SDRTrunk) for helping me work out a few of the things in here
//not entirely convinced on some of these calcs (speed, angle, and TS) but sure these bits
//are actual speed/direction/timestamps judging from what the coordinates show on a map
}
//externalize embedded GPS - Confirmed working now on NE, NW, SE, and SW coordinates
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
{
UNUSED(opts);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Embedded GPS:");
uint8_t slot = state->currentslot;
uint8_t pf = lc_bits[0];
uint8_t res_a = lc_bits[1];
uint8_t res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4);
uint8_t pos_err = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 3);
UNUSED2(res_a, res_b);
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
uint32_t lon_sign = lc_bits[23];
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24);
uint32_t lat_sign = lc_bits[48];
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23);
double lon_sf = 1.0f; //float value we can multiple longitude with
double lat_sf = 1.0f; //float value we can multiple latitude with
double lat_unit = (double)180/ pow (2.0, 24); //180 divided by 2^24
double lon_unit = (double)360/ pow (2.0, 25); //360 divided by 2^25
char latstr[3];
char lonstr[3];
sprintf (latstr, "%s", "N");
sprintf (lonstr, "%s", "E");
//run calculations and print
//7.2.16 and 7.2.17 (two's compliment)
double latitude = 0;
double longitude = 0;
if (pf) fprintf (stderr, " Protected");
else
{
if (lat_sign)
{
lat = 0x800001 - lat;
sprintf (latstr, "%s", "S");
lat_sf = -1.0f;
}
latitude = ((double)lat * lat_unit);
if (lon_sign)
{
lon = 0x1000001 - lon;
sprintf (lonstr, "%s", "W");
lon_sf = -1.0f;
}
longitude = ((double)lon * lon_unit);
//sanity check
if (fabs(latitude) < 90 && fabs(longitude) < 180)
{
fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf)", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude);
//7.2.15 Position Error
uint16_t position_error = 2 * pow(10, pos_err); //2 * 10^pos_err
if (pos_err == 0x7 ) fprintf (stderr, "\n Position Error: Unknown or Invalid");
else fprintf (stderr, "\n Position Error: Less than %dm", position_error);
//save to array for ncurses
if (pos_err != 0x7)
{
sprintf (state->dmr_embedded_gps[slot], "GPS: %.5lf%s%s %.5lf%s%s Err: %dm", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, position_error);
}
else sprintf (state->dmr_embedded_gps[slot], "GPS: %.5lf%s%s %.5lf%s%s Unknown Pos Err", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr);
uint32_t src = 0;
if (slot == 0) src = state->lasttg;
if (slot == 1) src = state->lasttgR;
//save to event history string
if (state->event_history_s[slot].Event_History_Items[0].source_id == src)
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]);
//save to LRRP report for mapping/logging
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr );
fprintf (pFile, "%08d\t", src);
fprintf (pFile, "%.5lf\t", latitude);
fprintf (pFile, "%.5lf\t", longitude);
fprintf (pFile, "0\t " ); //zero for velocity
fprintf (pFile, "0\t " ); //zero for azimuth
fprintf (pFile, "\n");
fclose (pFile);
}
}
}
fprintf (stderr, "%s", KNRM);
}
//This Function needs testing, is tested working for NW and NE lat and
//long coordinates, but not for SE and SW coordinates
void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " GPS:");
uint8_t slot = state->currentslot;
uint8_t pf = lc_bits[0];
uint8_t res_a = lc_bits[1];
uint8_t res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 7);
uint8_t expired = lc_bits[23]; //this bit seems to indicate that the GPS coordinates are out of date or fresh
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
uint32_t lon_sign = lc_bits[48];
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23);
uint32_t lat_sign = lc_bits[24];
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[25], 23);
double lat_unit = 90.0f / 0x7FFFFF;
double lon_unit = 180.0f / 0x7FFFFF;
char latstr[3];
char lonstr[3];
char valid[12];
sprintf (latstr, "%s", "N");
sprintf (lonstr, "%s", "E");
sprintf (valid, "%s", "Current Fix");
double latitude = 0;
double longitude = 0;
if (pf) fprintf (stderr, " Protected");
else
{
latitude = ((double)lat * lat_unit);
if (lat_sign)
{
latitude -= 90.0f;
sprintf (latstr, "%s", "S");
}
longitude = ((double)lon * lon_unit);
if (lon_sign)
{
longitude -= 180.0f;
sprintf (lonstr, "%s", "W");
}
//sanity check
if (fabs ((float)latitude) < 90 && fabs((float)longitude) < 180)
{
fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf) ", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, latitude, longitude);
if (expired)
{
fprintf (stderr, "Last Fix; ");
sprintf (valid, "%s", "Last Fix");
}
else if (!expired)
fprintf (stderr, "Current Fix; ");
if (res_a)
fprintf (stderr, "RES_A: %d; ", res_a);
if (res_b)
fprintf (stderr, "RES_B: %02X; ", res_b);
uint32_t src = 0;
if (slot == 0) src = state->lastsrc;
if (slot == 1) src = state->lastsrcR;
//save to array for ncurses
sprintf (state->dmr_embedded_gps[slot], "GPS: %lf%s%s %lf%s%s (%lf, %lf) %s", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, latitude, longitude, valid);
//save to event history string
if (state->event_history_s[slot].Event_History_Items[0].source_id == src)
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]);
//save to LRRP report for mapping/logging
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr );
fprintf (pFile, "%08d\t", src);
fprintf (pFile, "%.5lf\t", latitude);
fprintf (pFile, "%.5lf\t", longitude);
fprintf (pFile, "0\t " ); //zero for velocity
fprintf (pFile, "0\t " ); //zero for azimuth
fprintf (pFile, "\n");
fclose (pFile);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
}
}
fprintf (stderr, "%s", KNRM);
}
void decode_cellocator(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
{
//TODO: This
UNUSED(opts);
UNUSED(state);
UNUSED(input);
UNUSED(len);
//UTF8 Text: MCGP (0x4D434750) followed by data values
//(0417D1050000F45FD1DD00010000000097BDD56C81000009AAAABF12864C)
utf8_to_text(state, 0, 4, input);
fprintf (stderr, " Cellocator:");
uint8_t type = input[4];
if (type == 1) fprintf (stderr, " Platform Manifest Data;");
else if (type == 2) fprintf (stderr, " CAN Data;");
else if (type == 3) fprintf (stderr, " CAN Trigger Data;");
else if (type == 4) fprintf (stderr, " Time and Location Data;");
else if (type == 5) fprintf (stderr, " Accelerometer Data;");
else if (type == 6) fprintf (stderr, " PSP Alarm System Data;");
else if (type == 7) fprintf (stderr, " Usage Counter Data;");
else if (type == 8) fprintf (stderr, " Command Authentication Table Data;");
else if (type == 9) fprintf (stderr, " GSM Neighbor List Data;");
else if (type == 10) fprintf (stderr, " Maintenance Server Platform Manifest Data;");
else fprintf (stderr, " Unknown Data;");
//Data afterwards appears to be an arbitrary len so has variable reporting data
//will need to establish a len value for data and contents
}
void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
{
//TODO: This
UNUSED(opts);
UNUSED(state);
UNUSED(input);
UNUSED(len);
}

View File

@@ -5,8 +5,8 @@
int csvGroupImport(dsd_opts * opts, dsd_state * state) int csvGroupImport(dsd_opts * opts, dsd_state * state)
{ {
char filename[1024] = "filename.csv"; char filename[1024] = "filename.csv";
sprintf (filename, "%s", opts->group_in_file); sprintf (filename, "%s", opts->group_in_file);
//filename[1023] = '\0'; //necessary? //filename[1023] = '\0'; //necessary?
char buffer[BSIZE]; char buffer[BSIZE];
FILE * fp; FILE * fp;
@@ -18,7 +18,6 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
int row_count = 0; int row_count = 0;
int field_count = 0; int field_count = 0;
long int group_number = 0; //local group number for array index value long int group_number = 0; //local group number for array index value
UNUSED(group_number);
int i = 0; int i = 0;
while (fgets(buffer, BSIZE, fp)) { while (fgets(buffer, BSIZE, fp)) {
field_count = 0; field_count = 0;
@@ -27,7 +26,7 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
continue; //don't want labels continue; //don't want labels
char * field = strtok(buffer, ","); //seperate by comma char * field = strtok(buffer, ","); //seperate by comma
while (field) { while (field) {
if (field_count == 0) if (field_count == 0)
{ {
//group_number = atol(field); //group_number = atol(field);
@@ -44,23 +43,23 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
strcpy(state->group_array[i].groupName, field); strcpy(state->group_array[i].groupName, field);
fprintf (stderr, "%s ", state->group_array[i].groupName); fprintf (stderr, "%s ", state->group_array[i].groupName);
} }
field = strtok(NULL, ","); field = strtok(NULL, ",");
field_count++; field_count++;
} }
fprintf (stderr, "\n"); fprintf (stderr, "\n");
i++; i++;
state->group_tally++; state->group_tally++;
} }
fclose(fp); fclose(fp);
return 0; return 0;
} }
//LCN import for EDACS, migrated to channel map (channel map does both) //LCN import for EDACS, migrated to channel map (channel map does both)
int csvLCNImport(dsd_opts * opts, dsd_state * state) int csvLCNImport(dsd_opts * opts, dsd_state * state)
{ {
char filename[1024] = "filename.csv"; char filename[1024] = "filename.csv";
sprintf (filename, "%s", opts->lcn_in_file); sprintf (filename, "%s", opts->lcn_in_file);
//filename[1023] = '\0'; //necessary? //filename[1023] = '\0'; //necessary?
char buffer[BSIZE]; char buffer[BSIZE];
FILE * fp; FILE * fp;
@@ -86,12 +85,12 @@ int csvLCNImport(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "LCN [%d] [%ld]", field_count+1, state->trunk_lcn_freq[field_count]); fprintf (stderr, "LCN [%d] [%ld]", field_count+1, state->trunk_lcn_freq[field_count]);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
field = strtok(NULL, ","); field = strtok(NULL, ",");
field_count++; field_count++;
} }
fprintf (stderr, "LCN Count %d\n", state->lcn_freq_count); fprintf (stderr, "LCN Count %d\n", state->lcn_freq_count);
} }
fclose(fp); fclose(fp);
return 0; return 0;
@@ -99,7 +98,7 @@ int csvLCNImport(dsd_opts * opts, dsd_state * state)
int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import
{ {
char filename[1024] = "filename.csv"; char filename[1024] = "filename.csv";
sprintf (filename, "%s", opts->chan_in_file); sprintf (filename, "%s", opts->chan_in_file);
char buffer[BSIZE]; char buffer[BSIZE];
@@ -135,22 +134,21 @@ int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import
sscanf (field, "%ld", &state->trunk_lcn_freq[state->lcn_freq_count]); sscanf (field, "%ld", &state->trunk_lcn_freq[state->lcn_freq_count]);
state->lcn_freq_count++; //keep tally of number of Frequencies imported state->lcn_freq_count++; //keep tally of number of Frequencies imported
} }
field = strtok(NULL, ","); field = strtok(NULL, ",");
field_count++; field_count++;
} }
fprintf (stderr, "Channel [%05ld] [%09ld]", chan_number, state->trunk_chan_map[chan_number]); fprintf (stderr, "Channel [%05ld] [%09ld]", chan_number, state->trunk_chan_map[chan_number]);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
fclose(fp); fclose(fp);
return 0; return 0;
} }
//Decimal Variant of Key Import int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support
int csvKeyImportDec(dsd_opts * opts, dsd_state * state) //multi-key support
{ {
char filename[1024] = "filename.csv"; char filename[1024] = "filename.csv";
sprintf (filename, "%s", opts->key_in_file); sprintf (filename, "%s", opts->key_in_file);
char buffer[BSIZE]; char buffer[BSIZE];
@@ -162,7 +160,7 @@ int csvKeyImportDec(dsd_opts * opts, dsd_state * state) //multi-key support
} }
int row_count = 0; int row_count = 0;
int field_count = 0; int field_count = 0;
unsigned long long int keynumber = 0; unsigned long long int keynumber = 0;
unsigned long long int keyvalue = 0; unsigned long long int keyvalue = 0;
@@ -186,13 +184,13 @@ int csvKeyImportDec(dsd_opts * opts, dsd_state * state) //multi-key support
keynumber = keynumber & 0xFFFFFF; //truncate to 24-bits (max allowed) keynumber = keynumber & 0xFFFFFF; //truncate to 24-bits (max allowed)
for (int i = 0; i < 24; i++) for (int i = 0; i < 24; i++)
{ {
hash_bits[i] = ((keynumber << i) & 0x800000) >> 23; //load into array for CRC16 hash_bits[i] = ((keynumber << i) & 0x800000) >> 23; //load into array for CRC16
} }
hash = ComputeCrcCCITT16d (hash_bits, 24); hash = ComputeCrcCCITT16d (hash_bits, 24);
keynumber = hash & 0xFFFF; //make sure its no larger than 16-bits keynumber = hash & 0xFFFF; //make sure its no larger than 16-bits
fprintf (stderr, "Hashed "); fprintf (stderr, "Hashed ");
} }
} }
if (field_count == 1) if (field_count == 1)
@@ -200,80 +198,14 @@ int csvKeyImportDec(dsd_opts * opts, dsd_state * state) //multi-key support
sscanf (field, "%lld", &keyvalue); sscanf (field, "%lld", &keyvalue);
state->rkey_array[keynumber] = keyvalue & 0xFFFFFFFFFF; //doesn't exceed 40-bit value state->rkey_array[keynumber] = keyvalue & 0xFFFFFFFFFF; //doesn't exceed 40-bit value
} }
field = strtok(NULL, ","); field = strtok(NULL, ",");
field_count++; field_count++;
} }
fprintf (stderr, "Key [%03lld] [%05lld]", keynumber, state->rkey_array[keynumber]); fprintf (stderr, "Key [%03lld] [%05lld]", keynumber, state->rkey_array[keynumber]);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
hash = 0; hash = 0;
}
fclose(fp);
return 0;
}
//Hex Variant of Key Import
int csvKeyImportHex(dsd_opts * opts, dsd_state * state) //key import for hex keys
{
char filename[1024] = "filename.csv";
sprintf (filename, "%s", opts->key_in_file);
char buffer[BSIZE];
FILE * fp;
fp = fopen(filename, "r");
if (fp == NULL) {
printf("Unable to open file '%s'\n", filename);
exit(1);
}
int row_count = 0;
int field_count = 0;
unsigned long long int keynumber;
while (fgets(buffer, BSIZE, fp)) {
field_count = 0;
row_count++;
if (row_count == 1)
continue; //don't want labels
char * field = strtok(buffer, ","); //seperate by comma
while (field) {
if (field_count == 0)
{
sscanf (field, "%llX", &keynumber);
}
if (field_count == 1)
{
sscanf (field, "%llX", &state->rkey_array[keynumber]);
}
//this could also theoretically nuke other keys that are at the same offset
if (field_count == 2)
{
sscanf (field, "%llX", &state->rkey_array[keynumber+0x101]);
}
if (field_count == 3)
{
sscanf (field, "%llX", &state->rkey_array[keynumber+0x201]);
}
if (field_count == 4)
{
sscanf (field, "%llX", &state->rkey_array[keynumber+0x301]);
}
field = strtok(NULL, ",");
field_count++;
}
fprintf (stderr, "Key [%04llX] [%016llX]", keynumber, state->rkey_array[keynumber]);
//if longer key is loaded (or clash with the 0x101, 0x201, 0x301 offset, then print the full key listing)
if ( (state->rkey_array[keynumber+0x101] != 0) || (state->rkey_array[keynumber+0x201] != 0) || (state->rkey_array[keynumber+0x301] != 0) )
fprintf (stderr, " [%016llX] [%016llX] [%016llX]", state->rkey_array[keynumber+0x101], state->rkey_array[keynumber+0x201], state->rkey_array[keynumber+0x301]);
fprintf (stderr, "\n");
} }
fclose(fp); fclose(fp);
return 0; return 0;

View File

@@ -1,7 +0,0 @@
/*-------------------------------------------------------------------------------
* dsd_ks.c
* Placeholder File 6 -- This file is for temporary code storage
*
*
*
*-----------------------------------------------------------------------------*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,179 +0,0 @@
/*-------------------------------------------------------------------------------
*
*
*
* TODO: Fill me in'
*
*
* LWVMOBILE
* 2023-07 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
//new and simplified/organized ambe and imbe handling
//moving all audio handling and decryption to seperate files for simplicity (eventually)
#include "dsd.h"
//the initial functions will ONLY return demodulated ambe or imbe frames, THAT'S IT!
//decryption and audio handling etc will be handled at a different area
//using soft_demod will also allow for not compiling mbelib and also using a DVstick in the future
//P25p1 IMBE 7200 or AMBE+2 EFR
void soft_demod_imbe7200 (dsd_state * state, char imbe_fr7200[8][23], char imbe_d[88])
{
state->errs = mbe_eccImbe7200x4400C0 (imbe_fr7200);
state->errs2 = state->errs;
mbe_demodulateImbe7200x4400Data (imbe_fr7200);
state->errs2 += mbe_eccImbe7200x4400Data (imbe_fr7200, imbe_d);
state->debug_audio_errors += state->errs2;
}
//ProVoice IMBE 7100
void soft_demod_imbe7100 (dsd_state * state, char imbe_fr7100[7][24], char imbe_d[88])
{
state->errs = mbe_eccImbe7100x4400C0 (imbe_fr7100);
state->errs2 = state->errs;
mbe_demodulateImbe7100x4400Data (imbe_fr7100);
state->errs2 += mbe_eccImbe7100x4400Data (imbe_fr7100, imbe_d);
state->debug_audio_errors += state->errs2;
}
//AMBE+2 EHR
void soft_demod_ambe2_ehr(dsd_state * state, char ambe2_ehr[4][24], char ambe_d[49])
{
state->errs = mbe_eccAmbe3600x2450C0 (ambe2_ehr);
state->errs2 = state->errs;
mbe_demodulateAmbe3600x2450Data (ambe2_ehr);
state->errs2 += mbe_eccAmbe3600x2450Data (ambe2_ehr, ambe_d);
}
//AMBE One Shot (DSTAR)
void soft_demod_ambe_dstar(dsd_opts * opts, dsd_state * state, char ambe_fr[4][24], char ambe_d[49])
{
mbe_processAmbe3600x2400Framef (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->floating_point == 1)
memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l));
else processAudio(opts, state);
}
//AMBE+2 One Shot (X2-TDMA)
void soft_demod_ambe_x2(dsd_opts * opts, dsd_state * state, char ambe_fr[4][24], char ambe_d[49])
{
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->floating_point == 1)
memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l));
else processAudio(opts, state);
}
void soft_mbe (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
{
int i;
char ambe_d[49];
char imbe_d[88];
int slot = state->currentslot;
memset (ambe_d, 0, sizeof(ambe_d));
memset (imbe_d, 0, sizeof(imbe_d));
UNUSED(i);
//P25p1, YSF FR, NXDN EFR
if (state->synctype == 0 || state->synctype == 1)
{
soft_demod_imbe7200(state, imbe_fr, imbe_d);
//handle decryption here
//print IMBE frame
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);
//send to playback here
}
//ProVoice
else if (state->synctype == 14 || state->synctype == 15)
{
soft_demod_imbe7100(state, imbe7100_fr, imbe_d);
//nothing to do
//convert to 7200
mbe_convertImbe7100to7200(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);
//send to playback here
}
//D-STAR AMBE
else if (state->synctype == 6 || state->synctype == 7)
{
soft_demod_ambe_dstar(opts, state, ambe_fr, ambe_d);
if(opts->payload == 1)
PrintAMBEData (opts, state, ambe_d);
if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1)
playSynthesizedVoiceMS(opts, state);
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1)
playSynthesizedVoiceFM(opts, state);
if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceSS(opts, state);
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceFS(opts, state);
if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1)
writeSynthesizedVoice (opts, state);
//single wav file handled by playSynthesizedVoiceMS
if (opts->mbe_out_f != NULL)
saveAmbe2450Data (opts, state, ambe_d);
}
//X2-TDMA AMBE
else if (state->synctype >= 2 && state->synctype <= 5)
{
soft_demod_ambe_x2(opts, state, ambe_fr, ambe_d);
if(opts->payload == 1)
PrintAMBEData (opts, state, ambe_d);
if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1)
playSynthesizedVoiceMS(opts, state);
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1)
playSynthesizedVoiceFM(opts, state);
if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceSS(opts, state);
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceFS(opts, state);
if (opts->wav_out_f != NULL)
writeSynthesizedVoice (opts, state);
if (opts->mbe_out_f != NULL)
saveAmbe2450Data (opts, state, ambe_d);
}
//AMBE+2 EHR (NXDN, DMR, P25p2, YSF VD/1)
else
{
soft_demod_ambe2_ehr (state, ambe_fr, ambe_d);
//decrypt here
if(opts->payload == 1)
PrintAMBEData (opts, state, ambe_d);
//make left or right channel decision
if (slot == 0)
{
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 (slot == 1)
{
mbe_processAmbe2450Dataf (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR,
ambe_d, state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2, opts->uvquality);
}
}
}

View File

@@ -1,721 +0,0 @@
/*-------------------------------------------------------------------------------
* dsd_misc.c
* Misc Code that needs to be reorganized and sorted out
*
*
*
*-----------------------------------------------------------------------------*/
#include "dsd.h"
static const int PARITY[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};
// trellis_1_2 encode: source is in bits, result in bits
void trellis_encode(uint8_t result[], const uint8_t source[], int result_len, int reg)
{
for (int i=0; i<result_len; i+=2) {
reg = (reg << 1) | source[i>>1];
result[i] = PARITY[reg & 0x19];
result[i+1] = PARITY[reg & 0x17];
}
}
// simplified trellis 2:1 decode; source and result in bits
// assumes that encoding was done with NTEST trailing zero bits
// result_len should be set to the actual number of data bits
// in the original unencoded message (excl. these trailing bits)
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len)
{
int reg = 0;
int min_d;
int min_bt;
static const int NTEST = 4;
static const int NTESTC = 1 << NTEST;
uint8_t bt[NTEST];
uint8_t tt[NTEST*2];
int dstats[4];
int sum;
for (int p=0; p < 4; p++)
dstats[p] = 0;
for (int p=0; p < result_len; p++) {
for (int i=0; i<NTESTC; i++) {
bt[0] = (i&8)>>3;
bt[1] = (i&4)>>2;
bt[2] = (i&2)>>1;
bt[3] = (i&1);
trellis_encode(tt, bt, NTEST*2, reg);
sum=0;
for (int j=0; j<NTEST*2; j++) {
sum += tt[j] ^ source[p*2+j];
}
if (i == 0 || sum < min_d) {
min_d = sum;
min_bt = bt[0];
}
}
result[p] = min_bt;
reg = (reg << 1) | min_bt;
dstats[(min_d > 3) ? 3 : min_d] += 1;
}
//debug output
// fprintf (stderr, "\n stats\t%d %d %d %d\n", dstats[0], dstats[1], dstats[2], dstats[3]);
}
//Original Copyright/License
/* -*- c++ -*- */
/*
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
*
* This file is part of OP25
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
//Ripped from libM17
#define K 5 //constraint length
#define NUM_STATES (1 << (K - 1)) //number of states
static uint32_t prevMetrics[NUM_STATES];
static uint32_t currMetrics[NUM_STATES];
static uint32_t prevMetricsData[NUM_STATES];
static uint32_t currMetricsData[NUM_STATES];
static uint16_t viterbi_history[244];
/**
* @brief Decode unpunctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param len Input length in bits.
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
{
if(len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
viterbi_reset();
size_t pos = 0;
for(size_t i = 0; i < len; i += 2)
{
uint16_t s0 = in[i];
uint16_t s1 = in[i + 1];
viterbi_decode_bit(s0, s1, pos);
pos++;
}
uint32_t err = viterbi_chainback(out, pos, len/2);
//debug
// fprintf (stderr, "\n vcb: \n");
// for (size_t i = 0; i < len; i++)
// fprintf (stderr, "%02X", out[i]);
return err;
}
/**
* @brief Decode punctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param punct Puncturing matrix.
* @param in_len Input data length.
* @param p_len Puncturing matrix length (entries).
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
{
if(in_len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
uint16_t umsg[244*2]; //unpunctured message
uint8_t p=0; //puncturer matrix entry
uint16_t u=0; //bits count - unpunctured message
uint16_t i=0; //bits read from the input message
while(i<in_len)
{
if(punct[p])
{
umsg[u]=in[i];
i++;
}
else
{
umsg[u]=0x7FFF;
}
u++;
p++;
p%=p_len;
}
//debug
// fprintf (stderr, " p: %d, u: %d; p_len: %d; len: %d;", p, u, p_len, (u-in_len)*0x7FFF);
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
}
/**
* @brief Decode one bit and update trellis.
*
* @param s0 Cost of the first symbol.
* @param s1 Cost of the second symbol.
* @param pos Bit position in history.
*/
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos)
{
static const uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
static const uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0, 0, 0xFFFF, 0xFFFF, 0};
for(uint8_t i = 0; i < NUM_STATES/2; i++)
{
uint32_t metric = q_abs_diff(COST_TABLE_0[i], s0)
+ q_abs_diff(COST_TABLE_1[i], s1);
uint32_t m0 = prevMetrics[i] + metric;
uint32_t m1 = prevMetrics[i + NUM_STATES/2] + (0x1FFFE - metric);
uint32_t m2 = prevMetrics[i] + (0x1FFFE - metric);
uint32_t m3 = prevMetrics[i + NUM_STATES/2] + metric;
uint8_t i0 = 2 * i;
uint8_t i1 = i0 + 1;
if(m0 >= m1)
{
viterbi_history[pos]|=(1<<i0);
currMetrics[i0] = m1;
}
else
{
viterbi_history[pos]&=~(1<<i0);
currMetrics[i0] = m0;
}
if(m2 >= m3)
{
viterbi_history[pos]|=(1<<i1);
currMetrics[i1] = m3;
}
else
{
viterbi_history[pos]&=~(1<<i1);
currMetrics[i1] = m2;
}
}
//swap
uint32_t tmp[NUM_STATES];
for(uint8_t i=0; i<NUM_STATES; i++)
{
tmp[i]=currMetrics[i];
}
for(uint8_t i=0; i<NUM_STATES; i++)
{
currMetrics[i]=prevMetrics[i];
prevMetrics[i]=tmp[i];
}
}
/**
* @brief History chainback to obtain final byte array.
*
* @param out Destination byte array for decoded data.
* @param pos Starting position for the chainback.
* @param len Length of the output in bits.
* @return Minimum Viterbi cost at the end of the decode sequence.
*/
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
{
uint8_t state = 0;
size_t bitPos = len+4;
memset(out, 0, (len-1)/8+1);
while(pos > 0)
{
bitPos--;
pos--;
uint16_t bit = viterbi_history[pos]&((1<<(state>>4)));
state >>= 1;
if(bit)
{
state |= 0x80;
out[bitPos/8]|=1<<(7-(bitPos%8));
}
}
uint32_t cost = prevMetrics[0];
for(size_t i = 0; i < NUM_STATES; i++)
{
uint32_t m = prevMetrics[i];
if(m < cost) cost = m;
}
//debug
// fprintf (stderr, "\n cb: \n");
// for (size_t i = 0; i < len; i++)
// fprintf (stderr, "%02X", out[i]);
return cost;
}
/**
* @brief Reset the decoder state. No args.
*
*/
void viterbi_reset(void)
{
memset((uint8_t*)viterbi_history, 0, 2*244);
memset((uint8_t*)currMetrics, 0, 4*NUM_STATES);
memset((uint8_t*)prevMetrics, 0, 4*NUM_STATES);
memset((uint8_t*)currMetricsData, 0, 4*NUM_STATES);
memset((uint8_t*)prevMetricsData, 0, 4*NUM_STATES);
}
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
{
if(v2 > v1) return v2 - v1;
return v1 - v2;
}
//original sources
//--------------------------------------------------------------------
// M17 C library - decode/viterbi.c
//
// This file contains:
// - the Viterbi decoder
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// M17 C library - math/math.c
//
// This file contains:
// - absolute difference value
// - Euclidean norm (L2) calculation for n-dimensional vectors (float)
// - soft-valued arrays to integer conversion (and vice-versa)
// - fixed-valued multiplication and division
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------
//audio filter stuff sourced from: https://github.com/NedSimao/FilteringLibrary
//no license / information provided in source code
#define PI 3.141592653
//do we need this for test input sthit?
// #define PLOT_POINTS 100
// #define SAMPLE_TIME_S 0.001
// #define CUTOFFFREQ_HZ 50
// #define sine_freq 40
// #define sine_freq_1 500
// #define HFC 50
// #define LFC 100
void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
{
float RC=0.0;
RC=1.0/(2*PI*cutoffFreqHz);
filter->coef[0]=sampleTimeS/(sampleTimeS+RC);
filter->coef[1]=RC/(sampleTimeS+RC);
filter->v_out[0]=0.0;
filter->v_out[1]=0.0;
}
float LPFilter_Update(LPFilter *filter, float v_in)
{
filter->v_out[1]=filter->v_out[0];
filter->v_out[0]=(filter->coef[0]*v_in) + (filter->coef[1]*filter->v_out[1]);
return (filter->v_out[0]);
}
/********************************************************************************************************
* HIGH PASS FILTER
********************************************************************************************************/
void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS)
{
float RC=0.0;
RC=1.0/(2*PI*cutoffFreqHz);
filter->coef=RC/(sampleTimeS+RC);
filter->v_in[0]=0.0;
filter->v_in[1]=0.0;
filter->v_out[0]=0.0;
filter->v_out[1]=0.0;
}
float HPFilter_Update(HPFilter *filter, float v_in)
{
filter->v_in[1]=filter->v_in[0];
filter->v_in[0]=v_in;
filter->v_out[1]=filter->v_out[0];
filter->v_out[0]=filter->coef * (filter->v_in[0] - filter->v_in[1]+filter->v_out[1]);
return (filter->v_out[0]);
}
/********************************************************************************************************
* BAND PASS FILTER
********************************************************************************************************/
void PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS)
{
LPFilter_Init(&filter->lpf, LPF_cutoffFreqHz, sampleTimeS);
HPFilter_Init(&filter->hpf, HPF_cutoffFreqHz, sampleTimeS);
filter->out_in=0.0;
}
float PBFilter_Update(PBFilter *filter, float v_in)
{
filter->out_in=HPFilter_Update(&filter->hpf, v_in);
filter->out_in=LPFilter_Update(&filter->lpf, filter->out_in);
return (filter->out_in);
}
/********************************************************************************************************
* NOTCH FILTER
********************************************************************************************************/
void NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS)
{
//filter frequency to angular (rad/s)
float w0_rps=2.0 * PI *centerFreqHz;
float ww_rps=2.0 * PI *notchWidthHz;
//pre warp center frequency
float w0_pw_rps=(2.0/sampleTimeS) * tanf(0.5 * w0_rps * sampleTimeS);
//computing filter coefficients
filter->alpha=4.0 + w0_rps*w0_pw_rps*sampleTimeS*sampleTimeS;
filter->beta=2.0*ww_rps*sampleTimeS;
//clearing input and output buffers
for (uint8_t n=0; n<3; n++)
{
filter->vin[n]=0;
filter->vout[n]=0;
}
}
float NOTCHFilter_Update(NOTCHFilter *filter, float vin)
{
//shifting samples
filter->vin[2]=filter->vin[1];
filter->vin[1]=filter->vin[0];
filter->vout[2]=filter->vout[1];
filter->vout[1]=filter->vout[0];
filter->vin[0]=vin;
//compute new output
filter->vout[0]=(filter->alpha*filter->vin[0] + 2.0 *(filter->alpha -8.0)*filter->vin[1] + filter->alpha*filter->vin[2]
-(2.0f*(filter->alpha-8.0)*filter->vout[1]+(filter->alpha-filter->beta)*filter->vout[2]))/(filter->alpha+filter->beta);
return (filter->vout[0]);
}
void init_audio_filters (dsd_state * state)
{
//still not sure if this is even correct or not, but 48k sounds good now
LPFilter_Init(&state->RCFilter, 960, (float)1/(float)48000);
HPFilter_Init(&state->HRCFilter, 960, (float)1/(float)48000);
//left and right variants for stereo output testing on digital voice samples
LPFilter_Init(&state->RCFilterL, 960, (float)1/(float)16000);
HPFilter_Init(&state->HRCFilterL, 960, (float)1/(float)16000);
LPFilter_Init(&state->RCFilterR, 960, (float)1/(float)16000);
HPFilter_Init(&state->HRCFilterR, 960, (float)1/(float)16000);
//PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS);
//NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS);
//NOTE: PBFilter_init also inits a LPF and HPF, but on another set of filters, might be worth
//testing just using the PBFilter by itself and see how it does without the hpf used
//passband filter working (seems to be), notch filter unsure which values to use, doesn't have any appreciable affect when used as is
PBFilter_Init(&state->PBF, 8000, 12000, (float)1/(float)1536000); //RTL Sampling at 1536000 S/s.
NOTCHFilter_Init(&state->NF, 1000, 4000, (float)1/(float)1536000);
}
//FUNCTIONS for handing use of above filters
//lpf
void lpf(dsd_state * state, short * input, int len)
{
int i;
for (i = 0; i < len; i++)
{
// fprintf (stderr, "\n in: %05d", input[i]);
input[i] = LPFilter_Update(&state->RCFilter, input[i]);
// fprintf (stderr, "\n out: %05d", input[i]);
}
}
//hpf
void hpf(dsd_state * state, short * input, int len)
{
int i;
for (i = 0; i < len; i++)
{
// fprintf (stderr, "\n in: %05d", input[i]);
input[i] = HPFilter_Update(&state->HRCFilter, input[i]);
// fprintf (stderr, "\n out: %05d", input[i]);
}
}
//hpf digital left
void hpf_dL(dsd_state * state, short * input, int len)
{
int i;
for (i = 0; i < len; i++)
{
// fprintf (stderr, "\n in: %05d", input[i]);
input[i] = HPFilter_Update(&state->HRCFilterL, input[i]);
// fprintf (stderr, "\n out: %05d", input[i]);
}
}
//hpf digital right
void hpf_dR(dsd_state * state, short * input, int len)
{
int i;
for (i = 0; i < len; i++)
{
// fprintf (stderr, "\n in: %05d", input[i]);
input[i] = HPFilter_Update(&state->HRCFilterR, input[i]);
// fprintf (stderr, "\n out: %05d", input[i]);
}
}
//nf
void nf(dsd_state * state, short * input, int len)
{
int i;
for (i = 0; i < len; i++)
{
// fprintf (stderr, "\n in: %05d", input[i]);
input[i] = NOTCHFilter_Update(&state->NF, input[i]);
// fprintf (stderr, "\n out: %05d", input[i]);
}
}
//pbf
void pbf(dsd_state * state, short * input, int len)
{
int i;
for (i = 0; i < len; i++)
{
// fprintf (stderr, "\n in: %05d", input[i]);
input[i] = PBFilter_Update(&state->PBF, input[i]);
}
}
//Generic RMS function derived from RTL_FM (RTL_SDR) RMS code (doesnt' really work correctly outside of RTL)
long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as len
{
int i;
long int rms;
long p, t, s;
double dc, err;
p = t = 0L;
for (i=0; i<len; i+=step) {
s = (long)samples[i];
t += s;
p += s * s;
}
/* correct for dc offset in squares */
dc = (double)(t*step) / (double)len;
err = t * 2 * dc - dc * dc * len;
rms = (long int)sqrt((p-err) / len);
if (rms < 0) rms = 150;
return rms;
}
//same as symbol levels in m17.h (m17-fme version)
const int8_t symbol_list[4]={-3, -1, +1, +3};
/**
* @brief Slice payload symbols into soft dibits.
* Input (RRC filtered baseband sampled at symbol centers)
* should be already normalized to {-3, -1, +1 +3}.
* @param out Soft valued dibits (type-4).
* @param inp Array of 184 floats (1 sample per symbol).
*/
void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD])
{
for(uint8_t i=0; i<SYM_PER_PLD; i++)
{
//bit 0
if(inp[i]>=symbol_list[3])
{
out[i*2+1]=0xFFFF;
}
else if(inp[i]>=symbol_list[2])
{
out[i*2+1]=-(float)0xFFFF/(symbol_list[3]-symbol_list[2])*symbol_list[2]+inp[i]*(float)0xFFFF/(symbol_list[3]-symbol_list[2]);
}
else if(inp[i]>=symbol_list[1])
{
out[i*2+1]=0x0000;
}
else if(inp[i]>=symbol_list[0])
{
out[i*2+1]=(float)0xFFFF/(symbol_list[1]-symbol_list[0])*symbol_list[1]-inp[i]*(float)0xFFFF/(symbol_list[1]-symbol_list[0]);
}
else
{
out[i*2+1]=0xFFFF;
}
//bit 1
if(inp[i]>=symbol_list[2])
{
out[i*2]=0x0000;
}
else if(inp[i]>=symbol_list[1])
{
out[i*2]=0x7FFF-inp[i]*(float)0xFFFF/(symbol_list[2]-symbol_list[1]);
}
else
{
out[i*2]=0xFFFF;
}
}
}
/**
* @brief Soft logic NOT.
*
* @param a Input A.
* @return uint16_t Output = not A.
*/
uint16_t soft_bit_NOT(const uint16_t a)
{
return 0xFFFFU-a;
}
//randomizing pattern
const uint8_t rand_seq[46]=
{
0xD6, 0xB5, 0xE2, 0x30, 0x82, 0xFF, 0x84, 0x62, 0xBA, 0x4E, 0x96, 0x90, 0xD8, 0x98, 0xDD, 0x5D, 0x0C, 0xC8, 0x52, 0x43, 0x91, 0x1D, 0xF8,
0x6E, 0x68, 0x2F, 0x35, 0xDA, 0x14, 0xEA, 0xCD, 0x76, 0x19, 0x8D, 0xD5, 0x80, 0xD1, 0x33, 0x87, 0x13, 0x57, 0x18, 0x2D, 0x29, 0x78, 0xC3
};
/**
* @brief Randomize type-4 soft bits.
*
* @param inp Input 368 soft type-4 bits.
*/
void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2])
{
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
{
if((rand_seq[i/8]>>(7-(i%8)))&1) //flip bit if '1'
{
inp[i]=soft_bit_NOT(inp[i]);
}
}
}
//interleaver pattern
const uint16_t intrl_seq[SYM_PER_PLD*2]=
{
0, 137, 90, 227, 180, 317, 270, 39, 360, 129, 82, 219, 172, 309, 262, 31,
352, 121, 74, 211, 164, 301, 254, 23, 344, 113, 66, 203, 156, 293, 246, 15,
336, 105, 58, 195, 148, 285, 238, 7, 328, 97, 50, 187, 140, 277, 230, 367,
320, 89, 42, 179, 132, 269, 222, 359, 312, 81, 34, 171, 124, 261, 214, 351,
304, 73, 26, 163, 116, 253, 206, 343, 296, 65, 18, 155, 108, 245, 198, 335,
288, 57, 10, 147, 100, 237, 190, 327, 280, 49, 2, 139, 92, 229, 182, 319,
272, 41, 362, 131, 84, 221, 174, 311, 264, 33, 354, 123, 76, 213, 166, 303,
256, 25, 346, 115, 68, 205, 158, 295, 248, 17, 338, 107, 60, 197, 150, 287,
240, 9, 330, 99, 52, 189, 142, 279, 232, 1, 322, 91, 44, 181, 134, 271,
224, 361, 314, 83, 36, 173, 126, 263, 216, 353, 306, 75, 28, 165, 118, 255,
208, 345, 298, 67, 20, 157, 110, 247, 200, 337, 290, 59, 12, 149, 102, 239,
192, 329, 282, 51, 4, 141, 94, 231, 184, 321, 274, 43, 364, 133, 86, 223,
176, 313, 266, 35, 356, 125, 78, 215, 168, 305, 258, 27, 348, 117, 70, 207,
160, 297, 250, 19, 340, 109, 62, 199, 152, 289, 242, 11, 332, 101, 54, 191,
144, 281, 234, 3, 324, 93, 46, 183, 136, 273, 226, 363, 316, 85, 38, 175,
128, 265, 218, 355, 308, 77, 30, 167, 120, 257, 210, 347, 300, 69, 22, 159,
112, 249, 202, 339, 292, 61, 14, 151, 104, 241, 194, 331, 284, 53, 6, 143,
96, 233, 186, 323, 276, 45, 366, 135, 88, 225, 178, 315, 268, 37, 358, 127,
80, 217, 170, 307, 260, 29, 350, 119, 72, 209, 162, 299, 252, 21, 342, 111,
64, 201, 154, 291, 244, 13, 334, 103, 56, 193, 146, 283, 236, 5, 326, 95,
48, 185, 138, 275, 228, 365, 318, 87, 40, 177, 130, 267, 220, 357, 310, 79,
32, 169, 122, 259, 212, 349, 302, 71, 24, 161, 114, 251, 204, 341, 294, 63,
16, 153, 106, 243, 196, 333, 286, 55, 8, 145, 98, 235, 188, 325, 278, 47
};
/**
* @brief Reorder (interleave) 368 soft bits.
*
* @param outp Reordered soft bits.
* @param inp Input soft bits.
*/
void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_PLD*2])
{
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
outp[i]=inp[intrl_seq[i]];
}

3532
src/dsd_ncurses.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,925 +0,0 @@
/*-------------------------------------------------------------------------------
* dsd_ncurses_handler.c
* DSD-FME ncurses terminal user input handler
*
* LWVMOBILE
* 2025-05 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
{
struct stat st_wav = {0};
// //debug char value
// if (c != -1)
// fprintf (stderr, "\n User Input: %i / %c ;;", c, c);
//TEST: Handle Escape Characters
//if issues arise, just go back to the way it was before
//this is primarly a fix for scroll wheel activatign the menu
//and Windows Powershell right-click doing a copy and paste
//and sendign tons of garbage getch chars here and changing things
if (c == '\033')
{
//TODO: Find way to getch all the chars immediately after, or just
//run getch in a loop in case of somebody copying and pasting a ton of things accidentally
for (int i = 0; i < 100000; i++) //wonder how slow this will be
getch();
c = -1;
return 1;
}
//keyboard shortcuts - codes same as ascii codes
if (c == 10) //Return / Enter key, open menu
ncursesMenu (opts, state);
//use k and l keys to test tg hold toggles on slots 1 and slots 2
if (c == 107) //'k' key, hold tg on slot 1 for trunking purposes, or toggle clear
{
if (state->tg_hold == 0)
state->tg_hold = state->lasttg;
else state->tg_hold = 0;
if ( (opts->frame_nxdn48 == 1 || opts->frame_nxdn96 == 1) && (state->tg_hold == 0) )
state->tg_hold = state->nxdn_last_tg;
else if (opts->frame_provoice == 1 && state->ea_mode == 0)
state->tg_hold = state->lastsrc;
}
if (c == 108) //'l' key, hold tg on slot 2 for trunking purposes, or toggle clear
{
if (state->tg_hold == 0)
state->tg_hold = state->lasttgR;
else state->tg_hold = 0;
}
//toggling when 48k/1 OSS still has some lag -- needed to clear out the buffer when switching
if (c == 49) // '1' key, toggle slot1 on
{
//switching, but want to control each seperately plz
if (opts->slot1_on == 1)
{
opts->slot1_on = 0; if (opts->slot_preference == 0) opts->slot_preference = 2;
// opts->slot_preference = 1; //slot 2
//clear any previously buffered audio
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
state->audio_out_buf_p = state->audio_out_buf + 100;
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
memset (state->audio_out_buf, 0, 100 * sizeof (short));
state->audio_out_idx2 = 0;
state->audio_out_idx = 0;
}
else if (opts->slot1_on == 0)
{
opts->slot1_on = 1;
if (opts->audio_out_type == 5) //OSS 48k/1
{
opts->slot_preference = 0; //slot 1
// opts->slot2_on = 0; //turn off slot 2
}
}
}
if (c == 50) // '2' key, toggle slot2 on
{
//switching, but want to control each seperately plz
if (opts->slot2_on == 1)
{
opts->slot2_on = 0;
opts->slot_preference = 0; //slot 1
//clear any previously buffered audio
state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100;
state->audio_out_buf_pR = state->audio_out_bufR + 100;
memset (state->audio_out_float_bufR, 0, 100 * sizeof (float));
memset (state->audio_out_bufR, 0, 100 * sizeof (short));
state->audio_out_idx2R = 0;
state->audio_out_idxR = 0;
}
else if (opts->slot2_on == 0)
{
opts->slot2_on = 1;
if (opts->audio_out_type == 5) //OSS 48k/1
{
opts->slot_preference = 1; //slot 2
// opts->slot1_on = 0; //turn off slot 1
}
}
}
// if (c == 51) //'3' key, toggle slot preference on 48k/1
// {
// if (opts->slot_preference == 1) opts->slot_preference = 0;
// else opts->slot_preference = 1;
// }
if (c == 51) //'3' key, cycle slot preference
{
if (opts->slot_preference == 0 || opts->slot_preference == 1) opts->slot_preference++;
else opts->slot_preference = 0;
}
if (c == 43) //+ key, increment audio_gain
{
if (opts->audio_gain < 50)
opts->audio_gain++;
state->aout_gain = opts->audio_gain;
state->aout_gainR = opts->audio_gain;
opts->audio_gainR = opts->audio_gain;
}
if (c == 45) //- key, decrement audio_gain
{
if (opts->audio_gain > 0)
opts->audio_gain--;
state->aout_gain = opts->audio_gain;
state->aout_gainR = opts->audio_gain;
//reset to default on 0 for auto
if (opts->audio_gain == 0)
{
state->aout_gain = 25;
state->aout_gainR = 25;
}
opts->audio_gainR = opts->audio_gain;
}
if (c == 42) // * key, increment audio_gainA
{
if (opts->audio_gainA < 100)
opts->audio_gainA++;
}
if (c == 47) // / key, decrement audio_gainA
{
if (opts->audio_gainA > 0)
opts->audio_gainA--;
}
if (c == 122) //'z' key, toggle payload to console
{
if (opts->payload == 1) opts->payload = 0;
else opts->payload = 1;
}
if (c == 99) //'c' key, toggle compact mode
{
if (opts->ncurses_compact == 1) opts->ncurses_compact = 0;
else opts->ncurses_compact = 1;
}
if (c == 116) //'t' key, toggle trunking
{
if (opts->p25_trunk == 1) opts->p25_trunk = 0;
else opts->p25_trunk = 1;
}
if (c == 121) //'y' key, toggle scanner mode
{
if (opts->scanner_mode == 1) opts->scanner_mode = 0;
else opts->scanner_mode = 1;
opts->p25_trunk = 0; //turn off trunking mode
}
if (c == 97) //'a' key, toggle call alert beep
{
if (opts->call_alert == 1) opts->call_alert = 0;
else opts->call_alert = 1;
}
if (c == 104) //'h' key, cycle history off, short, long
{
opts->ncurses_history++;
opts->ncurses_history %= 3;
}
if (c == 113) //'q' key, quit
{
exitflag = 1;
}
if (c == 52) // '4' key, toggle force privacy key over fid and svc (dmr)
{
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0;
else state->forced_alg_id = 1;
}
if (c == 54) // '6' key, toggle force rc4 key over missing pi header/late entry
{
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0;
else state->forced_alg_id = 0x21;
}
if (c == 105) //'i' key, toggle signal inversion on inverted types
{
//Set all signal for inversion or uninversion
if (opts->inverted_dmr == 0)
{
opts->inverted_dmr = 1;
opts->inverted_dpmr = 1;
opts->inverted_x2tdma = 1;
opts->inverted_ysf = 1;
}
else
{
opts->inverted_dmr = 0;
opts->inverted_dpmr = 0;
opts->inverted_x2tdma = 0;
opts->inverted_ysf = 0;
}
}
if (c == 109) //'m' key, toggle qpsk/c4fm - everything but phase 2
{
if (state->rf_mod == 0)
{
opts->mod_c4fm = 0;
opts->mod_qpsk = 1;
opts->mod_gfsk = 0;
state->rf_mod = 1;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
else
{
opts->mod_c4fm = 1;
opts->mod_qpsk = 0;
opts->mod_gfsk = 0;
state->rf_mod = 0;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
}
if (c == 77) //'M' key, toggle qpsk - phase 2 6000 sps
{
if (state->rf_mod == 0)
{
opts->mod_c4fm = 0;
opts->mod_qpsk = 1;
opts->mod_gfsk = 0;
state->rf_mod = 1;
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
else
{
opts->mod_c4fm = 1;
opts->mod_qpsk = 0;
opts->mod_gfsk = 0;
state->rf_mod = 0;
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
}
if (c == 82) //'R', save symbol capture bin with date/time string as name
{
//for filenames (no colons, etc)
char * timestr = getTime();
char * datestr = getDate();
sprintf (opts->symbol_out_file, "%s_%s_dibit_capture.bin", datestr, timestr);
openSymbolOutFile (opts, state);
//add a system event to echo in the event history
state->event_history_s[0].Event_History_Items[0].color_pair = 4;
char event_str[2000]; memset (event_str, 0, sizeof(event_str));
sprintf (event_str, "DSD-FME Dibit Capture File Started: %s;", opts->symbol_out_file);
watchdog_event_datacall (opts, state, 0xFFFFFF, 0xFFFFFF, event_str, 0);
state->lastsrc = 0; //this could wipe a call src if they hit 'R' while call in slot 1 in progress
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
//allocated memory pointer needs to be free'd
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
opts->symbol_out_file_creation_time = time(NULL);
opts->symbol_out_file_is_auto = 1;
}
if (c == 114) //'r' key, stop capturing symbol capture bin file
{
if (opts->symbol_out_f)
{
closeSymbolOutFile (opts, state);
sprintf (opts->audio_in_dev, "%s", opts->symbol_out_file);
//add a system event to echo in the event history
state->event_history_s[0].Event_History_Items[0].color_pair = 4;
char event_str[2000]; memset (event_str, 0, sizeof(event_str));
sprintf (event_str, "DSD-FME Dibit Capture File Closed: %s;", opts->symbol_out_file);
watchdog_event_datacall (opts, state, 0xFFFFFF, 0xFFFFFF, event_str, 0);
state->lastsrc = 0; //this could wipe a call src if they hit 'R' while call in slot 1 in progress
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
}
opts->symbol_out_file_is_auto = 0;
}
#ifdef __CYGWIN__
//do nothing
#else
if (c == 32) //'space bar' replay last bin file (rework to do wav files too?)
{
struct stat stat_buf;
if (stat(opts->audio_in_dev, &stat_buf) != 0)
{
fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev);
goto SKIPR;
}
if (S_ISREG(stat_buf.st_mode))
{
opts->symbolfile = fopen(opts->audio_in_dev, "r");
opts->audio_in_type = 4; //symbol capture bin files
}
SKIPR: ; //do nothing
}
#endif
if (c == 80) //'P' key - start per call wav files //TODO: Fix
{
char wav_file_directory[1024];
sprintf (wav_file_directory, "%s", opts->wav_out_dir);
wav_file_directory[1023] = '\0';
if (stat(wav_file_directory, &st_wav) == -1)
{
fprintf (stderr, "%s wav file directory does not exist\n", wav_file_directory);
fprintf (stderr, "Creating directory %s to save decoded wav files\n", wav_file_directory);
mkdir(wav_file_directory, 0700);
}
fprintf (stderr,"\n Per Call Wav File Enabled to Directory: %s;.\n", opts->wav_out_dir);
srand(time(NULL)); //seed random for filenames (so two filenames aren't the exact same datetime string on initailization)
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0);
opts->dmr_stereo_wav = 1;
}
if (c == 112) //'p' key - stop all per call wav files //TODO: Fix
{
//TODO: Add Closing of RAW files as well?
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling
opts->wav_out_fileR[0] = 0;
opts->dmr_stereo_wav = 0;
}
//
#ifdef __CYGWIN__ //this might be okay on Aero as well, will need to look into and/or test
//
#else
if (c == 115) //'s' key, stop playing wav or symbol in files
{
if (opts->symbolfile != NULL)
{
if (opts->audio_in_type == 4)
{
fclose(opts->symbolfile);
}
}
if (opts->audio_in_type == 2) //wav input file
{
sf_close(opts->audio_in_file);
}
if (opts->audio_out_type == 0)
{
opts->audio_in_type = 0;
openPulseInput(opts);
}
else opts->audio_in_type = 5; //exitflag = 1;
}
#endif
//makes buzzing sound when locked out in new audio config and short, probably something to do with processaudio running or not running
if (state->lasttg != 0 && opts->frame_provoice != 1 && c == 33) //SHIFT+'1' key (exclamation point), lockout slot 1 or conventional tg from tuning/playback during session
{
state->group_array[state->group_tally].groupNumber = state->lasttg;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "B");
sprintf (state->group_array[state->group_tally].groupName, "%s", "LOCKOUT");
state->group_tally++;
sprintf (state->event_history_s[0].Event_History_Items[0].internal_str, "Target: %d; has been locked out; User Lock Out.", state->lasttg);
watchdog_event_current(opts, state, 0);
sprintf (state->call_string[0], "%s", " "); //21 spaces
//if we have an opened group file, let's write a group lock out into it to make it permanent
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,B,LOCKOUT,%02X\n", state->lasttg, state->payload_algid);
fclose (pFile);
}
//extra safeguards due to sync issues with NXDN
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1)
{
//drop all items (failsafe)
noCarrier(opts, state);
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
}
//rtl
#ifdef USE_RTLSDR
if (opts->p25_trunk == 1 && opts->audio_in_type == 3)
{
//drop all items (failsafe)
noCarrier(opts, state);
rtl_dev_tune (opts, state->p25_cc_freq);
}
#endif
state->last_cc_sync_time = time(NULL);
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
}
if (state->lasttgR != 0 && opts->frame_provoice != 1 && c == 64) //SHIFT+'2' key (@ at sign), lockout slot 2 tdma tgR from tuning/playback during session
{
state->group_array[state->group_tally].groupNumber = state->lasttgR;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "B");
sprintf (state->group_array[state->group_tally].groupName, "%s", "LOCKOUT");
state->group_tally++;
sprintf (state->event_history_s[1].Event_History_Items[0].internal_str, "Target: %d; has been locked out; User Lock Out.", state->lasttgR);
watchdog_event_current(opts, state, 1);
sprintf (state->call_string[1], "%s", " "); //21 spaces
//if we have an opened group file, let's write a group lock out into it to make it permanent
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,B,LOCKOUT,%02X\n", state->lasttgR, state->payload_algidR);
fclose (pFile);
}
//extra safeguards due to sync issues with NXDN
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1)
{
//drop all items (failsafe)
noCarrier(opts, state);
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
}
//rtl
#ifdef USE_RTLSDR
if (opts->p25_trunk == 1 && opts->audio_in_type == 3)
{
//drop all items (failsafe)
noCarrier(opts, state);
rtl_dev_tune (opts, state->p25_cc_freq);
}
#endif
state->last_cc_sync_time = time(NULL);
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
}
if (opts->p25_trunk == 1 && c == 119) //'w' key, toggle white list/black list mode
{
if (opts->trunk_use_allow_list == 1) opts->trunk_use_allow_list = 0;
else opts->trunk_use_allow_list = 1;
}
if (opts->p25_trunk == 1 && c == 117) //'u' key, toggle tune private calls
{
if (opts->trunk_tune_private_calls == 1) opts->trunk_tune_private_calls = 0;
else opts->trunk_tune_private_calls = 1;
}
if (opts->p25_trunk == 1 && c == 100) //'d' key, toggle tune data calls
{
if (opts->trunk_tune_data_calls == 1) opts->trunk_tune_data_calls = 0;
else opts->trunk_tune_data_calls = 1;
}
if (opts->p25_trunk == 1 && c == 101) //'e' key, toggle tune enc calls (P25 only on certain grants)
{
if (opts->trunk_tune_enc_calls == 1) opts->trunk_tune_enc_calls = 0;
else opts->trunk_tune_enc_calls = 1;
}
if (opts->p25_trunk == 1 && c == 103) //'g' key, toggle tune group calls
{
if (opts->trunk_tune_group_calls == 1) opts->trunk_tune_group_calls = 0;
else opts->trunk_tune_group_calls = 1;
}
if (c == 70) //'F' key - toggle agressive sync/crc failure/ras
{
if (opts->aggressive_framesync == 0) opts->aggressive_framesync = 1;
else opts->aggressive_framesync = 0;
}
if (c == 68) //'D' key - Reset DMR Site Parms/Call Strings, etc.
{
//dmr trunking/ncurses stuff
state->dmr_rest_channel = -1; //init on -1
state->dmr_mfid = -1; //
//dmr mfid branding and site parms
sprintf(state->dmr_branding_sub, "%s", "");
sprintf(state->dmr_branding, "%s", "");
sprintf (state->dmr_site_parms, "%s", "");
//DMR Location Area - DMRLA B***S***
opts->dmr_dmrla_is_set = 0;
opts->dmr_dmrla_n = 0;
//reset NXDN info
state->nxdn_location_site_code = 0;
state->nxdn_location_sys_code = 0;
sprintf (state->nxdn_location_category, "%s", " ");
state->nxdn_last_ran = -1; //0
state->nxdn_ran = 0; //0
state->nxdn_rcn = 0;
state->nxdn_base_freq = 0;
state->nxdn_step = 0;
state->nxdn_bw = 0;
}
//Debug/Troubleshooting Option
if (c == 90) //'Z' key - Simulate NoCarrier/No VC/CC sync to zero out more stuff (capital Z)
{
// opts->p25_is_tuned = 0;
state->last_cc_sync_time = 0;
state->last_vc_sync_time = 0;
noCarrier(opts, state);
}
if (c == 93) //']' key - increment event history indexer
{
state->eh_index++;
}
if (c == 91) //'[' key - decrement event history indexer
{
state->eh_index--;
}
if (c == 92) //'\' key - toggle events for slot displayed, and reset eh_index
{
state->eh_slot ^= 1;
state->eh_index = 0;
}
//attempt retry to TCP Audio server
if (c == 56) // '8' key, try audio in type 8 (TCP Audio Server connection) using defaults OR whatever the user last specified
{
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 Connect to TCP with libsndfile: %s\n", sf_strerror(NULL));
}
else
{
//close pulse input if it is currently open
if (opts->audio_in_type == 0) closePulseInput(opts);
fprintf (stderr, "TCP Socket Connected Successfully.\n");
opts->audio_in_type = 8;
}
}
else fprintf (stderr, "TCP Socket Connection Error.\n");
}
if (c == 57) //'9' key, try rigctl connection with default values
{
//use same or last specified host for TCP audio sink for connection
memcpy (opts->rigctlhostname, opts->tcp_hostname, sizeof (opts->rigctlhostname) );
opts->rigctl_sockfd = Connect(opts->rigctlhostname, opts->rigctlportno);
if (opts->rigctl_sockfd != 0) opts->use_rigctl = 1;
else opts->use_rigctl = 0;
}
//if trunking and user wants to just go back to the control channel and skip this call
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && c == 67) //Capital C key - Return to CC
{
//extra safeguards due to sync issues with NXDN
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1)
{
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
}
//rtl
#ifdef USE_RTLSDR
if (opts->p25_trunk == 1 && opts->audio_in_type == 3) rtl_dev_tune (opts, state->p25_cc_freq);
#endif
state->last_cc_sync_time = time(NULL);
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
//if P25p1 Data Revert on P25p2 TDMA CC, flip symbolrate
if (state->p25_cc_is_tdma == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
fprintf (stderr, "\n User Activated Return to CC; \n ");
}
//if trunking or scanning, manually cycle forward through channels loaded (can be run without trunking or scanning enabled)
if ( (opts->use_rigctl == 1 || opts->audio_in_type == 3) && c == 76) //Capital L key - Cycle Channels Forward
{
//extra safeguards due to sync issues with NXDN
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//just copy and pasted the cycle logic for CC/signal hunting on no sync
if (state->lcn_freq_roll >= state->lcn_freq_count) //fixed this to skip the extra wait out at the end of the list
{
state->lcn_freq_roll = 0; //reset to zero
}
//roll an extra value up if the current is the same as what's already loaded -- faster hunting on Cap+, etc
if (state->lcn_freq_roll != 0)
{
if (state->trunk_lcn_freq[state->lcn_freq_roll-1] == state->trunk_lcn_freq[state->lcn_freq_roll])
{
state->lcn_freq_roll++;
//check roll again if greater than expected, then go back to zero
if (state->lcn_freq_roll >= state->lcn_freq_count)
{
state->lcn_freq_roll = 0; //reset to zero
}
}
}
//check that we have a non zero value first, then tune next frequency
if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0)
{
//rigctl
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[state->lcn_freq_roll]);
}
//rtl
if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
rtl_dev_tune (opts, state->trunk_lcn_freq[state->lcn_freq_roll]);
#endif
}
fprintf (stderr, "\n User Activated Channel Cycle;");
fprintf (stderr, " Tuning to Frequency: %.06lf MHz\n",
(double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000);
}
state->lcn_freq_roll++;
state->last_cc_sync_time = time(NULL);
//may need to test to see if we want to do the conditional below or not for symbol rate flipping
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
//if P25p1 Data Revert on P25p2 TDMA CC, flip symbolrate
if (state->p25_cc_is_tdma == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
}
if (c == 118 && opts->audio_in_type == 3) //'v' key, cycle rtl volume multiplier, when active
{
if (opts->rtl_volume_multiplier == 1 || opts->rtl_volume_multiplier == 2) opts->rtl_volume_multiplier++;
else opts->rtl_volume_multiplier = 1;
}
if (c == 86) // 'V' Key, toggle LPF
{
if (opts->use_lpf == 0) opts->use_lpf = 1;
else opts->use_lpf = 0;
}
if (c == 66) // 'B' Key, toggle HPF
{
if (opts->use_hpf == 0) opts->use_hpf = 1;
else opts->use_hpf = 0;
}
if (c == 78) // 'N' Key, toggle PBF
{
if (opts->use_pbf == 0) opts->use_pbf = 1;
else opts->use_pbf = 0;
}
if (c == 72) // 'H' Key, toggle HPF on digital
{
if (opts->use_hpf_d == 0) opts->use_hpf_d = 1;
else opts->use_hpf_d = 0;
}
if(opts->frame_provoice == 1 && c == 65) //'A' Key, toggle ESK mask 0xA0
{
if (state->esk_mask == 0) state->esk_mask = 0xA0;
else state->esk_mask = 0;
}
if(opts->frame_provoice == 1 && c == 83) //'S' Key, toggle STD or EA mode and reset
{
if (state->ea_mode == -1) state->ea_mode = 0;
else if (state->ea_mode == 0) state->ea_mode = 1;
else state->ea_mode = 0;
//reset -- test to make sure these don't do weird things when reset
state->edacs_site_id = 0;
state->edacs_lcn_count = 0;
state->edacs_cc_lcn = 0;
state->edacs_vc_lcn = 0;
state->edacs_tuned_lcn = -1;
state->edacs_vc_call_type = 0;
state->p25_cc_freq = 0;
opts->p25_is_tuned = 0;
state->lasttg = 0;
state->lastsrc = 0;
}
//RTL PPM Manual Adjustment
if (c == 125)
opts->rtlsdr_ppm_error++;
if (c == 123)
opts->rtlsdr_ppm_error--;
//anything with an entry box will need the inputs and outputs stopped first
//so probably just write a function to handle c input, and when c = certain values
//needing an entry box, then stop all of those
return 0;
} //end ncursesPrinter

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,59 +2,26 @@
#include "dsd.h" #include "dsd.h"
#include "p25p1_heuristics.h" #include "p25p1_heuristics.h"
//fixed the memory leak, but now random segfaults occur -- double free or corruption (out) or (!prev) void
void resetState (dsd_state * state) resetState (dsd_state * state)
{ {
int i; int i, j;
//Dibit Buffer -- Free Allocated Memory
// free (state->dibit_buf);
//Dibit Buffer -- Memset/Init/Allocate Memory
// state->dibit_buf = malloc (sizeof (int) * 1000000); // state->dibit_buf = malloc (sizeof (int) * 1000000);
// state->dibit_buf_p = state->dibit_buf + 200;
state->dibit_buf_p = state->dibit_buf + 200; // memset (state->dibit_buf, 0, sizeof (int) * 200);
memset (state->dibit_buf, 0, sizeof (int) * 200); state->repeat = 0; //not sure yet
state->repeat = 0; //repeat frame? //state->audio_out_buf = malloc (sizeof (short) * 1000000);
//memset (state->audio_out_buf, 0, 100 * sizeof (short));
//Audio Buffer -- Free Allocated Memory //state->audio_out_buf_p = state->audio_out_buf + 100;
free (state->audio_out_buf); //state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
free (state->audio_out_float_buf); //memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
free (state->audio_out_bufR); //state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
free (state->audio_out_float_bufR); //state->audio_out_idx = 0;
//state->audio_out_idx2 = 0;
//Audio Buffer -- Memset/Init/Allocate Memory per slot //state->audio_out_temp_buf_p = state->audio_out_temp_buf;
//state->wav_out_bytes = 0;
//slot 1
state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
state->audio_out_buf = malloc (sizeof (short) * 1000000);
memset (state->audio_out_buf, 0, 100 * sizeof (short));
state->audio_out_buf_p = state->audio_out_buf + 100;
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
state->audio_out_idx = 0;
state->audio_out_idx2 = 0;
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
//slot 2
state->audio_out_bufR = malloc (sizeof (short) * 1000000);
state->audio_out_float_bufR = malloc (sizeof (float) * 1000000);
memset (state->audio_out_bufR, 0, 100 * sizeof (short));
state->audio_out_buf_pR = state->audio_out_bufR + 100;
memset (state->audio_out_float_bufR, 0, 100 * sizeof (float));
state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100;
state->audio_out_idxR = 0;
state->audio_out_idx2R = 0;
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
//Sync
state->center = 0; state->center = 0;
state->jitter = -1; state->jitter = -1;
state->synctype = -1; state->synctype = -1;
@@ -64,73 +31,76 @@ void resetState (dsd_state * state)
state->umid = 0; state->umid = 0;
state->minref = -12000; state->minref = -12000;
state->maxref = 12000; state->maxref = 12000;
state->lastsample = 0; state->lastsample = 0;
for (i = 0; i < 128; i++) for (i = 0; i < 128; i++)
{ {
state->sbuf[i] = 0; state->sbuf[i] = 0;
} }
state->sidx = 0; state->sidx = 0;
for (i = 0; i < 1024; i++) for (i = 0; i < 1024; i++)
{ {
state->maxbuf[i] = 15000; state->maxbuf[i] = 15000;
} }
for (i = 0; i < 1024; i++) for (i = 0; i < 1024; i++)
{ {
state->minbuf[i] = -15000; state->minbuf[i] = -15000;
} }
state->midx = 0; state->midx = 0;
state->err_str[0] = 0;
sprintf (state->fsubtype, " ");
sprintf (state->ftype, " ");
state->symbolcnt = 0; state->symbolcnt = 0;
state->rf_mod = 0;
state->numflips = 0; state->numflips = 0;
state->lastsynctype = -1; state->lastsynctype = -1;
state->lastp25type = 0; state->lastp25type = 0;
state->offset = 0; state->offset = 0;
state->carrier = 0; state->carrier = 0;
for (i = 0; i < 25; i++)
//Reset Voice Errors in C0 and C1 (or remaining Codewords in IMBE) {
for (j = 0; j < 16; j++)
{
state->tg[i][j] = 48;
}
}
state->tgcount = 0;
state->lasttg = 0;
state->lastsrc = 0;
state->nac = 0;
state->errs = 0; state->errs = 0;
state->errs2 = 0; state->errs2 = 0;
state->errsR = 0; state->mbe_file_type = -1;
state->errs2R = 0;
//Misc -- may not be needed
state->optind = 0; state->optind = 0;
state->numtdulc = 0; state->numtdulc = 0;
state->firstframe = 0; state->firstframe = 0;
sprintf (state->slot0light, " slot0 ");
sprintf (state->slot1light, " slot1 ");
state->aout_gain = 25;
memset (state->aout_max_buf, 0, sizeof (float) * 200);
state->aout_max_buf_p = state->aout_max_buf;
state->aout_max_buf_idx = 0;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
sprintf (state->algid, "________");
sprintf (state->keyid, "________________");
state->currentslot = 0;
state->cur_mp = malloc (sizeof (mbe_parms));
state->prev_mp = malloc (sizeof (mbe_parms));
state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
state->p25kid = 0;
//unsure if these are still used or ever were used, state->debug_audio_errors = 0;
// memset (state->aout_max_buf, 0, sizeof (float) * 200); state->debug_header_errors = 0;
// state->aout_max_buf_p = state->aout_max_buf; state->debug_header_critical_errors = 0;
// state->aout_max_buf_idx = 0;
// //MBE Specific state->nxdn_last_ran = 0;
// //free the memory before allocating it again -- may not use this // state->payload_algid = 0;
// free (state->cur_mp); // state->payload_algidR = 0;
// free (state->prev_mp); state->dmr_encL = 0;
// free (state->prev_mp_enhanced); state->dmr_encR = 0;
// //memory allocation and init on mbe -- may not use this //each time you run this, it increses memory use by 4MB, massive memory leak
// state->cur_mp = malloc (sizeof (mbe_parms)); //need to revisit this sometime and look into only resetting only the necesary items to let P25 switch between signals (C4FM or Wide) without needing a restart
// state->prev_mp = malloc (sizeof (mbe_parms)); initialize_p25_heuristics(&state->p25_heuristics); //see if we want to re-init this or not, currently seems to cause memory leak when running over and over, mitigated with a reset flag
// state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
// mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
//rest the heurestics, we want to do this on each tune, each RF frequency can deviate quite a bit in strength
initialize_p25_heuristics(&state->p25_heuristics);
initialize_p25_heuristics(&state->inv_p25_heuristics);
}
//simple function to reset the dibit buffer
void reset_dibit_buffer(dsd_state * state)
{
//Dibit Buffer -- Free Allocated Memory
// free (state->dibit_buf);
//Dibit Buffer -- Memset/Init/Allocate Memory
// state->dibit_buf = malloc (sizeof (int) * 1000000);
state->dibit_buf_p = state->dibit_buf + 200;
memset (state->dibit_buf, 0, sizeof (int) * 200);
} }

View File

@@ -30,15 +30,12 @@ void error(char *msg) {
exit(0); exit(0);
} }
struct sockaddr_in address;
struct sockaddr_in addressA;
// //
// Connect // Connect
// //
int Connect (char *hostname, int portno) int Connect (char *hostname, int portno)
{ {
int sockfd; int sockfd, n;
struct sockaddr_in serveraddr; struct sockaddr_in serveraddr;
struct hostent *server; struct hostent *server;
@@ -50,14 +47,14 @@ int Connect (char *hostname, int portno)
fprintf(stderr,"ERROR opening socket\n"); fprintf(stderr,"ERROR opening socket\n");
error("ERROR opening socket"); error("ERROR opening socket");
} }
/* gethostbyname: get the server's DNS entry */ /* gethostbyname: get the server's DNS entry */
server = gethostbyname(hostname); server = gethostbyname(hostname);
if (server == NULL) { if (server == NULL) {
fprintf(stderr,"ERROR, no such host as %s\n", hostname); fprintf(stderr,"ERROR, no such host as %s\n", hostname);
//exit(0); //exit(0);
return (0); //return 0, check on other end and configure pulse input return (0); //return 0, check on other end and configure pulse input
} }
/* build the server's Internet address */ /* build the server's Internet address */
@@ -72,7 +69,7 @@ int Connect (char *hostname, int portno)
{ {
fprintf(stderr,"ERROR opening socket\n"); fprintf(stderr,"ERROR opening socket\n");
return (0); return (0);
} }
return sockfd; return sockfd;
} }
@@ -107,7 +104,7 @@ bool Recv(int sockfd, char *buf)
// //
// GQRX Protocol // GQRX Protocol
// //
long int GetCurrentFreq(int sockfd) long int GetCurrentFreq(int sockfd)
{ {
long int freq = 0; long int freq = 0;
char buf[BUFSIZE]; char buf[BUFSIZE];
@@ -115,13 +112,13 @@ long int GetCurrentFreq(int sockfd)
char * token; char * token;
Send(sockfd, "f\n"); Send(sockfd, "f\n");
Recv(sockfd, buf); Recv(sockfd, buf);
if (strcmp(buf, "RPRT 1") == 0 ) if (strcmp(buf, "RPRT 1") == 0 )
return freq; return freq;
token = strtok (buf, "\n"); token = strtok (buf, "\n");
freq = strtol (token, &ptr, 10); freq = strtol (token, &ptr, 10);
// fprintf (stderr, "\nRIGCTL VFO Freq: [%ld]\n", freq); // fprintf (stderr, "\nRIGCTL VFO Freq: [%ld]\n", freq);
return freq; return freq;
} }
@@ -130,33 +127,25 @@ bool SetFreq(int sockfd, long int freq)
{ {
char buf[BUFSIZE]; char buf[BUFSIZE];
sprintf (buf, "F %ld\n", freq); sprintf (buf, "F %ld\n", freq);
Send(sockfd, buf); Send(sockfd, buf);
Recv(sockfd, buf); Recv(sockfd, buf);
if (strcmp(buf, "RPRT 1\n") == 0 ) //sdr++ has a linebreak here, is that in all versions of the protocol? if (strcmp(buf, "RPRT 1") == 0 )
return false; return false;
return true; return true;
} }
bool SetModulation(int sockfd, int bandwidth) bool SetModulation(int sockfd, int bandwidth)
{ {
char buf[BUFSIZE]; char buf[BUFSIZE];
//the bandwidth is now a user/system based configurable variable //the bandwidth is now a user/system based configurable variable
sprintf (buf, "M NFM %d\n", bandwidth); //SDR++ has changed the token from FM to NFM, even if Ryzerth fixes it later, users may still have an older version sprintf (buf, "M FM %d\n", bandwidth);
Send(sockfd, buf); Send(sockfd, buf);
Recv(sockfd, buf); Recv(sockfd, buf);
//if it fails the first time, send the other token instead if (strcmp(buf, "RPRT 1") == 0 )
if (strcmp(buf, "RPRT 1\n") == 0 ) //sdr++ has a linebreak here, is that in all versions of the protocol?
{
sprintf (buf, "M FM %d\n", bandwidth); //anything not SDR++
Send(sockfd, buf);
Recv(sockfd, buf);
}
if (strcmp(buf, "RPRT 1\n") == 0 )
return false; return false;
return true; return true;
@@ -233,10 +222,9 @@ bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp)
//shoe in UDP input connection here...still having issues that I don't know how to resolve //shoe in UDP input connection here...still having issues that I don't know how to resolve
int UDPBind (char *hostname, int portno) int UDPBind (char *hostname, int portno)
{ {
UNUSED(hostname); int sockfd, n;
struct sockaddr_in serveraddr, client_addr;
int sockfd; struct hostent *server;
struct sockaddr_in serveraddr;
/* socket: create the socket */ /* socket: create the socket */
//UDP socket //UDP socket
@@ -255,26 +243,17 @@ int UDPBind (char *hostname, int portno)
serveraddr.sin_port = htons(portno); serveraddr.sin_port = htons(portno);
//Bind socket to listening //Bind socket to listening
if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) { if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
perror("ERROR on binding UDP Port"); perror("ERROR on binding UDP Port");
} }
//set these for non blocking when no samples to read
struct timeval read_timeout;
read_timeout.tv_sec = 0;
read_timeout.tv_usec = 10;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &read_timeout, sizeof read_timeout);
return sockfd; return sockfd;
} }
//going to leave this function available, even if completely switched over to rtl_dev_tune now, may be useful in the future void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
{ {
UNUSED(state); int handle;
unsigned short udp_port = opts->rtl_udp_port;
int handle;
unsigned short udp_port = opts->rtl_udp_port;
char data[5] = {0}; //data buffer size is 5 for UDP frequency tuning char data[5] = {0}; //data buffer size is 5 for UDP frequency tuning
struct sockaddr_in address; struct sockaddr_in address;
@@ -295,188 +274,8 @@ void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
address.sin_port = htons(udp_port); address.sin_port = htons(udp_port);
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in)); sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
//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. close (handle); //close socket after sending.
} }
void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
{
UNUSED(state);
size_t err = 0;
//listen with:
//short 8k/2
//socat stdio udp-listen:23456 | play --buffer 640 -q -b 16 -r 8000 -c2 -t s16 -
//short 8k/1
//socat stdio udp-listen:23456 | play --buffer 320 -q -b 16 -r 8000 -c1 -t s16 -
//float 8k/2
//socat stdio udp-listen:23456 | play --buffer 1280 -q -e float -b 32 -r 8000 -c2 -t f32 -
//float 8k/1
//socat stdio udp-listen:23456 | play --buffer 640 -q -e float -b 32 -r 8000 -c1 -t f32 -
//send audio or data to socket
err = sendto(opts->udp_sockfd, data, nsam, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
if (err < 0) fprintf (stderr, "\n UDP SENDTO ERR %ld", err); //return value here is size_t number of characters sent, or -1 for failure
if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible
}
//Analog UDP port on +2 of normal open socket
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
{
UNUSED(state);
size_t err = 0;
//listen with:
//short 48k/1
//socat stdio udp-listen:23456 | play --buffer 1920 -q -b 16 -r 48000 -c1 -t s16 -
//send audio or data to socket
err = sendto(opts->udp_sockfdA, data, nsam, 0, (const struct sockaddr * ) & addressA, sizeof(struct sockaddr_in));
if (err < 0) fprintf (stderr, "\n UDP SENDTO ERR %ld", err); //return value here is size_t number of characters sent, or -1 for failure
if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible
}
int udp_socket_connect(dsd_opts * opts, dsd_state * state)
{
UNUSED(state);
long int err = 0;
err = opts->udp_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (err < 0)
{
fprintf (stderr, " UDP Socket Error %ld\n", err);
return (err);
}
// Don't think this is needed, but doesn't seem to hurt to keep it here either
int broadcastEnable = 1;
err = setsockopt(opts->udp_sockfd, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
if (err < 0)
{
fprintf (stderr, " UDP Broadcast Set Error %ld\n", err);
return (err);
}
memset((char * ) & address, 0, sizeof(address));
address.sin_family = AF_INET;
err = address.sin_addr.s_addr = inet_addr(opts->udp_hostname);
if (err < 0) //error in this context reports back 32-bit inet_addr reversed order byte pairs
{
fprintf (stderr, " UDP inet_addr Error %ld\n", err);
return (err);
}
address.sin_port = htons(opts->udp_portno);
if (err < 0)
{
fprintf (stderr, " UDP htons Error %ld\n", err);
return (err);
}
return 0;
}
int udp_socket_connectA(dsd_opts * opts, dsd_state * state)
{
UNUSED(state);
long int err = 0;
err = opts->udp_sockfdA = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (err < 0)
{
fprintf (stderr, " UDP Socket Error %ld\n", err);
return (err);
}
// Don't think this is needed, but doesn't seem to hurt to keep it here either
int broadcastEnable = 1;
err = setsockopt(opts->udp_sockfdA, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
if (err < 0)
{
fprintf (stderr, " UDP Broadcast Set Error %ld\n", err);
return (err);
}
memset((char * ) & addressA, 0, sizeof(addressA));
addressA.sin_family = AF_INET;
err = addressA.sin_addr.s_addr = inet_addr(opts->udp_hostname);
if (err < 0) //error in this context reports back 32-bit inet_addr reversed order byte pairs
{
fprintf (stderr, " UDP inet_addr Error %ld\n", err);
return (err);
}
addressA.sin_port = htons(opts->udp_portno+2); //plus 2 to current port assignment for the analog port value
if (err < 0)
{
fprintf (stderr, " UDP htons Error %ld\n", err);
return (err);
}
return 0;
}
void return_to_cc (dsd_opts * opts, dsd_state * state)
{
//extra safeguards due to sync issues with NXDN
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->gi[0] = -1;
state->gi[1] = -1;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1)
{
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
}
//rtl
#ifdef USE_RTLSDR
if (opts->p25_trunk == 1 && opts->audio_in_type == 3) rtl_dev_tune (opts, state->p25_cc_freq);
#endif
state->last_cc_sync_time = time(NULL);
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
//if P25p1 Data Revert on P25p2 TDMA CC, flip symbolrate
if (state->p25_cc_is_tdma == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
// fprintf (stderr, "\n User Activated Return to CC; \n ");
}

Some files were not shown because too many files have changed in this diff Show More