mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 01:07:24 -05:00
Compare commits
55 Commits
win32-rc3
...
win32-aero
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c42c50a40 | ||
|
|
36399386c5 | ||
|
|
8eee4cd699 | ||
|
|
1410e8bd81 | ||
|
|
915375fb1c | ||
|
|
52fff28dc4 | ||
|
|
bd035622b8 | ||
|
|
44163710f9 | ||
|
|
44b1ccdeae | ||
|
|
34113cb782 | ||
|
|
2a336a9275 | ||
|
|
5c08a9193c | ||
|
|
2cae7f9c18 | ||
|
|
e056c97d9d | ||
|
|
dc49e0d08b | ||
|
|
b639f6af28 | ||
|
|
6b45c4ba9b | ||
|
|
2062502a59 | ||
|
|
a490d9ead2 | ||
|
|
c73832ac2c | ||
|
|
fe1fa79f70 | ||
|
|
f2f29f2287 | ||
|
|
3181e58b19 | ||
|
|
6204aec2a5 | ||
|
|
267180ed0e | ||
|
|
8d9cbb3720 | ||
|
|
8af6d2e185 | ||
|
|
abeb92ff8a | ||
|
|
be47075611 | ||
|
|
1640b58ae4 | ||
|
|
7a34d531b1 | ||
|
|
0af814c72f | ||
|
|
1477190dea | ||
|
|
b0cd1881bb | ||
|
|
44ad75ec17 | ||
|
|
1ce487dc13 | ||
|
|
ed9a96fdc8 | ||
|
|
c677efda49 | ||
|
|
fea4f97b89 | ||
|
|
f2e3834e0a | ||
|
|
b8b62ef69a | ||
|
|
4a8a4a088a | ||
|
|
9308c5b251 | ||
|
|
cdfe9435d3 | ||
|
|
f6cd915f4d | ||
|
|
1dfd59801e | ||
|
|
c4513fab7a | ||
|
|
a3b8839897 | ||
|
|
9e009867ff | ||
|
|
e95e2218d9 | ||
|
|
a9742043b8 | ||
|
|
05eef039c9 | ||
|
|
818e952b7b | ||
|
|
38b962aacf | ||
|
|
0217202801 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ build
|
||||
*.sh
|
||||
*.csv
|
||||
examples
|
||||
*.bin
|
||||
*.ans
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
project(dsd-fme-lite)
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
#Ubuntu 18.04 uses 3.10.2 currently -- almost EOL
|
||||
#Ubuntu 20.04 uses 3.16.3
|
||||
#Ubuntu 22.04 uses 3.22.1
|
||||
#Arch currently on 3.26.1 -- new nag warning starts on this one, see below
|
||||
#cmake_minimum_required() should be called prior to this top-level project()
|
||||
#Cygwin currently using 3.23.2 (or newer)
|
||||
cmake_minimum_required(VERSION 3.10.2)
|
||||
project(dsd-fme-aero)
|
||||
|
||||
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
#Set curses to ncurses, and wide true for ascii
|
||||
set(CURSES_NEED_NCURSES TRUE)
|
||||
set(CURSES_NEED_WIDE TRUE)
|
||||
|
||||
#use cmake option -DCOLORS=OFF to disable color output
|
||||
option(COLORS
|
||||
"Build with Colors Enabled" ON)
|
||||
if (COLORS)
|
||||
add_definitions(-DPRETTY_COLORS)
|
||||
endif ()
|
||||
|
||||
include(git_revision)
|
||||
git_describe(GIT_TAG)
|
||||
|
||||
@@ -14,7 +27,7 @@ find_package(MBE REQUIRED)
|
||||
find_package(ITPP REQUIRED)
|
||||
find_package(RTLSDR)
|
||||
find_package(Curses REQUIRED)
|
||||
find_package(PULSEAUDIO REQUIRED)
|
||||
find_package(PulseAudio REQUIRED)
|
||||
|
||||
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIR})
|
||||
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${CURSES_LIBRARY})
|
||||
@@ -38,24 +51,24 @@ list(APPEND SRCS "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c")
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/include")
|
||||
|
||||
ADD_EXECUTABLE(dsd-fme-lite ${SRCS} ${HEADERS})
|
||||
TARGET_LINK_LIBRARIES(dsd-fme-lite ${LIBS})
|
||||
ADD_EXECUTABLE(dsd-fme-aero ${SRCS} ${HEADERS})
|
||||
TARGET_LINK_LIBRARIES(dsd-fme-aero ${LIBS})
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS dsd-fme-lite DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(TARGETS dsd-fme-aero DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
# man page
|
||||
find_program(HELP2MAN_FOUND help2man)
|
||||
if (HELP2MAN_FOUND)
|
||||
add_custom_command(TARGET dsd-fme-lite POST_BUILD
|
||||
add_custom_command(TARGET dsd-fme-aero POST_BUILD
|
||||
COMMAND help2man
|
||||
ARGS -n "Digital Speech Decoder"
|
||||
--version-string=${GIT_TAG}
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme-lite.1
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme-aero.1
|
||||
--no-info
|
||||
$<TARGET_FILE:dsd-fme-lite>
|
||||
$<TARGET_FILE:dsd-fme-aero>
|
||||
)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme-lite.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme-aero.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
endif()
|
||||
|
||||
# uninstall target
|
||||
|
||||
23
README.md
23
README.md
@@ -2,29 +2,6 @@
|
||||
|
||||
This branch is for modifications to make a Windows 32-bit binary to be precompiled, please DO NOT use this branch if you are a Linux User, or copy any contents out of here to modify your existing setup!
|
||||
|
||||
# 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"), [SDTRunk](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, so please understand that this version may not compile, compile easily, or run correctly in other environments.
|
||||
|
||||
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu and others for the many hours of wav samples submitted by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. Thank you everybody.
|
||||
|
||||
Click image below for video of current DMR/P25 trunking capabilities.
|
||||
|
||||
[](https://www.youtube.com/watch?v=hGIijBbbUxg "DSD-FME Trunking Demo")
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Information
|
||||
|
||||
See the [examples](https://github.com/lwvmobile/dsd-fme/tree/main/examples "examples") folder for information on [cloning and installing](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Install_Notes.md "cloning and installing"), [example usage](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Example_Usage.md "example usage"), and [trunking examples](https://github.com/lwvmobile/dsd-fme/blob/main/examples/trunking.sh "trunking examples").
|
||||
|
||||
## Notice
|
||||
|
||||
The pulseaudio branch has been deleted in favor of the new main branch which also uses Pulse Audio. Users moving to the main branch should review the help file and example usage files for changes in syntax. Users can download the last pulse audio branch version from the release link if they prefer the older version. Any pre-compiled windows versions found in the release link will be considered legacy as well, and do not feature trunking support and have older handling for some other modes. Users are free to use those, but keep in mind the use case syntax may and will be different between versions. Furthermore, any wiki articles with 'auto intstall scripts' made for windows users may contain outdated information for building the deleted pulseaudio branch, please adjust any information found in those accordingly.
|
||||
|
||||
## License
|
||||
Copyright (C) 2010 DSD Author
|
||||
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
|
||||
Binary file not shown.
@@ -1,158 +0,0 @@
|
||||
### Example Usage and Notes!
|
||||
|
||||
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, and P25 (1 and 2) . To use other decoding methods which cannot be auto detected, please use the following command line switches. Make sure to route audio into and out of DSD-FME using pavucontrol and virtual sinks as needed.
|
||||
|
||||
```
|
||||
-ft XDMA decoder class (P25 1 and 2, and DMR Stereo)
|
||||
-fs DMR Stereo, also may need to use -xr if inverted DMR
|
||||
-fa Legacy Auto (not recommended)
|
||||
-fi NXDN48
|
||||
-fn NXDN96
|
||||
-fp ProVoice
|
||||
-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
|
||||
-f2 P25P2 (will need to specify wacn/sys/nac manually)
|
||||
-fx X2-TDMA
|
||||
```
|
||||
|
||||
## Trunking Examples
|
||||
Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation.
|
||||
|
||||
## Example Usage - Default Decoder, Ncurses Terminal, Pulse Input/Output, and Log Console to file
|
||||
`dsd-fme -N 2> voice.log`
|
||||
|
||||
and in a second terminal tab, same folder, run
|
||||
|
||||
`tail -n 40 -f voice.log`
|
||||
|
||||
Then you can open up your pavucontrol "Pulse Audio Volume Control" or "Volume Control" application and route input into DSD-FME from any application and DSD-FME output to the left and right speakers respectively. (unlock the channel in the application stream and adjust left and right independently)
|
||||
|
||||
### Input ###
|
||||
|
||||
--TCP Direct Audio Link with SDR++
|
||||
|
||||
`dsd-fme -i tcp` Currently defaults to localhost:7355 (SDR++ defaults)
|
||||
|
||||
`dsd-fme -i tcp:192.168.7.5:7356` (remote host and custom port)
|
||||
|
||||
--48000/9600 Mono Wav File Input
|
||||
|
||||
`dsd-fme -i filename.wav` 48k/1 Audio (SDR++, GQRX wav recordings)
|
||||
|
||||
`dsd-fme -i filename.wav -s 96000` 96k/1 (DSDPlus Raw Signal Wav Files)
|
||||
|
||||
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
|
||||
`dsd-fme -ft -i rtl:0:154.9875M:26:-2:8:0:6020 `
|
||||
|
||||
```
|
||||
RTL-SDR options:
|
||||
WARNING! Old CLI Switch Handling has been depreciated in favor of rtl:<parms>
|
||||
Usage: rtl:dev:freq:gain:ppm:bw:sq:udp
|
||||
dev <num> RTL-SDR Device Index Number
|
||||
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
|
||||
gain <num> RTL-SDR Device Gain (0-49) (default = 26)(0 = Hardware AGC, not recommended)
|
||||
ppm <num> RTL-SDR PPM Error (default = 0)
|
||||
bw <num> RTL-SDR VFO Bandwidth kHz (default = 12)(6, 8, 12, 24)
|
||||
sq <num> RTL-SDR Squelch Level (0 - Open, 25 - Little, 50 - Higher)
|
||||
udp <num> RTL-SDR UDP Remote Port (default = 6020)
|
||||
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:12:0:6021
|
||||
|
||||
```
|
||||
|
||||
## Example Usage - Extra Payload/PDU Logging
|
||||
|
||||
`dsd-fme -Z -N 2>> log.ans`
|
||||
|
||||
and in a second terminal tab, same folder, run
|
||||
|
||||
`tail -n 40 -f log.ans`
|
||||
|
||||
### EDACS/P25/NXDN/DMR Simple/Single VFO Trunking ###
|
||||
|
||||
EDACS Trunking (w/ channel map import)
|
||||
|
||||
--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (Tested and Working on EDACS/EDACS-EA with Provoice only, no analog voice monitoring)
|
||||
|
||||
`dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans`
|
||||
|
||||
--NXDN48 Trunking (standard band plan) with SDR++ (untested for frequency accuracy)
|
||||
|
||||
`dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans`
|
||||
|
||||
--NXDN48 Trunking (w/ channel map import) with SDR++ (put control channel frequency at channel 0 in channel_map.csv)
|
||||
|
||||
`dsd-fme -fi -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans`
|
||||
|
||||
--DMR Trunking (w/ channel map import) with SDR++ (put control channel frequency at channel 0 (TIII and Con+) in channel_map.csv)
|
||||
|
||||
`dsd-fme -fs -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans`
|
||||
|
||||
--P25 Trunking P1 and P2 (C4FM) with SDR++
|
||||
|
||||
`dsd-fme -i tcp -T -U 4532 -N 2> log.ans`
|
||||
|
||||
--P25 Trunking (CQPSK) with P1 Control Channel (Should switch symbol rate and center on Phase 2 audio channels)
|
||||
|
||||
`dsd-fme -i tcp -T -U 4532 -N -mq 2> log.ans`
|
||||
|
||||
--P25 Trunking Phase 2 TDMA Control Channel systems with CQPSK (non Phase 1 systems)
|
||||
|
||||
`dsd-fme -i tcp -T -U 4532 -N -f2 -m2 2> log.ans`
|
||||
|
||||
Trunking Note1: All samples above can also be run with the RTL input method and setting of RTL UDP remote port. In terms of performance, however, SDR++ will do a much better job on weaker/marginal signal compared to the RTL input. RTL input should only be used on strong signal.
|
||||
|
||||
`dsd-fme -fp -i rtl:0:851.8M:22:-2:24:0:6020 -T -C channel_map.csv -G group.csv -N 2> log.ans`
|
||||
|
||||
Trunking Note2: CQPSK Phase 1 and Phase 2 Systems are subceptible to LSM distortion issues, but seem to do okay, but require really good signal. Some CRC issues still occur with Phase 2 TDMA LCCH Mac Signal that can affect reliability, I believe this issue is ultimately caused by the PSK demodulation inside of FME. I also don't believe this will work on 8-level PSK, but I cannot determine that at the moment. Update: I have improved the LCCH Mac Signal decoding my increasing the QPSK decision point buffers to their maximum values.
|
||||
|
||||
Trunking Note3: DMR Trunking has been coded, and some testing and tweaks have been carried out. Cap+, Con+, and TIII systems seem to do well with trunking now. Placing the frequency for the control channel at channel map 0 in your channel_map.csv file is not required now if using RIGCTL or the RTL Input, both can poll the VFO for the current frequency if it believes its on a control channel, but setting a fake channel number (i.e. 999) first with the CC frequency will result in finding the CC faster on startup if desired. If you need to map out your channels for TIII, you can observe the console output and look for channel numbers. For conveniece I have included the DSDPlus channel numbering (as best as I can figure it, but they seem consistent) into the console print so it will make it easier for users from DSDPlus to map frequencies into the channel_map.csv file. Make sure your channel numbers are the Cd (channel decimal) values from the log, and not the C+ (dsdplus) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area.
|
||||
|
||||
```
|
||||
Talkgroup Voice Channel Grant (TV_GRANT) - Logical
|
||||
Ch [036] Cd [0054] C+ [0110] - TS [1] - Target [01900500] - Source [01900505]
|
||||
```
|
||||
|
||||
Use channel 54 in your import file, which would correspond to dsdplus channels 109 and 110 (109 = TS0 / 110 = TS1).
|
||||
|
||||
For Connect Plus, enumerate your list from 1 to the last channel and add the frequency. For Capacity Plus, Rest Channels 1 and 2 will share the same frequency, 3 and 4 will share, 5 and 6 will share, and 7-8 will share, as Capacity Plus counts each RF frequency as two seperate channels, one for each time slot. Capacity Plus Quirk: DSD-FME makes its best effort to follow the rest channel in the event that the sync is lost for longer than the hangtime, but occassionally, DSD-FME will lose the rest channel and will have to hunt through all frequencies to find it again.
|
||||
|
||||
Currently uncoded/unknown DMR trunking systems include Hytera XPT.
|
||||
|
||||
Trunking Note4: NXDN TRunking will require a channel map. Please see the example folder for an appropriate channel map. ~NXDN Trunking may also require a channel map file, depending on the system. If it uses a custom range (above 800), then channels will need to be mapped. If channels do not require mapping (have channels below 800) but you are not tuning properly, then please use a channel_map and please report the issue in the issues along with the channels you are tuning and the actual rf frequency so corrections can be made.~
|
||||
|
||||
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.
|
||||
|
||||
## NCurses Keyboard Shortcuts ##
|
||||
|
||||
Some Keyboard Shortcuts have been implemented for testing to see how users like them. Just hope nobody pushes a key on accident and doesn't know which one it was or what it does. The current list of keyboard shortcuts include:
|
||||
|
||||
```
|
||||
esc or arrow keys - ncurses menu
|
||||
q - quit
|
||||
c - toggle compact mode
|
||||
h - toggle call history
|
||||
z - toggle console payloads
|
||||
a - toggle call alert beep
|
||||
4 - force dmr privacy key assertion over fid and svc bits
|
||||
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 8/3 (phase 2 tdma control channel)
|
||||
R - start capturing symbol capture bin (date/time name file)
|
||||
r - stop capturing symbol capture bin
|
||||
spacebar - replay last symbol capture bin (captures must be stopped first)
|
||||
s - stop playing symbol capture bin or wav input file
|
||||
P - start per call decoded wav files
|
||||
p - stop per call decoded wav files
|
||||
t - toggle trunking (needs either rtl input, or rigctl connection)
|
||||
1 - Lockout Tuning/Playback of TG in Slot 1 or Conventional (Current Session Only)
|
||||
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
|
||||
g - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25)
|
||||
u - Toggle Trunking Tuning to Private Calls (DMR T3, P25)
|
||||
d - Toggle Trunking Tuning to Data Calls (DMR T3)
|
||||
|
||||
```
|
||||
@@ -1,131 +0,0 @@
|
||||
## Notice
|
||||
|
||||
These should not be followed for the 'lite' branch
|
||||
|
||||
## How to clone and build this branch
|
||||
|
||||
### Ubuntu 22.04/20.04/LM20/Debian Bullseye or Newer:
|
||||
|
||||
Using the included download-and-install.sh should make for a simple and painless clone, build, and install on newer Debian/Ubuntu/Mint/Pi systems. Simply acquire or copy the script, and run it. Update: Ubuntu 22.04 and RPi Bullseye 64-bit has been tested working with the installer script and functions appropriately.
|
||||
|
||||
If you need all dependencies build and installed first (only on Debian/Ubuntu/Mint/Pi), run:
|
||||
|
||||
```
|
||||
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install.sh
|
||||
chmod +x download-and-install.sh
|
||||
./download-and-install.sh
|
||||
```
|
||||
|
||||
If you have dependencies already installed (i.e. need a fresh clean install on a system with DSD-FME already or using system other than Debian/Ubuntu, etc), please run this instead:
|
||||
|
||||
```
|
||||
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install-nodeps.sh
|
||||
chmod +x download-and-install-nodeps.sh
|
||||
./download-and-install-nodeps.sh
|
||||
```
|
||||
|
||||
### Ubuntu 18.04/LM19/Buster Note:
|
||||
The above install.sh should now function on older system types. You can elect to manually follow the steps down below. Do NOT Manually build and install ITPP 4.3.1 on older systems, it is currently not wanting to build on Ubuntu 18.04 and Linux Mint 19. Install it from the repository instead.
|
||||
|
||||
## Manual Install
|
||||
|
||||
First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different.
|
||||
|
||||
```
|
||||
sudo apt update
|
||||
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev
|
||||
```
|
||||
## Headless
|
||||
|
||||
If running headless, swap out pavucontrol for pulsemixer, and also install pulseaudio as well. Attempting to install pavucontrol in a headless environment may attempt to install a minimal desktop environment. Note: Default behavior of pulseaudio in a headless environment may be to be muted, so check by opening pulsemixer and unmuting and routing audio appropriately.
|
||||
|
||||
```
|
||||
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev pulsemixer pulseaudio
|
||||
```
|
||||
|
||||
### Build and Install ITPP - ONLY IF NOT IN REPO!!
|
||||
|
||||
```
|
||||
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
|
||||
tar xjf itpp*
|
||||
#if you can't cd into this folder, double check folder name first
|
||||
cd itpp-4.3.1
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
cd ..
|
||||
cd ..
|
||||
```
|
||||
|
||||
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice") This version of MBELib is 1.3.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
|
||||
cd mbelib
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
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.
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
```
|
||||
Optional 'Virtual Sinks' for routing audio from SDR++ or GQRX, Media Players, etc. into DSD-FME
|
||||
|
||||
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired.
|
||||
|
||||
```
|
||||
pacmd load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
||||
pacmd load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
|
||||
```
|
||||
|
||||
Already have this branch, and just want to pull the latest build? You can run the rebuild.sh file in the dsd-fme folder, or manually do the pull with the commands:
|
||||
|
||||
```
|
||||
##Open your clone folder##
|
||||
git pull
|
||||
##cd into your build folder##
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
```
|
||||
|
||||
If the call alert wav files aren't playing, then make sure to run the following in the dsd-fme folder to copy the wav files to the /usr/share/ folder and give them adequate permission to be accessed.
|
||||
|
||||
```
|
||||
sudo cp tone8.wav /usr/share/
|
||||
sudo cp tone24.wav /usr/share/
|
||||
sudo 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
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||
1,858606250
|
||||
2,858606250
|
||||
3,859606250
|
||||
4,859606250
|
||||
5,860606250
|
||||
6,860606250
|
||||
7,861606250
|
||||
8,861606250
|
||||
|
||||
|
@@ -1,19 +0,0 @@
|
||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||
999,862093750,default cc (come here first), optional use non-existent channel as first entry
|
||||
1,863093750
|
||||
2,862093750
|
||||
3,861093750
|
||||
4,860093750
|
||||
5,859093750
|
||||
6,863593750
|
||||
7,862593750
|
||||
8,861593750
|
||||
9,860593750
|
||||
10,859593750
|
||||
11,863393750
|
||||
12,862393750
|
||||
13,861393750
|
||||
14,860393750
|
||||
15,859393750
|
||||
|
||||
|
||||
|
@@ -1,8 +0,0 @@
|
||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||
999,456318750,default cc (come here first), optional use non-existent channel as first entry
|
||||
36,455756250
|
||||
54,455981250
|
||||
81,456318750
|
||||
|
||||
|
||||
|
||||
|
@@ -1,6 +0,0 @@
|
||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||
1,851375000
|
||||
2,851800000
|
||||
3,855987500
|
||||
4,858487500
|
||||
5,852312500
|
||||
|
@@ -1,5 +0,0 @@
|
||||
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
|
||||
1449,A,Fire Dispatch,Fire
|
||||
929,A,Fire Tac,Fire
|
||||
22033,DE,Law Dispatch,Law
|
||||
|
@@ -1,8 +0,0 @@
|
||||
channel(dec), freq(Hz)
|
||||
141,423862500
|
||||
142,424337500
|
||||
143,424912500
|
||||
203,424100000
|
||||
204,424625000
|
||||
271,422650000
|
||||
302,423287500
|
||||
|
@@ -1,5 +0,0 @@
|
||||
key(dec), value(dec), load any single keys as key 0
|
||||
0,23466
|
||||
1,12345
|
||||
2,32700
|
||||
63,32767
|
||||
|
@@ -1,40 +0,0 @@
|
||||
#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
|
||||
|
||||
#P25 P1 FDMA CC with C4FM
|
||||
#dsd-fme -i tcp -U 4532 -T -N 2> log.ans
|
||||
#dsd-fme -i rtl:0:850M:26:-2:8:0:6020 -T -N 2> log.ans
|
||||
|
||||
#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 rtl:0:855.625M:26:-2:12:0:6020 -T -mq -N 2> log.ans
|
||||
|
||||
#P25 P2 TDMA CC with QPSK (Simulcast)
|
||||
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -m2 -N 2> log.ans
|
||||
#dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -T -f2 -m2 -N 2> log.ans
|
||||
|
||||
#DMR TIII, Con+, Cap+
|
||||
dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
|
||||
#dsd-fme -fs -i rtl:0:450M:26:-2:8:0:6020 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans
|
||||
|
||||
#EDACS/EDACS-EA Digital Only
|
||||
#dsd-fme -fp -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||
#dsd-fme -fp -i rtl:0:850M:26:-2:24:0:6020 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||
|
||||
#NXDN48 Trunking with Standard Band Plan (Channels 0-800)
|
||||
#dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans
|
||||
#dsd-fme -fi -i rtl:0:450M:26:-2:8:75:6020 -T -G group.csv -N 2> log.ans
|
||||
|
||||
#NXDN48 Trunking with Channel Map (Custom Channels > 800)
|
||||
#dsd-fme -fi -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans
|
||||
#dsd-fme -fi -i rtl:0:450M:26:-2:8:75:6020 -T -G group.csv -C channel_map.csv -N 2> log.ans
|
||||
|
||||
#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
|
||||
#running under the assumption that the user will be connnecting the VFO to the same instance of SDR++
|
||||
#You can also specify an ip address to connect to a remote SDR++ instance, make sure that the settings
|
||||
#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
|
||||
|
||||
#Having a -G group.csv is purely optional, but useful if you want to block groups, or give groups names
|
||||
@@ -17,7 +17,8 @@
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
//pretty pretty colors
|
||||
//defined by CMakeLists.txt -- Disable by using cmake -DCOLORS=OFF ..
|
||||
#ifdef PRETTY_COLORS
|
||||
#define KNRM "\x1B[0m"
|
||||
#define KRED "\x1B[31m"
|
||||
#define KGRN "\x1B[32m"
|
||||
@@ -26,6 +27,16 @@
|
||||
#define KMAG "\x1B[35m"
|
||||
#define KCYN "\x1B[36m"
|
||||
#define KWHT "\x1B[37m"
|
||||
#else
|
||||
#define KNRM ""
|
||||
#define KRED ""
|
||||
#define KGRN ""
|
||||
#define KYEL ""
|
||||
#define KBLU ""
|
||||
#define KMAG ""
|
||||
#define KCYN ""
|
||||
#define KWHT ""
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
@@ -325,7 +336,9 @@ typedef struct
|
||||
|
||||
int p25_trunk; //experimental P25 trunking with RIGCTL (or RTLFM)
|
||||
int p25_is_tuned; //set to 1 if currently on VC, set back to 0 if on CC
|
||||
int trunk_hangtime; //hangtime in seconds before tuning back to CC
|
||||
float trunk_hangtime; //hangtime in seconds before tuning back to CC
|
||||
|
||||
int scanner_mode; //experimental -- use the channel map as a conventional scanner, quicker tuning, but no CC
|
||||
|
||||
//csv import filenames
|
||||
char group_in_file[1024];
|
||||
@@ -527,17 +540,19 @@ typedef struct
|
||||
int directmode;
|
||||
|
||||
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
|
||||
uint8_t data_header_blocks[2]; //collect number of blocks to follow from data header per slot
|
||||
int data_header_blocks[2]; //collect number of blocks to follow from data header per slot
|
||||
int data_block_counter[2]; //counter for number of data blocks collected
|
||||
uint8_t data_header_valid[2]; //flag for verifying the data header if still valid (in case of tact/burst fec errs)
|
||||
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
|
||||
uint8_t data_header_format[2]; //collect format of data header (conf or unconf) per slot
|
||||
uint8_t data_header_sap[2]; //collect sap info per slot
|
||||
uint8_t data_block_counter[2]; //counter for number of data blocks collected
|
||||
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
|
||||
|
||||
//new stuff below here
|
||||
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
|
||||
uint8_t dmr_pdu_sf[2][288]; //unified pdu 'superframe' //[slot][byte]
|
||||
uint8_t cap_plus_csbk_bits[12*8*4]; //CSBK Cap+ FL initial and appended block bit storage
|
||||
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
|
||||
uint8_t dmr_pdu_sf[2][288]; //unified pdu 'superframe' //[slot][byte]
|
||||
uint8_t cap_plus_csbk_bits[2][12*8*8]; //CSBK Cap+ FL initial and appended block bit storage, by slot
|
||||
uint8_t cap_plus_block_num; //received block number storage
|
||||
uint8_t data_block_crc_valid[2][25]; //flag each individual block as good crc on confirmed data
|
||||
char dmr_embedded_signalling[2][7][48]; //embedded signalling 2 slots by 6 vc by 48 bits (replacing TS1SuperFrame.TimeSlotRawVoiceFrame.Sync structure)
|
||||
|
||||
@@ -593,6 +608,7 @@ typedef struct
|
||||
int p2_is_lcch; //flag to tell us when a frame is lcch and not sacch
|
||||
|
||||
//iden freq storage for frequency calculations
|
||||
int p25_chan_tdma[16]; //set from iden_up vs iden_up_tdma
|
||||
int p25_chan_iden;
|
||||
int p25_chan_type[16];
|
||||
int p25_trans_off[16];
|
||||
@@ -649,13 +665,21 @@ typedef struct
|
||||
uint8_t nxdn_sacch_frame_segcrc[4];
|
||||
uint8_t nxdn_alias_block_number;
|
||||
char nxdn_alias_block_segment[4][4][8];
|
||||
|
||||
//site/srv/cch info
|
||||
char nxdn_location_category[14];
|
||||
uint32_t nxdn_location_sys_code;
|
||||
uint16_t nxdn_location_site_code;
|
||||
|
||||
//multi-key array for nxdn keys
|
||||
unsigned long long int rkey_array[0x1FF];
|
||||
//channel access information
|
||||
uint8_t nxdn_rcn;
|
||||
uint8_t nxdn_base_freq;
|
||||
uint8_t nxdn_step;
|
||||
uint8_t nxdn_bw;
|
||||
|
||||
//multi-key array
|
||||
unsigned long long int rkey_array[0xFFFF];
|
||||
int keyloader; //let us know the keyloader is active
|
||||
|
||||
//dmr late entry mi
|
||||
uint64_t late_entry_mi_fragment[2][7][3];
|
||||
@@ -809,6 +833,9 @@ int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
|
||||
void upsample (dsd_state * state, float invalue);
|
||||
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
||||
|
||||
//new p25lcw
|
||||
void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t irrecoverable_errors);
|
||||
|
||||
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
||||
void processHDU (dsd_opts * opts, dsd_state * state);
|
||||
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
||||
@@ -821,7 +848,8 @@ void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
|
||||
void processDSTAR_HD (dsd_opts * opts, dsd_state * state);
|
||||
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
|
||||
void processP2(dsd_opts * opts, dsd_state * state); //P2
|
||||
void processTSBK(dsd_opts * opts, dsd_state * state);
|
||||
void processTSBK(dsd_opts * opts, dsd_state * state); //P25 Trunking Single Block
|
||||
void processMPDU(dsd_opts * opts, dsd_state * state); //P25 Multi Block PDU (SAP 0x61 FMT 0x15 or 0x17 for Trunking Blocks)
|
||||
short dmr_filter(short sample);
|
||||
short nxdn_filter(short sample);
|
||||
short dpmr_filter(short sample);
|
||||
@@ -837,10 +865,15 @@ void ncursesClose ();
|
||||
//new NXDN Functions start here!
|
||||
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
||||
void nxdn_descramble (uint8_t dibits[], int len);
|
||||
//nxdn deinterleaving/depuncturing functions
|
||||
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
|
||||
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
|
||||
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
|
||||
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348]);
|
||||
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
|
||||
//type-d 'idas' deinterleaving/depuncturing functions
|
||||
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction);
|
||||
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
|
||||
//end
|
||||
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
|
||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
|
||||
|
||||
@@ -850,6 +883,7 @@ static uint8_t crc6(const uint8_t buf[], int len);
|
||||
static uint16_t crc12f(const uint8_t buf[], int len);
|
||||
static uint16_t crc15(const uint8_t buf[], int len);
|
||||
static uint16_t crc16cac(const uint8_t buf[], int len);
|
||||
static uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
|
||||
|
||||
/* NXDN Convolution functions */
|
||||
void CNXDNConvolution_start(void);
|
||||
@@ -872,7 +906,9 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
|
||||
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void nxdn_location_id_handler (dsd_state * state, uint32_t location_id);
|
||||
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
//Type-D SCCH Message Decoder
|
||||
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
|
||||
|
||||
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
||||
|
||||
@@ -926,11 +962,12 @@ void dmrMSData (dsd_opts * opts, dsd_state * state);
|
||||
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
|
||||
|
||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified)
|
||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
||||
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type);
|
||||
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state);
|
||||
uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||
void dmr_locn (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||
|
||||
//dmr alg stuff
|
||||
void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
|
||||
@@ -950,6 +987,7 @@ uint8_t crc7(uint8_t bits[], unsigned int len);
|
||||
//LFSR code courtesy of https://github.com/mattames/LFSR/
|
||||
void LFSR(dsd_state * state);
|
||||
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
|
||||
void LFSR64(dsd_state * state);
|
||||
|
||||
void Hamming_7_4_init();
|
||||
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
|
||||
|
||||
12
install.sh
12
install.sh
@@ -1,12 +0,0 @@
|
||||
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
|
||||
829
src/dmr_block.c
829
src/dmr_block.c
@@ -8,292 +8,416 @@
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||
//hopefully a more simplified (or logical) version...once you get past all the variables
|
||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||
{
|
||||
|
||||
uint32_t i, j, k;
|
||||
uint8_t slot = state->currentslot;
|
||||
|
||||
//clear out unified pdu 'superframe' slot
|
||||
for (short int i = 0; i < 288; i++)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i] = 0;
|
||||
}
|
||||
for (short int i = 0; i < 288; i++) state->dmr_pdu_sf[slot][i] = 0;
|
||||
|
||||
//TODO: ONLY RUN ON GOOD CRC?
|
||||
//reset block counter to 1
|
||||
state->data_block_counter[slot] = 1;
|
||||
|
||||
//collect relevant data header info, figure out format, and grab appropriate info from it
|
||||
if (state->data_p_head[slot] == 0)
|
||||
if (IrrecoverableErrors == 0 && CRCCorrect == 1) //&&CRCCorrect == 1
|
||||
{
|
||||
|
||||
uint8_t gi = dheader_bits[0]; //group or individual data
|
||||
uint8_t a = dheader_bits[1]; //response requested flag
|
||||
uint8_t ab = (uint8_t)ConvertBitIntoBytes(&dheader_bits[2], 2); //appended blocks
|
||||
uint8_t dpf = (uint8_t)ConvertBitIntoBytes(&dheader_bits[4], 4); //data packet format
|
||||
uint8_t sap = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 4); //service access point
|
||||
uint8_t poc = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 4); //padding octets
|
||||
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[16], 24); //destination llid
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[40], 24); //source llid
|
||||
|
||||
//extra tgt/src handling for XPT
|
||||
uint8_t target_hash[24];
|
||||
uint8_t tg_hash = 0;
|
||||
uint8_t is_xpt = 0;
|
||||
//set flag if XPT
|
||||
if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1;
|
||||
|
||||
//truncate tgt/src to 16-bit values if XPT
|
||||
if (is_xpt == 1)
|
||||
{
|
||||
target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16); //destination llid
|
||||
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16); //source llid
|
||||
if (gi == 0)
|
||||
{
|
||||
for (int i = 0; i < 16; i++) target_hash[i] = dheader_bits[24+i];
|
||||
tg_hash = crc8 (target_hash, 16);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t is_cap = 0;
|
||||
if (strcmp (state->dmr_branding_sub, "Cap+ ") == 0) is_cap = 1;
|
||||
|
||||
if (is_cap)
|
||||
{
|
||||
//truncate tg on group? or just on private/individual data?
|
||||
if (gi == 0) target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16);
|
||||
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16);
|
||||
}
|
||||
|
||||
//store source for dmr pdu packet handling (lrrp) when not available in completed message
|
||||
if (dpf != 15) state->dmr_lrrp_source[slot] = source;
|
||||
|
||||
//set dpf to storage for later use (UDT, SD, etc)
|
||||
state->data_header_format[slot] = dpf;
|
||||
|
||||
//Type Strings
|
||||
char sap_string[20];
|
||||
char mfid_string[20];
|
||||
char udtf_string[20];
|
||||
char sddd_string[20];
|
||||
sprintf (sap_string, "%s", "");
|
||||
sprintf (mfid_string, "%s", "");
|
||||
sprintf (udtf_string, "%s", "");
|
||||
sprintf (sddd_string, "%s", "");
|
||||
|
||||
//see 9.3 - ETSI TS 102 361-1 V2.5.1 (2017-10) for more info
|
||||
uint8_t f = dheader_bits[64]; //F -- Full message flag (F)
|
||||
uint8_t bf = (uint8_t)ConvertBitIntoBytes(&dheader_bits[65], 7); //Blocks to Follow (BF)
|
||||
|
||||
//confirmed data header
|
||||
uint8_t s = dheader_bits[72]; //S -- Re-Synchronize Flag
|
||||
uint8_t ns = (uint8_t)ConvertBitIntoBytes(&dheader_bits[73], 3); //N(S) -- send sequence number
|
||||
uint8_t fsn = (uint8_t)ConvertBitIntoBytes(&dheader_bits[76], 4); //Fragment Sequence Number (FSN)
|
||||
|
||||
//response header
|
||||
uint8_t r_class = (uint8_t)ConvertBitIntoBytes(&dheader_bits[72], 2);
|
||||
uint8_t r_type = (uint8_t)ConvertBitIntoBytes(&dheader_bits[74], 3);
|
||||
uint8_t r_status = (uint8_t)ConvertBitIntoBytes(&dheader_bits[77], 3);
|
||||
|
||||
//short data - status/precoded
|
||||
uint8_t s_ab_msb = (uint8_t)ConvertBitIntoBytes(&dheader_bits[2], 2); //appended block, msb
|
||||
uint8_t s_ab_lsb = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4);//appended block, lsb
|
||||
uint8_t s_ab_fin = (s_ab_msb << 2) | s_ab_lsb; //appended blocks, final value
|
||||
uint8_t s_source_port = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 3);
|
||||
uint8_t s_dest_port = (uint8_t)ConvertBitIntoBytes(&dheader_bits[67], 3);
|
||||
uint8_t s_status_precoded = (uint8_t)ConvertBitIntoBytes(&dheader_bits[70], 10);
|
||||
|
||||
//short data - raw
|
||||
uint8_t sd_sarq = dheader_bits[70]; //Selective Automatic Repeat reQuest
|
||||
uint8_t sd_f = dheader_bits[71]; //full message flag
|
||||
uint8_t sd_bp = (uint8_t)ConvertBitIntoBytes(&dheader_bits[72], 8); //bit padding
|
||||
|
||||
//short data - defined
|
||||
uint8_t dd_format = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 6);
|
||||
|
||||
//Unified Data Transport (UDT)
|
||||
uint8_t udt_format = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4);
|
||||
uint8_t udt_padnib = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 5);
|
||||
uint8_t udt_uab = (uint8_t)ConvertBitIntoBytes(&dheader_bits[70], 2); //udt appended blocks
|
||||
uint8_t udt_sf = dheader_bits[72];
|
||||
uint8_t udt_pf = dheader_bits[73];
|
||||
uint8_t udt_op = (uint8_t)ConvertBitIntoBytes(&dheader_bits[74], 6);
|
||||
|
||||
//p_head
|
||||
uint8_t p_sap = (uint8_t)ConvertBitIntoBytes(&dheader_bits[0], 4);
|
||||
uint8_t p_mfid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8);
|
||||
uint8_t p_pot = (uint8_t)ConvertBitIntoBytes(&dheader_bits[3], 5); //looking for LRRP pot_report
|
||||
|
||||
fprintf (stderr, "%s ", KGRN);
|
||||
state->data_block_counter[slot] = 1; //reset block counter to 1
|
||||
state->data_header_format[slot] = dheader[0]; //collect the entire byte, we can check group or individual, response requested as well on this
|
||||
state->data_header_sap[slot] = (dheader[1] & 0xF0) >> 4;
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Slot %d Data Header - ", slot+1);
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0x2)
|
||||
if (gi == 1 && dpf != 15) fprintf(stderr, "Group - ");
|
||||
if (gi == 0 && dpf != 15) fprintf(stderr, "Indiv - ");
|
||||
|
||||
if (dpf == 0) fprintf (stderr, "Unified Data Transport (UDT) ");
|
||||
else if (dpf == 1) fprintf (stderr, "Response Packet ");
|
||||
else if (dpf == 2) fprintf (stderr, "Unconfirmed Delivery ");
|
||||
else if (dpf == 3) fprintf (stderr, "Confirmed Delivery ");
|
||||
else if (dpf == 13) fprintf (stderr, "Short Data: Defined ");
|
||||
else if (dpf == 14) fprintf (stderr, "Short Data: Raw or S/P ");
|
||||
else if (dpf == 15) fprintf (stderr, "Proprietary Packet Data");
|
||||
else fprintf (stderr, "Reserved/Unknown DPF %X ", dpf);
|
||||
|
||||
if (a == 1 && dpf != 15) fprintf(stderr, "- Response Requested ");
|
||||
if (dpf != 15) fprintf (stderr, "- Source: %d Target: %d ", source, target);
|
||||
|
||||
//include the hash value if this is an XPT and if its IND data
|
||||
if (dpf != 15 && is_xpt == 1 && gi == 0) fprintf (stderr, "Hash: %d ", tg_hash);
|
||||
|
||||
//sap string handling
|
||||
if (dpf == 15) sap = p_sap;
|
||||
|
||||
if (sap == 0) sprintf (sap_string, "%s", "UDT Data"); //apparently, both dpf and sap for UDT is 0
|
||||
else if (sap == 2) sprintf (sap_string, "%s", "TCP Comp"); //TCP/IP header compression
|
||||
else if (sap == 3) sprintf (sap_string, "%s", "UDP Comp"); //UDP/IP header compression
|
||||
else if (sap == 4) sprintf (sap_string, "%s", "IP Based"); //IP based Packet Data
|
||||
else if (sap == 5) sprintf (sap_string, "%s", "ARP Prot"); //Address Resoution Protocol (ARP)
|
||||
else if (sap == 9) sprintf (sap_string, "%s", "Prop PDU"); //Proprietary Packet Data
|
||||
else if (sap == 10) sprintf (sap_string, "%s", "Short DT"); //Short Data
|
||||
else sprintf (sap_string, "%s", "Reserved"); //reserved, or err/unk
|
||||
|
||||
//mfid string handling
|
||||
if (dpf == 15)
|
||||
{
|
||||
state->data_conf_data[slot] = 0; //flag as unconfirmed data, no need for additional CRC/Serial checks
|
||||
state->data_header_padding[slot] = dheader[1] & 0xF; //number of padding octets on last block
|
||||
state->data_header_blocks[slot] = dheader[8] & 0x7F; //number of blocks to follow, most useful for when to poll the super array for data
|
||||
fprintf (stderr, "\n Unconfirmed Data Header: DPF %01X - SAP %02X - Block Count %02X - Padding Octets %02X",
|
||||
state->data_header_format[slot] & 0xF, state->data_header_sap[slot],
|
||||
state->data_header_blocks[slot], state->data_header_padding[slot] );
|
||||
if (p_mfid == 0x10) sprintf (mfid_string, "%s", "Motorola");
|
||||
else if (p_mfid == 0x58) sprintf (mfid_string, "%s", "Tait");
|
||||
else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera");
|
||||
else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera");
|
||||
else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot");
|
||||
else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard");
|
||||
else sprintf (mfid_string, "%s", "Other");
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0x3)
|
||||
//udt format string handling
|
||||
if (dpf == 0)
|
||||
{
|
||||
state->data_conf_data[slot] = 1; //set flag to indicate the following blocks are confirmed data so we know to run additional CRC etc on it.
|
||||
state->data_header_padding[slot] = dheader[1] & 0xF; //number of padding octets on last block
|
||||
state->data_header_blocks[slot] = dheader[8] & 0x7F; //number of blocks to follow, most useful for when to poll the super array for data
|
||||
fprintf (stderr, "\n Confirmed Data Header: DPF %01X - SAP %02X - Block Count %02X - Padding Octets %02X",
|
||||
state->data_header_format[slot] & 0xF, state->data_header_sap[slot],
|
||||
state->data_header_blocks[slot], state->data_header_padding[slot] );
|
||||
if (udt_format == 0x00) sprintf (udtf_string, "%s", "Binary");
|
||||
else if (udt_format == 0x01) sprintf (udtf_string, "%s", "MS/TG Adr");
|
||||
else if (udt_format == 0x02) sprintf (udtf_string, "%s", "4-bit BCD");
|
||||
else if (udt_format == 0x03) sprintf (udtf_string, "%s", "ISO7 Char");
|
||||
else if (udt_format == 0x04) sprintf (udtf_string, "%s", "ISO8 Char");
|
||||
else if (udt_format == 0x05) sprintf (udtf_string, "%s", "NMEA LOCN");
|
||||
else if (udt_format == 0x06) sprintf (udtf_string, "%s", "IP Addr");
|
||||
else if (udt_format == 0x07) sprintf (udtf_string, "%s", "UTF-16"); //16-bit Unicode Chars
|
||||
else if (udt_format == 0x08) sprintf (udtf_string, "%s", "Manu Spec"); //Manufacturer Specific
|
||||
else if (udt_format == 0x09) sprintf (udtf_string, "%s", "Manu Spec"); //Manufacturer Specific
|
||||
else if (udt_format == 0x0A) sprintf (udtf_string, "%s", "Mixed UTF"); //Appended block contains addr and 16-bit UTF-16BE
|
||||
else sprintf (udtf_string, "%s", "Reserved");
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0xD) //DD_HEAD
|
||||
//short data dd_head format string
|
||||
if (dpf == 13)
|
||||
{
|
||||
//The Response Requested (A) information element of the header shall be set to 0 for unconfirmed data and shall be set
|
||||
//to 1 for confirmed data. ETSI TS 102 361-3 V1.2.1 (2013-07) - 6.1.2
|
||||
if ( (state->data_header_format[slot] & 0x40) == 0x40 && state->data_p_head[slot] == 0) //check response req bit
|
||||
if (dd_format == 0x00) sprintf (sddd_string, "%s", "Binary");
|
||||
else if (dd_format == 0x01) sprintf (sddd_string, "%s", "BCD ");
|
||||
else if (dd_format == 0x02) sprintf (sddd_string, "%s", "7-bit Char");
|
||||
else if (dd_format == 0x03) sprintf (sddd_string, "%s", "IEC 8859-1");
|
||||
else if (dd_format == 0x04) sprintf (sddd_string, "%s", "IEC 8859-2");
|
||||
else if (dd_format == 0x05) sprintf (sddd_string, "%s", "IEC 8859-3");
|
||||
else if (dd_format == 0x06) sprintf (sddd_string, "%s", "IEC 8859-4");
|
||||
else if (dd_format == 0x07) sprintf (sddd_string, "%s", "IEC 8859-5");
|
||||
else if (dd_format == 0x08) sprintf (sddd_string, "%s", "IEC 8859-6");
|
||||
else if (dd_format == 0x09) sprintf (sddd_string, "%s", "IEC 8859-7");
|
||||
else if (dd_format == 0x0A) sprintf (sddd_string, "%s", "IEC 8859-8");
|
||||
else if (dd_format == 0x0B) sprintf (sddd_string, "%s", "IEC 8859-9");
|
||||
else if (dd_format == 0x0C) sprintf (sddd_string, "%s", "IEC 8859-10");
|
||||
else if (dd_format == 0x0D) sprintf (sddd_string, "%s", "IEC 8859-11");
|
||||
else if (dd_format == 0x0E) sprintf (sddd_string, "%s", "IEC 8859-13"); //there is no 8059-12
|
||||
else if (dd_format == 0x0F) sprintf (sddd_string, "%s", "IEC 8859-14");
|
||||
else if (dd_format == 0x10) sprintf (sddd_string, "%s", "IEC 8859-15");
|
||||
else if (dd_format == 0x11) sprintf (sddd_string, "%s", "IEC 8859-16");
|
||||
else if (dd_format == 0x12) sprintf (sddd_string, "%s", "UTF-8 ");
|
||||
else if (dd_format == 0x13) sprintf (sddd_string, "%s", "UTF-16 ");
|
||||
else if (dd_format == 0x14) sprintf (sddd_string, "%s", "UTF-16BE");
|
||||
else if (dd_format == 0x15) sprintf (sddd_string, "%s", "UTF-16LE");
|
||||
else if (dd_format == 0x16) sprintf (sddd_string, "%s", "UTF-32 ");
|
||||
else if (dd_format == 0x17) sprintf (sddd_string, "%s", "UTF-32BE");
|
||||
else if (dd_format == 0x18) sprintf (sddd_string, "%s", "UTF-32LE");
|
||||
else sprintf (sddd_string, "%s", "Reserved");
|
||||
}
|
||||
|
||||
if (dpf == 0) //UDT
|
||||
{
|
||||
//UDT packet info -- samples needed for testing
|
||||
//NOTE: This format's completed message has a CRC16 - like MBC - but has number of blocks (appended blocks) like R 1/2, etc.
|
||||
fprintf (stderr, "\n SAP %02X [%s] - FMT %d [%s] - PDn %d - BLOCKS %d SF %d - PF %d OP %02X", sap, sap_string, udt_format, udtf_string, udt_padnib, udt_uab, udt_sf, udt_pf, udt_op);
|
||||
|
||||
//set number of blocks to follow (appended blocks) for block assembler
|
||||
state->data_header_blocks[slot] = udt_uab;
|
||||
|
||||
//set data header to valid
|
||||
state->data_header_valid[slot] = 1;
|
||||
|
||||
//send to assembler as type 3, rearrange into CSBK type PDU (to verify), and send to dmr_cspdu
|
||||
dmr_block_assembler (opts, state, dheader, 12, 0x0B, 3);
|
||||
|
||||
}
|
||||
|
||||
if (dpf == 1) //response data packet header
|
||||
{
|
||||
//mostly fleshed out response packet info
|
||||
fprintf (stderr, "\n SAP %02X [%s] - Class %d - Type %0d - ", sap, sap_string, r_class, r_type);
|
||||
if (r_class == 0 && r_type == 1) fprintf (stderr, "ACK - Success");
|
||||
if (r_class == 1)
|
||||
{
|
||||
state->data_conf_data[slot] = 1;
|
||||
fprintf (stderr, "NACK - ");
|
||||
if (r_type == 0) fprintf (stderr, "Illegal Format");
|
||||
if (r_type == 1) fprintf (stderr, "Illegal Format");
|
||||
if (r_type == 2) fprintf (stderr, "Packet CRC ERR");
|
||||
if (r_type == 3) fprintf (stderr, "Memory Full");
|
||||
if (r_type == 4) fprintf (stderr, "FSN Out of Seq");
|
||||
if (r_type == 5) fprintf (stderr, "Undeliverable");
|
||||
if (r_type == 6) fprintf (stderr, "PKT Out of Seq");
|
||||
if (r_type == 7) fprintf (stderr, "Invalid User");
|
||||
}
|
||||
else state->data_conf_data[slot] = 0;
|
||||
if (r_class == 2) fprintf (stderr, "SACK - Retry");
|
||||
if (r_status) fprintf (stderr, " - %d", r_status); //the object/value of the ack/nack/sack
|
||||
|
||||
state->data_header_padding[slot] = dheader[7] & 0xF;
|
||||
state->data_header_blocks[slot] = ( (dheader[0] & 0x30) | ((dheader[1] & 0xF) >> 0) ); //what a pain MSB | LSB
|
||||
fprintf (stderr, "\n Short Data - Defined: DPF %01X - SAP %02X - Appended Blocks %02X - Padding Bits %02X",
|
||||
state->data_header_format[slot] & 0xF, state->data_header_sap[slot],
|
||||
state->data_header_blocks[slot], state->data_header_padding[slot] );
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0xE) //SP_HEAD or R_HEAD
|
||||
//Confirmed or Unconfirmed Data Packets Header
|
||||
if (dpf == 2 || dpf == 3)
|
||||
{
|
||||
//The Response Requested (A) information element of the header shall be set to 0 for unconfirmed data and shall be set
|
||||
//to 1 for confirmed data. ETSI TS 102 361-3 V1.2.1 (2013-07) - 6.2.2
|
||||
if ( (state->data_header_format[slot] & 0x40) == 0x40 && state->data_p_head[slot] == 0) //check response req bit
|
||||
if (dpf == 2) fprintf (stderr, "\n SAP %02X [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN %d", sap, sap_string, f, bf, poc, fsn);
|
||||
if (dpf == 3) fprintf (stderr, "\n SAP %02X [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN %d", sap, sap_string, f, bf, poc, s, ns, fsn);
|
||||
state->data_header_blocks[slot] = bf;
|
||||
if (dpf == 3) state->data_conf_data[slot] = 1; //set confirmed data delivery flag for additional CRC checks, block assembly, etc.
|
||||
|
||||
}
|
||||
|
||||
//Short Data DD_Head (13), and R_Head or SP_Head (14)
|
||||
if (dpf == 13 || dpf == 14)
|
||||
{
|
||||
//only set if not all zeroes
|
||||
if (s_ab_fin) state->data_header_blocks[slot] = s_ab_fin;
|
||||
|
||||
//Short Data: Defined
|
||||
if (dpf == 13) fprintf (stderr, "\n SD:D [DD_HEAD] - SAP %02d [%s] - BLOCKS %02d - DD %02X - PADb %d - FMT %02X [%s]", sap, sap_string, s_ab_fin, dd_format, sd_bp, dd_format, sddd_string);
|
||||
//Short Data: Raw or S/P
|
||||
if (dpf == 14)
|
||||
{
|
||||
state->data_conf_data[slot] = 1;
|
||||
//S/P has all appended block bits set to zero -- any other way to tell difference?
|
||||
if (s_ab_fin == 0) fprintf (stderr, "\n SD:S/P [SP_HEAD] - SAP %02d [%s] - SP %02d - DP %02d - S/P %02X", sap, sap_string, s_source_port, s_dest_port, s_status_precoded);
|
||||
|
||||
//Raw
|
||||
else fprintf (stderr, "\n SD:RAW [R_HEAD] - SAP %02d [%s] - BLOCKS %02d - SP %02d - DP %02d - SARQ %d - FMF %d - PDb %d", sap, sap_string, s_ab_fin, s_source_port, s_dest_port, sd_sarq, sd_f, sd_bp);
|
||||
}
|
||||
else state->data_conf_data[slot] = 0;
|
||||
|
||||
state->data_header_padding[slot] = dheader[7] & 0xF;
|
||||
state->data_header_blocks[slot] = ( (dheader[0] & 0x30) | ((dheader[1] & 0xF) >> 0) );
|
||||
fprintf (stderr, "\n Short Data - Raw or Precoded: DPF %01X - SAP %02X - Appended Blocks %02X - Padding Bits %02X",
|
||||
state->data_header_format[slot] & 0xF, state->data_header_sap[slot],
|
||||
state->data_header_blocks[slot], state->data_header_padding[slot] );
|
||||
//6.2.2 The Response Requested (A) information element of the header shall be set to 0
|
||||
//for unconfirmed data and shall be set to 1 for confirmed data. DD_HEAD, R_HEAD, or SP_HEAD (double check)
|
||||
if (a == 1)
|
||||
{
|
||||
state->data_conf_data[slot] = 1;
|
||||
fprintf (stderr, " - Confirmed Data");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//do we still need this? since we have ample PDU storage
|
||||
if (state->data_header_blocks[slot] > 7)
|
||||
{
|
||||
state->data_header_blocks[slot] = 7;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
//end collecting data header info
|
||||
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
|
||||
if (state->data_header_sap[slot] == 0x4 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n IP Data - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
|
||||
|
||||
//Remus! Uncomment these line
|
||||
// if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0)
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "IP DATA SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x0 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Unified Data Transport - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
|
||||
|
||||
//Remus! Uncomment these line
|
||||
// if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0)
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "UDT SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x9 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Proprietary Packet Data - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
|
||||
|
||||
//Remus! Uncomment this line
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "P_HEAD SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x2 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n TCP/IP Header Compression Data - Source: ");
|
||||
fprintf (stderr, "[%d]", dheader[3]);
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", dheader[2]);
|
||||
|
||||
state->dmr_lrrp_source[slot] = dheader[3];
|
||||
|
||||
//Remus! Uncomment these line
|
||||
// if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0)
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "IPC SRC [%d] DST [%d] ", dheader[3], dheader[2]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x3 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n UDP/IP Header Compression Data - Source: ");
|
||||
fprintf (stderr, "[%d]", dheader[3]);
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", dheader[2]);
|
||||
|
||||
state->dmr_lrrp_source[slot] = dheader[3];
|
||||
|
||||
//Remus! Uncomment these line
|
||||
// if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0)
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "UDP SRC [%d] DST [%d] ", dheader[3], dheader[2]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x5 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n ARP - Address Resolution Protocol ");
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF ) == 0xD && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Short Data - Defined - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
//Remus! Uncomment this line
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "SD-D SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0xE && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Short Data - Raw or Status/Precoded - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
//Remus! Uncomment this line
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "SD-R/P SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF ) == 0x1 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Response Packet - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
//Remus! Uncomment these lines
|
||||
// char rsp[90];
|
||||
// sprintf (rsp, " - RSP SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
// strcat (state->dmr_lrrp_gps[slot], rsp);
|
||||
|
||||
}
|
||||
|
||||
//Removed Dup UDT Message
|
||||
|
||||
if ( (state->data_header_format[slot] & 0x40) == 0x40 && state->data_p_head[slot] == 0) //check response req bit
|
||||
{
|
||||
fprintf (stderr, "\n Response Requested - ");
|
||||
fprintf (stderr, " Source:");
|
||||
fprintf (stderr, " [%d]", (dheader[2] <<16 ) + (dheader[3] << 8) + dheader[4] );
|
||||
fprintf (stderr, " Destination:");
|
||||
fprintf (stderr, " [%d]", (dheader[5] <<16 ) + (dheader[6] <<8 ) + dheader[7] );
|
||||
|
||||
//Remus! Uncomment these lines
|
||||
// char rsp[90];
|
||||
// sprintf (rsp, "- RSP REQ ");
|
||||
// strcat (state->dmr_lrrp_gps[slot], rsp);
|
||||
|
||||
}
|
||||
|
||||
//if proprietary header was flagged by the last data header, then process data differently
|
||||
if (state->data_p_head[slot] == 1)
|
||||
{
|
||||
fprintf (stderr, "\n Proprietary Data Header: SAP %01X - Format %01X - MFID %02X",
|
||||
(dheader[0] & 0xF0) >> 4, dheader[0] & 0xF, dheader[1] );
|
||||
|
||||
//state->data_p_head[slot] = 0; //disabling this, we can flag it off after processing pdu instead
|
||||
state->data_header_sap[slot] = 0; //reset this to zero so we don't trip the condition below
|
||||
state->data_block_counter[slot]++; //increment the counter since this counts against the data blocks
|
||||
|
||||
//set overarching manufacturer in use when non-standard feature id set is up
|
||||
//if (dheader[1] != 0) state->dmr_mfid = dheader[1]; //diable, will only set in link control or csbk for now (random manufacturers getting set, bad decode? or random feature?)
|
||||
|
||||
//assign prop header pdu to 'superframe'
|
||||
uint8_t blocks = state->data_header_blocks[slot] - 1;
|
||||
for(i = 0; i < 12; i++)
|
||||
|
||||
//Proprietary Data Header
|
||||
if (dpf == 15)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i+(blocks*12)] = dheader[i];
|
||||
fprintf (stderr, " - P_SAP %02d [%s] - MFID %02X [%s]", p_sap, sap_string, p_mfid, mfid_string);
|
||||
|
||||
//look for LRRP potential report 0x1F (may be others as well)
|
||||
//this could just be a sap == 1, which is 'reserved', unsure (or maybe no actual correlation?)
|
||||
if (p_sap != 1) state->data_header_blocks[slot]--; //decrement the blocks to follow count in order to pass CRC32 and properly assemble blocks
|
||||
else //add block to start of the dmr_pdu_sf since it probably contains vital LRRP data (0x1F report) -- won't pass CRC32 either way
|
||||
{
|
||||
int blocks = state->data_header_blocks[slot]-1;
|
||||
if (blocks < 1) blocks = 4; //safety value
|
||||
if (blocks > 9) blocks = 9;
|
||||
for(i = 0; i < 12; i++) state->dmr_pdu_sf[slot][i+(blocks*12)] = dheader[i];
|
||||
state->data_block_counter[slot]++;
|
||||
//no matter how you assemble this, with or without the p_head block(s), there is ALWAYS a CRC32 Failure
|
||||
//even when it continues to pass on other SAP values, SAP 1 must have a different checksum?
|
||||
//Users trying to get this will want to enable -F to bypass the CRC32 failure
|
||||
|
||||
//a recent discovery on a few systems and samples show the existence of multiple Prop Head Packets (how does that work)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//only set this flag after everything else is processed
|
||||
if (state->data_header_sap[slot] == 0x9 && state->data_p_head[slot] == 0)
|
||||
//block storage sanity
|
||||
if (state->data_header_blocks[slot] > 9) state->data_header_blocks[slot] = 9;
|
||||
//assuming we didn't receive the initial data header block on a p_head and then decremented it
|
||||
//3 or 4 seems to be the average safe value
|
||||
if (state->data_header_blocks[slot] < 1) state->data_header_blocks[slot] = 4;
|
||||
//set data header validity unless its a p_head (should be set prior, if received)
|
||||
if (dpf != 15) state->data_header_valid[slot] = 1;
|
||||
|
||||
//REMUS! Uncomment these lines if desired
|
||||
if (dpf != 15 && dpf != 1)
|
||||
{
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Data Call - %s SRC [%d] DST [%d] ", sap_string, source, target);
|
||||
if (a == 1) strcat (state->dmr_lrrp_gps[slot], "- RSP REQ ");
|
||||
}
|
||||
|
||||
//REMUS! Uncomment these lines if desired -- if the data request data header is missing, though, this may keep accumulating
|
||||
// if (dpf == 1)
|
||||
// {
|
||||
// strcat (state->dmr_lrrp_gps[slot], "- RSP ");
|
||||
// if (r_class == 0) strcat (state->dmr_lrrp_gps[slot], "ACK - RX OK");
|
||||
// if (r_class == 1) strcat (state->dmr_lrrp_gps[slot], "NACK - RX ERR");
|
||||
// if (r_class == 2) strcat (state->dmr_lrrp_gps[slot], "SACK - RETRY");
|
||||
|
||||
// }
|
||||
//End REMUS Data string
|
||||
|
||||
//store SAP value
|
||||
state->data_header_sap[slot] = sap;
|
||||
|
||||
} //End Irrecoverable Errors
|
||||
|
||||
if (IrrecoverableErrors != 0)
|
||||
{
|
||||
fprintf (stderr, "\n Proprietary Packet Data Incoming ");
|
||||
state->data_p_head[slot] = 1; //flag that next data header will be a proprietary data header
|
||||
state->data_header_valid[slot] = 0;
|
||||
sprintf (state->dmr_lrrp_gps[slot], "%s", "");
|
||||
state->data_p_head[slot] = 0;
|
||||
state->data_conf_data[slot] = 0;
|
||||
state->data_block_counter[slot] = 1;
|
||||
state->data_header_blocks[slot] = 1;
|
||||
state->data_header_format[slot] = 7;
|
||||
|
||||
}
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
|
||||
//let user know the data header may have errors
|
||||
// fprintf (stderr, "%s", KRED);
|
||||
// if (CRCCorrect == 1) ; //CRCCorrect 1 is good, else is bad CRC; no print on good
|
||||
// else if(IrrecoverableErrors == 0) fprintf(stderr, " (FEC OK)");
|
||||
// else fprintf(stderr, " (FEC/CRC ERR)");
|
||||
// fprintf (stderr, "%s", KNRM);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
//convert UDT to a CSBK friendly PDU format
|
||||
void dmr_udt_cspdu_converter (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
//rearrange UDT data to a CSBK message format
|
||||
uint8_t cs_byte[48]; //12 x 4, 2 bit AB should mean only 3 total appended blocks, so 4 all together (minus rearrangement)
|
||||
memset (cs_byte, 0, sizeof (cs_byte));
|
||||
uint8_t cs_bits[48*8];
|
||||
memset (cs_bits, 0, sizeof (cs_bits));
|
||||
|
||||
cs_byte[0] = (uint8_t)ConvertBitIntoBytes(&block_bits[73], 7); //lb, pf and opcode byte
|
||||
uint8_t udt_opcode = (uint8_t)ConvertBitIntoBytes(&block_bits[74], 6); //for observation testing
|
||||
cs_byte[1] = 0; //set mfid to zero for now (may use custom value if needed later)
|
||||
uint8_t udt_mfid = 0; //setting to zero
|
||||
|
||||
//should be 34 total (maximum) after removing tailing CRC16 (initial and last block)
|
||||
for (i = 0; i < 34; i++) cs_byte[2+i] = block_bytes[12+i];
|
||||
|
||||
//bytes to bits
|
||||
for(i = 0, j = 0; i < 36; i++, j+=8) //4 blocks max?
|
||||
{
|
||||
cs_bits[j + 0] = (cs_byte[i] >> 7) & 0x01;
|
||||
cs_bits[j + 1] = (cs_byte[i] >> 6) & 0x01;
|
||||
cs_bits[j + 2] = (cs_byte[i] >> 5) & 0x01;
|
||||
cs_bits[j + 3] = (cs_byte[i] >> 4) & 0x01;
|
||||
cs_bits[j + 4] = (cs_byte[i] >> 3) & 0x01;
|
||||
cs_bits[j + 5] = (cs_byte[i] >> 2) & 0x01;
|
||||
cs_bits[j + 6] = (cs_byte[i] >> 1) & 0x01;
|
||||
cs_bits[j + 7] = (cs_byte[i] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
dmr_cspdu (opts, state, cs_bits, cs_byte, CRCCorrect, IrrecoverableErrors);
|
||||
|
||||
}
|
||||
|
||||
//assemble the blocks as they come in, shuffle them into the unified dmr_pdu_sf
|
||||
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type)
|
||||
{
|
||||
int i, j, k;
|
||||
uint8_t lb = 0; //mbc last block
|
||||
uint8_t pf = 0; //mbc protect flag
|
||||
// uint8_t is_conf_data = 0; //internal flag to see if type 1 blocks contain confirmed data crcs and serials
|
||||
|
||||
uint8_t slot = state->currentslot;
|
||||
int blocks;
|
||||
uint8_t blockcounter = state->data_block_counter[slot];
|
||||
uint8_t block_num = state->data_header_blocks[slot];
|
||||
|
||||
//crc specific variables
|
||||
// uint8_t len = 24;
|
||||
// uint8_t crclen = 32; //len of the crc32 (obviously)
|
||||
// uint8_t start = 12; //starting position (+12 offset loaded up data header)
|
||||
// uint8_t n = 12; //number of bytes per block
|
||||
|
||||
//TODO: CRC check on Header, Blocks and full frames as appropriate
|
||||
uint32_t CRCCorrect = 0;
|
||||
uint32_t CRCComputed = 0;
|
||||
uint32_t CRCExtracted = 0;
|
||||
uint32_t IrrecoverableErrors = 0;
|
||||
|
||||
uint8_t dmr_pdu_sf_bits[8*8*50]; //give plenty of space so we don't go oob
|
||||
uint8_t dmr_pdu_sf_bits[8*24*50]; //give plenty of space so we don't go oob
|
||||
|
||||
//MBC Header and Block CRC
|
||||
uint8_t mbc_crc_good[2]; //header and blocks crc pass/fail local storage
|
||||
@@ -302,20 +426,27 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
if (type == 1) blocks = state->data_header_blocks[slot] - 1;
|
||||
if (type == 2) blocks = state->data_block_counter[slot];
|
||||
|
||||
//UDT Header and Block Check
|
||||
uint8_t is_udt = 0;
|
||||
if (type == 3)
|
||||
{
|
||||
blocks = state->data_block_counter[slot];
|
||||
is_udt = 1;
|
||||
type = 2;
|
||||
}
|
||||
|
||||
//sanity check, setting block_len and block values to sane numbers in case of missing header, else could overload array (crash) or print out
|
||||
if (blocks < 1) blocks = 1; //changed blocks from uint8_t to int
|
||||
if (blocks > 8) blocks = 8;
|
||||
if (blocks > 9) blocks = 9;
|
||||
if (block_len == 0) block_len = 18;
|
||||
if (block_len > 24) block_len = 24;
|
||||
|
||||
//WIP: CRC32 on Completed Message Working! CRC9 on 3/4 Working! CRC9 or 1/2 and 1 still need testing - type 1
|
||||
//may/will still fail on p_head data and blocks
|
||||
if (type == 1)
|
||||
{
|
||||
//type 1 data block, shuffle method
|
||||
for (j = 0; j < blocks; j++) //6
|
||||
for (j = 0; j < blocks; j++)
|
||||
{
|
||||
for (i = 0; i < block_len; i++) //block_len
|
||||
for (i = 0; i < block_len; i++)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i+block_len*j] = state->dmr_pdu_sf[slot][i+block_len*(j+1)];
|
||||
}
|
||||
@@ -325,8 +456,9 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i+(blocks*block_len)] = block_bytes[i];
|
||||
}
|
||||
|
||||
//time to send the completed 'superframe' to the DMR PDU message handler
|
||||
if (state->data_block_counter[slot] == state->data_header_blocks[slot])
|
||||
if (state->data_block_counter[slot] == state->data_header_blocks[slot] && state->data_header_valid[slot] == 1)
|
||||
{
|
||||
//CRC32 on completed messages
|
||||
for(i = 0, j = 0; i < (block_num * block_len); i++, j+=8)
|
||||
@@ -388,28 +520,77 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
//individual CRC9s are correct on confirmed data blocks (but we can confirm now that they are all good)
|
||||
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||
|
||||
//test only running on good CRC or CRC/RAS bypass (aggressive_framesync)
|
||||
if (CRCCorrect || opts->aggressive_framesync == 0) dmr_pdu (opts, state, block_len, state->dmr_pdu_sf[slot]);
|
||||
//only run on good CRC or CRC/RAS bypass (aggressive_framesync == 0)
|
||||
if (CRCCorrect || opts->aggressive_framesync == 0)
|
||||
dmr_pdu (opts, state, block_len, state->dmr_pdu_sf[slot]);
|
||||
|
||||
if (CRCCorrect) ; //print nothing
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "\n Slot %d - Multi Block PDU Message CRC32 ERR", slot+1);
|
||||
|
||||
//debug print
|
||||
// fprintf (stderr, " %X - %X", CRCExtracted, CRCComputed);
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
state->data_conf_data[slot] = 0; //flag off conf data flag
|
||||
state->data_header_format[slot] = 0; //zero out so we don't keep ascii printing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Full Super Frame Type 1 - Payload Output
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, "\n Slot %d - Multi Block PDU Message\n ", slot+1);
|
||||
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||
{
|
||||
fprintf (stderr, "\n ");
|
||||
}
|
||||
}
|
||||
|
||||
//debug print
|
||||
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
||||
|
||||
if (state->data_header_format[slot] == 13 || state->data_header_format[slot] == 14 || state->data_header_sap[slot] == 10)
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, "\n Short Data: ASCII \n" );
|
||||
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||
{
|
||||
if (state->dmr_pdu_sf[slot][i] <= 0x7E && state->dmr_pdu_sf[slot][i] >=0x20)
|
||||
{
|
||||
fprintf (stderr, "%c", state->dmr_pdu_sf[slot][i]);
|
||||
}
|
||||
else fprintf (stderr, " ");
|
||||
|
||||
if (i == 47 || i == 95) fprintf (stderr, "\n ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
//reset data header format storage
|
||||
state->data_header_format[slot] = 7;
|
||||
//flag off data header validity
|
||||
state->data_header_valid[slot] = 0;
|
||||
//flag off conf data flag
|
||||
state->data_conf_data[slot] = 0;
|
||||
|
||||
} //end completed sf
|
||||
|
||||
} //end type 1 blocks
|
||||
|
||||
|
||||
//type 2 - MBC header and MBC continuation blocks
|
||||
//type 2 - MBC and UDT header, MBC and UDT continuation blocks
|
||||
if (type == 2)
|
||||
{
|
||||
//sanity check (marginal signal, bad decodes, etc)
|
||||
if (state->data_block_counter[slot] > 8) state->data_block_counter[slot] = 8;
|
||||
//sanity check (marginal signal, bad decodes, etc) -- may go a little lower (find out max number of MBC blocks supported)
|
||||
if (state->data_block_counter[slot] > 4) state->data_block_counter[slot] = 4;
|
||||
|
||||
//Type 2 data block, additive method
|
||||
for (i = 0; i < block_len; i++)
|
||||
@@ -422,9 +603,17 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
lb = block_bytes[0] >> 7; //last block flag
|
||||
pf = (block_bytes[0] >> 6) & 1;
|
||||
|
||||
if (lb == 1) //last block arrived, time to send to cspdu decoder
|
||||
if (is_udt)
|
||||
{
|
||||
for(i = 0, j = 0; i < 12*4; i++, j+=8) //
|
||||
blocks = state->data_header_blocks[slot];
|
||||
if (blocks == blockcounter) lb = 1; //double check
|
||||
pf = 0; //flag off is set from bit in error
|
||||
}
|
||||
|
||||
//last block arrived and we have a valid data header, time to send to cspdu decoder
|
||||
if (lb == 1 && state->data_header_valid[slot] == 1)
|
||||
{
|
||||
for(i = 0, j = 0; i < 12*4; i++, j+=8) //4 blocks max?
|
||||
{
|
||||
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||
@@ -436,10 +625,13 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
//check UDT PF
|
||||
if (is_udt) pf = dmr_pdu_sf_bits[73];
|
||||
|
||||
//CRC check on Header and full frames as appropriate (header crc already stored)
|
||||
//The 16 bit CRC in the header shall include the data carried by the header.
|
||||
//The 16 bit CRC in the last block shall be performed on
|
||||
//all MBC blocks (conmbined) except the header block. <-- misleading statement, just like half the dmr manual
|
||||
//all MBC blocks (conmbined) except the header block.
|
||||
|
||||
mbc_crc_good[0] = state->data_block_crc_valid[slot][0];
|
||||
|
||||
@@ -454,15 +646,13 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
uint8_t mbc_block_bits[12*8*3];
|
||||
memset (mbc_block_bits, 0, sizeof(mbc_block_bits));
|
||||
//shift continuation blocks and last block into seperate array for crc check
|
||||
for (i = 0; i < 12*8*3; i++)
|
||||
for (i = 0; i < 12*8*3; i++) //only doing 3 blocks (4 minus the header)
|
||||
{
|
||||
mbc_block_bits[i] = dmr_pdu_sf_bits[i+96]; //skip mbc header
|
||||
}
|
||||
|
||||
CRCComputed = ComputeCrcCCITT16d (mbc_block_bits, ((blocks+0)*96)-16 );
|
||||
|
||||
// fprintf (stderr, " \n CRC EXT %X CRC CMP %X", CRCExtracted, CRCComputed);
|
||||
|
||||
if (CRCComputed == CRCExtracted) mbc_crc_good[1] = 1;
|
||||
|
||||
CRCCorrect = 0;
|
||||
@@ -477,60 +667,51 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf(stderr, " (CRC ERR)");
|
||||
fprintf (stderr, "\n Slot %d - Multi Block Control Message CRC16 ERR", slot+1);
|
||||
|
||||
//debug print
|
||||
fprintf (stderr, " %X - %X", CRCExtracted, CRCComputed);
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
//cspdu will only act on any fid/opcodes if good CRC to prevent falsing on control signalling
|
||||
dmr_cspdu (opts, state, dmr_pdu_sf_bits, state->dmr_pdu_sf[slot], CRCCorrect, IrrecoverableErrors);
|
||||
if (!is_udt && !pf) dmr_cspdu (opts, state, dmr_pdu_sf_bits, state->dmr_pdu_sf[slot], CRCCorrect, IrrecoverableErrors);
|
||||
|
||||
}
|
||||
}
|
||||
//will need to reassemble partially before sending to dmr_pdu or dmr_cspdu for handling -- needs testing
|
||||
if (is_udt && !pf) dmr_udt_cspdu_converter (opts, state, state->dmr_pdu_sf[slot], dmr_pdu_sf_bits, CRCCorrect, IrrecoverableErrors);
|
||||
|
||||
//Full Super Frame Type 1 - Debug Output
|
||||
if (opts->payload == 1 && type == 1 && state->data_block_counter[slot] == state->data_header_blocks[slot])
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, "\n Slot %d - Multi Block PDU Message\n ", slot+1);
|
||||
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||
//Full Super Frame MBC/UDT - Debug Output
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "\n ");
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, "\n Slot %d - Multi Block Control Message\n ", slot+1);
|
||||
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||
{
|
||||
fprintf (stderr, "\n ");
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "%s", KRED);
|
||||
if (mbc_crc_good[0] == 0) fprintf (stderr, "MBC/UDT Header CRC ERR ");
|
||||
if (mbc_crc_good[1] == 0) fprintf (stderr, "MBC/UDT Blocks CRC ERR ");
|
||||
if (pf) fprintf (stderr, "MBC/UDT Header/Blocks Protected ");
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
//debug print
|
||||
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
||||
} //end last block flag on MBC
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
} //end type 2 (MBC Header and Continuation)
|
||||
|
||||
//Full Super Frame MBC - Debug Output
|
||||
if (opts->payload == 1 && type == 2 && lb == 1)
|
||||
//leave this seperate so we can reset/zero stuff in case the data header isn't valid, etc
|
||||
//switched to an if-elseif-else so we could get the block counter increment on the end
|
||||
//without having it in an akward position
|
||||
|
||||
//if the end of normal data header and blocks
|
||||
if (type == 1 && state->data_block_counter[slot] == state->data_header_blocks[slot])
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, "\n Slot %d - Multi Block Control Message\n ", slot+1);
|
||||
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||
{
|
||||
fprintf (stderr, "\n ");
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "%s", KRED);
|
||||
if (mbc_crc_good[0] == 0) fprintf (stderr, "MBC Header CRC ERR ");
|
||||
if (mbc_crc_good[1] == 0) fprintf (stderr, "MBC Blocks CRC ERR ");
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
state->data_block_counter[slot]++; //increment block counter
|
||||
|
||||
if (type == 2 && lb == 1)
|
||||
{
|
||||
state->data_block_counter[slot] = 0; //reset the block counter (MBCs)
|
||||
|
||||
//clear out unified pdu 'superframe' slot
|
||||
for (short int i = 0; i < 288; i++)
|
||||
@@ -538,23 +719,65 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
|
||||
state->dmr_pdu_sf[slot][i] = 0;
|
||||
}
|
||||
|
||||
state->data_block_crc_valid[slot][0] = 0; //Zero Out MBC Header Block CRC Valid
|
||||
//Zero Out MBC Header Block CRC Valid
|
||||
state->data_block_crc_valid[slot][0] = 0;
|
||||
//reset the block counter (data blocks)
|
||||
state->data_block_counter[slot] = 1;
|
||||
//reset data header format storage
|
||||
state->data_header_format[slot] = 7;
|
||||
//flag off data header validity
|
||||
state->data_header_valid[slot] = 0;
|
||||
//flag off conf data flag
|
||||
state->data_conf_data[slot] = 0;
|
||||
|
||||
}
|
||||
|
||||
//else if the end of MBC Header and Blocks
|
||||
else if (type == 2 && lb == 1)
|
||||
{
|
||||
|
||||
//clear out unified pdu 'superframe' slot
|
||||
for (short int i = 0; i < 288; i++)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i] = 0;
|
||||
}
|
||||
|
||||
//Zero Out MBC Header Block CRC Valid
|
||||
state->data_block_crc_valid[slot][0] = 0;
|
||||
//reset the block counter (data blocks)
|
||||
state->data_block_counter[slot] = 1;
|
||||
//reset data header format storage
|
||||
state->data_header_format[slot] = 7;
|
||||
//flag off data header validity
|
||||
state->data_header_valid[slot] = 0;
|
||||
//flag off conf data flag
|
||||
state->data_conf_data[slot] = 0;
|
||||
|
||||
}
|
||||
|
||||
//else increment block counter after sorting/shuffling blocks
|
||||
else state->data_block_counter[slot]++;
|
||||
|
||||
}
|
||||
|
||||
//failsafe to clear old data header, block info, cach, in case of tact/emb/slottype failures and we
|
||||
//can no longer verify accurate data block reporting
|
||||
//failsafe to clear old data header, block info, cach, in case of tact/emb/slottype failures
|
||||
//or tuning away and we can no longer verify accurate data block reporting
|
||||
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
memset (state->data_p_head, 0, sizeof(state->data_p_head));
|
||||
memset (state->data_conf_data, 0, sizeof(state->data_conf_data));
|
||||
memset (state->dmr_pdu_sf, 0, sizeof(state->dmr_pdu_sf));
|
||||
memset (state->data_block_counter, 0, sizeof(state->data_block_counter));
|
||||
memset (state->data_header_blocks, 0, sizeof(state->data_header_blocks));
|
||||
memset (state->data_block_counter, 1, sizeof(state->data_block_counter));
|
||||
memset (state->data_header_blocks, 1, sizeof(state->data_header_blocks));
|
||||
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
|
||||
memset (state->dmr_lrrp_source, 0, sizeof(state->dmr_lrrp_source));
|
||||
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
||||
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||
memset (state->data_header_valid, 0, sizeof(state->data_header_valid));
|
||||
memset (state->data_header_format, 7, sizeof(state->data_header_format));
|
||||
//reset some strings
|
||||
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||
sprintf (state->dmr_lrrp_gps[0], "%s", "");
|
||||
sprintf (state->dmr_lrrp_gps[1], "%s", "");
|
||||
}
|
||||
49
src/dmr_bs.c
49
src/dmr_bs.c
@@ -114,13 +114,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
//disabled for dmr_cach testing
|
||||
tact_okay = 0;
|
||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||
if (tact_okay != 1)
|
||||
{
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
if (tact_okay != 1) goto END;
|
||||
|
||||
|
||||
internalslot = state->currentslot = tact_bits[1];
|
||||
|
||||
@@ -384,7 +379,7 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
|
||||
cach_err = dmr_cach (opts, state, cachdata);
|
||||
fprintf (stderr, "\n");
|
||||
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||
|
||||
// run alg refresh after vc6 ambe processing
|
||||
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
|
||||
@@ -478,18 +473,16 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
fprintf (stderr, "| VOICE CACH/EMB ERR");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
fprintf (stderr, "\n");
|
||||
//run LFSR if either slot had an active MI in it.
|
||||
//run refresh if either slot had an active MI in it.
|
||||
if (state->payload_algid >= 0x21)
|
||||
{
|
||||
state->currentslot = 0;
|
||||
LFSR(state);
|
||||
fprintf (stderr, "\n");
|
||||
state->currentslot = 0;
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
if (state->payload_algidR >= 0x21)
|
||||
{
|
||||
state->currentslot = 1;
|
||||
LFSR(state);
|
||||
fprintf (stderr, "\n");
|
||||
state->currentslot = 1;
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
|
||||
//failsafe to reset all data header and blocks when bad tact or emb
|
||||
@@ -572,13 +565,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//decode and correct tact and compare
|
||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||
if (tact_okay != 1)
|
||||
{
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
if (tact_okay != 1) goto END;
|
||||
|
||||
internalslot = state->currentslot = tact_bits[1];
|
||||
|
||||
@@ -636,11 +623,8 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
||||
{
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
sync_okay = 0;
|
||||
goto END;
|
||||
}
|
||||
sync_okay = 0;
|
||||
goto END;
|
||||
}
|
||||
|
||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||
@@ -740,7 +724,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
|
||||
|
||||
cach_err = dmr_cach (opts, state, cachdata);
|
||||
fprintf (stderr, "\n");
|
||||
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||
|
||||
//update voice sync time for trunking purposes (particularly Con+)
|
||||
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
|
||||
@@ -755,18 +739,17 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "| VOICE CACH/SYNC ERR");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
//run LFSR if either slot had an active MI in it.
|
||||
fprintf (stderr, "\n");
|
||||
//run refresh if either slot had an active MI in it.
|
||||
if (state->payload_algid >= 0x21)
|
||||
{
|
||||
state->currentslot = 0;
|
||||
LFSR(state);
|
||||
fprintf (stderr, "\n");
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
if (state->payload_algidR >= 0x21)
|
||||
{
|
||||
state->currentslot = 1;
|
||||
LFSR(state);
|
||||
fprintf (stderr, "\n");
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
|
||||
//failsafe to reset all data header and blocks when bad tact or emb
|
||||
|
||||
736
src/dmr_csbk.c
736
src/dmr_csbk.c
@@ -22,31 +22,33 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
int csbk_pf = 0;
|
||||
int csbk_o = 0;
|
||||
int csbk_fid = 0;
|
||||
uint64_t csbk_data = 0;
|
||||
int csbk = 0;
|
||||
|
||||
long int ccfreq = 0;
|
||||
|
||||
if(IrrecoverableErrors == 0 && CRCCorrect == 1)
|
||||
csbk_lb = ( (cs_pdu[0] & 0x80) >> 7 );
|
||||
csbk_pf = ( (cs_pdu[0] & 0x40) >> 6 );
|
||||
csbk_o = cs_pdu[0] & 0x3F;
|
||||
csbk_fid = cs_pdu[1]; //feature set id
|
||||
|
||||
//check, regardless of CRC err
|
||||
if (IrrecoverableErrors == 0)
|
||||
{
|
||||
|
||||
csbk_lb = ( (cs_pdu[0] & 0x80) >> 7 );
|
||||
csbk_pf = ( (cs_pdu[0] & 0x40) >> 6 );
|
||||
csbk_o = cs_pdu[0] & 0x3F;
|
||||
csbk_fid = cs_pdu[1]; //feature set id
|
||||
csbk_pf = ( (cs_pdu[1] & 0x80) >> 7);
|
||||
csbk = ((cs_pdu[0] & 0x3F) << 8) | cs_pdu[1]; //opcode and fid combo set
|
||||
|
||||
//update time to prevent random 'Control Channel Signal Lost' hopping
|
||||
//in the middle of voice call on current Control Channel (con+ and t3)
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
|
||||
//Hytera XPT CSBK Check -- if bits 0 and 1 are used as lcss, gi, ts, then this bit may be set on
|
||||
if ( csbk_fid == 0x68 && (csbk_o == 0x0A || csbk_o == 0x0B) ) csbk_pf = 0;
|
||||
if (csbk_pf == 1) //check the protect flag, don't run if set
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "\n Protected Control Signalling Block(s)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
if(IrrecoverableErrors == 0 && CRCCorrect == 1)
|
||||
{
|
||||
|
||||
//update time to prevent random 'Control Channel Signal Lost' hopping
|
||||
//in the middle of voice call on current Control Channel (con+ and t3)
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
|
||||
if (csbk_pf == 0) //okay to run
|
||||
{
|
||||
@@ -209,6 +211,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
SetFreq(opts->rigctl_sockfd, freq);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||
}
|
||||
|
||||
//rtl_udp
|
||||
@@ -217,6 +220,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
rtl_udp_tune (opts, state, freq);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||
opts->p25_is_tuned = 1;
|
||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||
}
|
||||
|
||||
}
|
||||
@@ -387,9 +391,12 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
|
||||
if (gi) fprintf (stderr, " Group");
|
||||
if (!gi) fprintf (stderr, " Private");
|
||||
|
||||
if (p_kind == 0) fprintf (stderr, " Disable Target PTT (DIS_PTT)");
|
||||
if (p_kind == 1) fprintf (stderr, " Enable Target PTT (EN_PTT)");
|
||||
if (p_kind == 2) fprintf (stderr, " Private Call (ILLEGALLY_PARKED)");
|
||||
if (p_kind == 2) fprintf (stderr, " Call (ILLEGALLY_PARKED)");
|
||||
if (p_kind == 3) fprintf (stderr, " Enable Target MS PTT (EN_PTT_ONE_MS)");
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
@@ -608,13 +615,44 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
|
||||
uint8_t target_hash[24];
|
||||
uint8_t tg_hash = 0;
|
||||
|
||||
if (gi == 0) fprintf (stderr, "Individual ");
|
||||
else fprintf (stderr, "Group ");
|
||||
|
||||
if (content == 0) fprintf (stderr, "CSBK - ");
|
||||
else fprintf (stderr, "Data - ");
|
||||
|
||||
fprintf (stderr, "Target [%d] - Source [%d] ", target, source);
|
||||
// check to see if this is XPT
|
||||
if (strcmp (state->dmr_branding_sub, "XPT ") == 0)
|
||||
{
|
||||
//get 16-bit truncated target and source ids
|
||||
target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16); //40, or 32?
|
||||
source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[64], 16); //56, or 64?
|
||||
|
||||
//check to see if this is indiv data (private) then we will need to report the hash value as well
|
||||
if (gi == 0)
|
||||
{
|
||||
//the crc8 hash is the value represented in the Hytera XPT Site Status CSBK when dealing with indiv data
|
||||
for (int i = 0; i < 16; i++) target_hash[i] = cs_pdu_bits[40+i];
|
||||
tg_hash = crc8 (target_hash, 16);
|
||||
fprintf (stderr, "Source: %d - Target: %d - Hash: %d ", source, target, tg_hash);
|
||||
}
|
||||
else fprintf (stderr, "Source: %d - Target: %d ", source, target);
|
||||
|
||||
}
|
||||
// check to see if this is Cap+
|
||||
else if (strcmp (state->dmr_branding_sub, "Cap+ ") == 0)
|
||||
{
|
||||
//truncate tg on group? or just on private/individual data?
|
||||
if (gi == 0) target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16);
|
||||
source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[64], 16);
|
||||
int rest = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[60], 4);
|
||||
fprintf (stderr, "Source: %d - Target: %d - Rest Channel: %d", source, target, rest);
|
||||
}
|
||||
else fprintf (stderr, "Source: %d - Target: %d ", source, target);
|
||||
|
||||
}
|
||||
//end tier 2 csbks
|
||||
|
||||
@@ -624,7 +662,56 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
//experimental, but seems to be working well
|
||||
if (csbk_fid == 0x10)
|
||||
{
|
||||
//Cap+ Channel Status PDU
|
||||
|
||||
//Cap+ Something
|
||||
if (csbk_o == 0x3A)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
|
||||
fprintf (stderr, " Capacity Plus CSBK 0x3A ");
|
||||
|
||||
// 01:15:08 Sync: +DMR slot1 [slot2] | Color Code=03 | CSBK
|
||||
// Capacity Plus Channel Status - FL: 3 TS: 1 RS: 0 - Rest Channel 1 - Single Block
|
||||
// Ch1: Rest Ch2: Idle Ch3: Idle Ch4: Idle
|
||||
// Ch5: Idle Ch6: Idle Ch7: Idle Ch8: Idle
|
||||
// DMR PDU Payload [BE][10][E1][00][00][00][00][00][00][00][4E][15]
|
||||
// 01:15:08 Sync: +DMR [slot1] slot2 | Color Code=03 | CSBK
|
||||
|
||||
//FL, TS, and Rest Channel seem to be in same location as the Channel Status CSBK
|
||||
//other values are currently unknown
|
||||
// DMR PDU Payload [BA][10][C1][3B][61][11][51][00][00][00][3D][D6]
|
||||
// 01:15:08 Sync: +DMR slot1 [slot2] | Color Code=03 | CSBK
|
||||
// DMR PDU Payload [BA][10][E1][3B][61][11][51][00][00][00][46][BE]
|
||||
|
||||
}
|
||||
|
||||
//Cap+ Adjacent Sites
|
||||
if (csbk_o == 0x3B)
|
||||
{
|
||||
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
|
||||
fprintf (stderr, " Capacity Plus Adjacent Sites\n ");
|
||||
|
||||
uint8_t nl[6]; //adjacet site numerical value
|
||||
uint8_t nr[6]; //adjacent site current rest channel
|
||||
memset (nl, 0, sizeof (nl));
|
||||
memset (nr, 0, sizeof (nr));
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
nl[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[32+(i*8)], 4);
|
||||
nr[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[36+(i*8)], 4);
|
||||
if (nl[i]) fprintf (stderr, "Site: %d Rest: %d; ", nl[i], nr[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Cap+ Channel Status -- Expanded for up to 16 LSNs and private voice/data calls and dual slot csbk_bit storage
|
||||
if (csbk_o == 0x3E)
|
||||
{
|
||||
|
||||
@@ -634,35 +721,52 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
|
||||
uint8_t fl = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 2);
|
||||
uint8_t ts = cs_pdu_bits[18]; //timeslot this PDU occurs in
|
||||
uint8_t res = cs_pdu_bits[19]; //unknown, always seems to be 0
|
||||
uint8_t rest_channel = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[20], 4);
|
||||
uint8_t group_tally = 0; //set this to the number of active group channels tallied
|
||||
|
||||
uint8_t ch[8]; //one bit per channel
|
||||
uint8_t pch[8]; //private or data call channel bits
|
||||
uint8_t tg = 0;
|
||||
int i, j, k;
|
||||
|
||||
uint8_t res = cs_pdu_bits[19]; //unknown or unused bit value
|
||||
uint8_t rest_channel = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[20], 4);
|
||||
uint8_t group_tally = 0; //set this to the number of active channels tallied
|
||||
uint8_t bank_one = 0;
|
||||
uint8_t bank_two = 0;
|
||||
uint8_t b2_start = 0;
|
||||
uint8_t block_num = state->cap_plus_block_num;
|
||||
uint8_t pdflag = 0;
|
||||
uint8_t pdflag2 = 0;
|
||||
uint16_t private_target = 0;
|
||||
uint8_t pd_b2 = 0;
|
||||
uint8_t start = 8;
|
||||
uint8_t end = 8;
|
||||
uint8_t fl_bytes = 0;
|
||||
uint8_t ch[24]; //one bit per channel
|
||||
uint8_t pch[24]; //private or data call channel bits
|
||||
uint16_t tg = 0;
|
||||
int i, j, k, x;
|
||||
//tg and channel info for trunking purposes
|
||||
uint8_t t_tg[9];
|
||||
memset (t_tg, 0, sizeof(t_tg));
|
||||
uint16_t t_tg[24];
|
||||
|
||||
//initialize group ch and private/data channel bit arrays
|
||||
for (i = 0; i < 8; i++)
|
||||
//sanity check
|
||||
if (block_num > 6)
|
||||
{
|
||||
ch[i] = 0;
|
||||
pch[i] = 0;
|
||||
state->cap_plus_block_num = 6;
|
||||
block_num = 6;
|
||||
}
|
||||
|
||||
//init some arrays
|
||||
memset (t_tg, 0, sizeof(t_tg));
|
||||
memset (ch, 0, sizeof(ch));
|
||||
memset (pch, 0, sizeof(pch));
|
||||
|
||||
//treating FL as a form of LCSS
|
||||
if (fl != 1) //initial or single block (fl2 or fl3)
|
||||
if (fl == 2 || fl == 3) //initial or single block (fl2 or fl3)
|
||||
{
|
||||
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||
for (i = 0; i < 10*8; i++) state->cap_plus_csbk_bits[i] = cs_pdu_bits[i];
|
||||
//NOTE: this has been changed to store per slot
|
||||
memset (state->cap_plus_csbk_bits[ts], 0, sizeof(state->cap_plus_csbk_bits[ts]));
|
||||
for (i = 0; i < 10*8; i++) state->cap_plus_csbk_bits[ts][i] = cs_pdu_bits[i];
|
||||
state->cap_plus_block_num = 0;
|
||||
}
|
||||
else //appended block (fl1)
|
||||
else //appended block (fl 0) or final block (fl 1)
|
||||
{
|
||||
for (i = 0; i < 7*8; i++) state->cap_plus_csbk_bits[i+80] = cs_pdu_bits[i+24];
|
||||
for (i = 0; i < 7*8; i++) state->cap_plus_csbk_bits[ts][i+80+(7*8*block_num)] = cs_pdu_bits[i+24];
|
||||
block_num++;
|
||||
state->cap_plus_block_num++;
|
||||
}
|
||||
|
||||
if (rest_channel != state->dmr_rest_channel)
|
||||
@@ -679,95 +783,172 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
}
|
||||
|
||||
fprintf (stderr, " Capacity Plus Channel Status - FL: %d TS: %d RS: %d - Rest Channel %d", fl, ts, res, rest_channel);
|
||||
if (fl == 1) fprintf (stderr, " - Appended Block");
|
||||
if (fl == 0) fprintf (stderr, " - Appended Block"); //have not yet observed a system use this fl value
|
||||
if (fl == 1) fprintf (stderr, " - Final Block");
|
||||
if (fl == 2) fprintf (stderr, " - Initial Block");
|
||||
if (fl == 3) fprintf (stderr, " - Single Block");
|
||||
|
||||
//look at each group channel bit -- run this PRIOR to checking private or data calls
|
||||
//or else we will overwrite the ch bits we set below for private calls with zeroes
|
||||
//these also seem to indicate data calls -- can't find a distinction of which is which
|
||||
bank_one = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][24], 8);
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
ch[i] = state->cap_plus_csbk_bits[i+24];
|
||||
if (ch[i] == 1) group_tally++; //figure out where to start looking in the byte stream for the 0x80 flag
|
||||
}
|
||||
ch[i] = state->cap_plus_csbk_bits[ts][i+24];
|
||||
if (ch[i] == 1) group_tally++; //figure out where to start looking in the byte stream for the 0next bank of calls
|
||||
}
|
||||
|
||||
//check for private/data calls
|
||||
uint8_t pdflag = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[40+(group_tally*8)], 8); //seems to be 0x80 (last block flag?) on samples I have
|
||||
uint16_t private_target = 0;
|
||||
|
||||
//first, check to see if we have a completed PDU
|
||||
if (fl != 2)
|
||||
//Expanded to cover larger systems up to 16 slots.
|
||||
bank_two = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][32+(group_tally*8)], 8); //32
|
||||
b2_start = group_tally;
|
||||
if (bank_two)
|
||||
{
|
||||
//then check to see if this byte has a value, should be 0x80
|
||||
//this bytes location seems to shift depending on level of activity
|
||||
if (pdflag != 0) //now looking to see if any appended data was added
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
ch[i+8] = state->cap_plus_csbk_bits[ts][i+32+(b2_start*8)];
|
||||
if (ch[i+8] == 1) group_tally++;
|
||||
}
|
||||
}
|
||||
|
||||
//check flag for appended private and/or data calls
|
||||
pdflag = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][40+(group_tally*8)], 8);
|
||||
|
||||
//check for private activity on LSNs 1-8
|
||||
if (fl == 1 || fl == 3)
|
||||
{
|
||||
if (pdflag == 0x80)
|
||||
{
|
||||
k = 0; //= 0
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " F%X Private or Data Call", pdflag);
|
||||
fprintf (stderr, " F%X Private or Data Call - ", pdflag);
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
pch[i] = state->cap_plus_csbk_bits[i+48+(group_tally*8)];
|
||||
pch[i] = state->cap_plus_csbk_bits[ts][i+48+(group_tally*8)];
|
||||
if (pch[i] == 1)
|
||||
{
|
||||
fprintf (stderr, " Ch%d:", i+1);
|
||||
private_target = (uint16_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[56+(k*16)+(group_tally*8)], 16);
|
||||
fprintf (stderr, " LSN %02d:", i+1);
|
||||
private_target = (uint16_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][56+(k*16)+(group_tally*8)], 16);
|
||||
fprintf (stderr, " TGT %d;", private_target);
|
||||
if (opts->trunk_tune_private_calls == 1) t_tg[i] = private_target; //set here for tuning allow/block
|
||||
// if (opts->trunk_tune_data_calls == 1) t_tg[i] = private_target; //moved to below section
|
||||
k++;
|
||||
if (bank_one == 0) bank_one = 1; //flag on bank one if not already for private call listings
|
||||
}
|
||||
}
|
||||
//save for starting point of the next private call bank
|
||||
pd_b2 = k;
|
||||
}
|
||||
}
|
||||
//end private/data call check on LSNs 1-8
|
||||
|
||||
//check flag for appended private and/or data calls -- flag two still needs work or testing, double checking, etc, disable if issues arise
|
||||
pdflag2 = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][48+(group_tally*8)+(pd_b2*16)], 8);
|
||||
|
||||
//check for private activity on LSNs 9-16
|
||||
if (fl == 1 || fl == 3)
|
||||
{
|
||||
//then check to see if this byte has a value, should be 0x80, could be other?
|
||||
//this bytes location shifts depending on level of activity -- see banks above
|
||||
if (pdflag2 == 0x80) //now looking to see if any appended data was added
|
||||
{
|
||||
k = 0;
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " F%X Private or Data Call - ", pdflag2);
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
pch[i+8] = state->cap_plus_csbk_bits[ts][i+56+(group_tally*8)+(pd_b2*16)]; //48
|
||||
if (pch[i+8] == 1)
|
||||
{
|
||||
fprintf (stderr, " LSN %02d:", i+1);
|
||||
private_target = (uint16_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][64+(k*16)+(group_tally*8)+(pd_b2*16)], 16); //64
|
||||
fprintf (stderr, " TGT %d;", private_target);
|
||||
// if (opts->trunk_tune_data_calls == 1) t_tg[i+8] = private_target; //moved to below section
|
||||
k++;
|
||||
if (bank_two == 0) bank_two = 1; //flag on bank one if not already for private call listings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//end private/data call check
|
||||
//end private/data call check on LSNs 9-16
|
||||
|
||||
if (fl != 2)
|
||||
if (fl == 1 || fl == 3)
|
||||
{
|
||||
fprintf (stderr, "\n ");
|
||||
|
||||
k = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
x = 0;
|
||||
|
||||
//start position;
|
||||
start = 8;
|
||||
if (bank_one) start = 0;
|
||||
else if (rest_channel < 8) start = 0;
|
||||
|
||||
//end position;
|
||||
end = 8;
|
||||
if (bank_two) end = 16;
|
||||
else if (rest_channel > 8) end = 16;
|
||||
|
||||
//start parsing info and listing active LSNs
|
||||
for (i = start; i < end; i++)
|
||||
{
|
||||
fprintf (stderr, "Ch%d: ", i+1);
|
||||
//skip an additional k value per bank
|
||||
if (i == 8) k++;
|
||||
|
||||
if (i == 4 || i == 12)
|
||||
fprintf (stderr, "\n ");
|
||||
|
||||
if (i == 8 && start == 0 && end == 16)
|
||||
fprintf (stderr, "\n ");
|
||||
|
||||
fprintf (stderr, "LSN %02d: ", i+1);
|
||||
if (ch[i] == 1) //group voice channels
|
||||
{
|
||||
tg = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[k*8+32], 8);
|
||||
if (tg != 0) fprintf (stderr, " %03d ", tg);
|
||||
else fprintf (stderr, "Actv ");
|
||||
//add values to trunking tg/channel potentials
|
||||
t_tg[i] = tg;
|
||||
if (tg != 0) k++;
|
||||
|
||||
tg = (uint16_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][(k*8)+32], 8);
|
||||
if (tg != 0) fprintf (stderr, "%5d; ", tg);
|
||||
else fprintf (stderr, "Group; ");
|
||||
//flag as available for tuning if group calls enabled
|
||||
if (opts->trunk_tune_group_calls == 1) t_tg[i] = tg;
|
||||
if (tg != 0) k++;
|
||||
}
|
||||
else if (pch[i] == 1) //private or data channels
|
||||
{
|
||||
fprintf (stderr, "P||D ");
|
||||
//flag as available for tuning if enabled
|
||||
if (opts->trunk_tune_private_calls == 1) ch[i] = 1;
|
||||
tg = (uint16_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][(group_tally*8)+(x*16)+56], 16);
|
||||
if (tg != 0) fprintf (stderr, "%5d; ", tg);
|
||||
else fprintf (stderr, " P||D; ");
|
||||
//flag as available for tuning if data calls enabled
|
||||
if (opts->trunk_tune_data_calls == 1) t_tg[i] = tg; //ch[i] = 1;
|
||||
if (tg != 0) x++;
|
||||
}
|
||||
else if (i+1 == rest_channel) fprintf (stderr, "Rest ");
|
||||
else fprintf (stderr, "Idle ");
|
||||
else if (i+1 == rest_channel) fprintf (stderr, " Rest; ");
|
||||
else fprintf (stderr, " Idle; ");
|
||||
|
||||
if (i == 3) fprintf (stderr, "\n ");
|
||||
}
|
||||
|
||||
//debug ncurses terminal display -- LSNs 1-8
|
||||
// sprintf (state->dmr_lrrp_gps[0], "CAP LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d;", 1, t_tg[0], 2, t_tg[1], 3, t_tg[2], 4, t_tg[3]);
|
||||
// sprintf (state->dmr_lrrp_gps[1], "CAP LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d;", 5, t_tg[4], 6, t_tg[5], 7, t_tg[6], 8, t_tg[7]);
|
||||
|
||||
//debug ncurses terminal display -- LSNs 9-16
|
||||
// sprintf (state->dmr_lrrp_gps[0], "CAP LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d;", 9, t_tg[8], 10, t_tg[9], 11, t_tg[10], 12, t_tg[11]);
|
||||
// sprintf (state->dmr_lrrp_gps[1], "CAP LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d;", 13, t_tg[12], 14, t_tg[13], 15, t_tg[14], 16, t_tg[15]);
|
||||
|
||||
|
||||
state->dmr_mfid = 0x10;
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
||||
|
||||
//nullify any previous TIII data (bugfix for bad assignments or system type switching)
|
||||
//nullify any previous site_parm data
|
||||
sprintf(state->dmr_site_parms, "%s", "");
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPCAP;
|
||||
//Skip tuning group calls if group calls are disabled -- moved to individual switches in the parsing phase
|
||||
// if (opts->trunk_tune_group_calls == 0) goto SKIPCAP;
|
||||
|
||||
//don't tune if vc on the current channel
|
||||
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||
{
|
||||
for (j = 0; j < 8; j++) //go through the channels stored looking for active ones to tune to
|
||||
for (j = start; j < end; j++) //go through the channels stored looking for active ones to tune to
|
||||
{
|
||||
char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
@@ -787,6 +968,9 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
//without priority, this will tune the first one it finds (if group isn't blocked)
|
||||
if (t_tg[j] != 0 && state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0))
|
||||
{
|
||||
//debug print for tuning verification
|
||||
fprintf (stderr, "\n LSN/TG to tune to: %d - %d", j+1, t_tg[j]);
|
||||
|
||||
if (state->trunk_chan_map[j+1] != 0) //if we have a valid frequency
|
||||
{
|
||||
//RIGCTL
|
||||
@@ -796,6 +980,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[j+1]);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+1];
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||
j = 11; //break loop
|
||||
}
|
||||
|
||||
@@ -805,6 +990,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
rtl_udp_tune (opts, state, state->trunk_chan_map[j+1]);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+1];
|
||||
opts->p25_is_tuned = 1;
|
||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||
j = 11; //break loop
|
||||
}
|
||||
|
||||
@@ -819,169 +1005,38 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
if (fl == 1 && opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s\n", KYEL);
|
||||
fprintf (stderr, " CAP+ Multi Block PDU ");
|
||||
uint8_t fl_bytes = 0;
|
||||
for (i = 0; i < 17; i++)
|
||||
fprintf (stderr, " CAP+ Multi Block PDU \n ");
|
||||
fl_bytes = 0;
|
||||
for (i = 0; i < (10+(block_num*7)); i++)
|
||||
{
|
||||
fl_bytes = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[i*8], 8);
|
||||
fl_bytes = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[ts][i*8], 8);
|
||||
fprintf (stderr, "[%02X]", fl_bytes);
|
||||
if (i == 17 || i == 35) fprintf (stderr, "\n ");
|
||||
}
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||
memset (state->cap_plus_csbk_bits[ts], 0, sizeof(state->cap_plus_csbk_bits[ts]));
|
||||
state->cap_plus_block_num = 0;
|
||||
|
||||
} //fl != 2
|
||||
} //if (fl == 1 || fl == 3)
|
||||
} //opcode == 0x3E
|
||||
|
||||
} //fid == 0x10
|
||||
|
||||
//Capacity+ Section -- fallback if issues arise
|
||||
// if (csbk_fid == 0x10)
|
||||
// {
|
||||
// //not quite sure how these tuning rules will go over
|
||||
// //if they don't work so well, may just fall back to
|
||||
// //a 'follow rest channel on no sync' only approach
|
||||
// if (csbk_o == 0x3E)
|
||||
// {
|
||||
|
||||
// //initial line break
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, "%s", KYEL);
|
||||
|
||||
// uint8_t fl = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 2);
|
||||
// uint8_t ts = cs_pdu_bits[18]; //accurate?
|
||||
// uint8_t res = cs_pdu_bits[19]; //unknown??
|
||||
// uint8_t rest_channel = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[20], 4);
|
||||
|
||||
// uint8_t ch[8]; //one bit per channel
|
||||
// uint8_t tg = 0;
|
||||
// uint32_t tghex = 0; //combined all tgs for debug
|
||||
// int i, j, k;
|
||||
|
||||
// //tg and channel info for trunking purposes
|
||||
// uint8_t t_tg[9];
|
||||
// memset (t_tg, 0, sizeof(t_tg));
|
||||
|
||||
// k = 0;
|
||||
// if (rest_channel != state->dmr_rest_channel)
|
||||
// {
|
||||
// state->dmr_rest_channel = rest_channel;
|
||||
// }
|
||||
// for (int i = 0; i < 8; i++)
|
||||
// {
|
||||
// ch[i] = cs_pdu_bits[i+24];
|
||||
// }
|
||||
|
||||
// //assign to cc freq to follow during no sync
|
||||
// if (state->trunk_chan_map[rest_channel] != 0)
|
||||
// {
|
||||
// state->p25_cc_freq = state->trunk_chan_map[rest_channel];
|
||||
// //set to always tuned
|
||||
// opts->p25_is_tuned = 1;
|
||||
// }
|
||||
|
||||
// fprintf (stderr, " Capacity Plus Channel Status - FL: %d TS: %d RS: %d - Rest Channel %d", fl, ts, res, rest_channel);
|
||||
|
||||
// fprintf (stderr, "\n ");
|
||||
// for (i = 0; i < 8; i++)
|
||||
// {
|
||||
// fprintf (stderr, "Ch%d: ", i+1);
|
||||
// if (ch[i] != 0)
|
||||
// {
|
||||
// tg = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[k*8+32], 8);
|
||||
// if (tg != 0) fprintf (stderr, " %03d ", tg);
|
||||
// else fprintf (stderr, "Priv "); //observed 000s for TG value seem to appear during a Cap+ Private TXI call
|
||||
// //add values to trunking tg/channel potentials
|
||||
// t_tg[i] = tg;
|
||||
// k++;
|
||||
|
||||
// }
|
||||
// else if (i+1 == rest_channel) fprintf (stderr, "Rest ");
|
||||
// else fprintf (stderr, "Idle ");
|
||||
|
||||
// if (i == 3) fprintf (stderr, "\n ");
|
||||
// }
|
||||
// tghex = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
// //fprintf (stderr, "\n TG Hex = 0x%06X", tghex);
|
||||
// state->dmr_mfid = 0x10;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
||||
|
||||
// //nullify any previous TIII data (bugfix for bad assignments or system type switching)
|
||||
// sprintf(state->dmr_site_parms, "%s", "");
|
||||
|
||||
// fprintf (stderr, "%s", KNRM);
|
||||
|
||||
// //Skip tuning group calls if group calls are disabled
|
||||
// if (opts->trunk_tune_group_calls == 0) goto SKIPCAP;
|
||||
|
||||
// //don't tune if currently a vc on the current channel
|
||||
// if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||
// {
|
||||
// for (j = 0; j < 8; j++) //go through the channels stored looking for active ones to tune to
|
||||
// {
|
||||
// char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
// //if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
// //comparison below will look for an 'A' to write to mode if it is allowed
|
||||
// if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
// for (int i = 0; i < state->group_tally; i++)
|
||||
// {
|
||||
// if (state->group_array[i].groupNumber == t_tg[j])
|
||||
// {
|
||||
// fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
// strcpy (mode, state->group_array[i].groupMode);
|
||||
// }
|
||||
// }
|
||||
|
||||
// //no more 0 reporting, that was some bad code that caused that issue
|
||||
// //without priority, this will tune the first one it finds (if group isn't blocked)
|
||||
// if (t_tg[j] != 0 && state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0))
|
||||
// {
|
||||
// if (state->trunk_chan_map[j+1] != 0) //if we have a valid frequency
|
||||
// {
|
||||
// //RIGCTL
|
||||
// if (opts->use_rigctl == 1)
|
||||
// {
|
||||
// if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
// SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[j+1]);
|
||||
// state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+1];
|
||||
// opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
// j = 11; //break loop
|
||||
// }
|
||||
|
||||
// //rtl_udp
|
||||
// else if (opts->audio_in_type == 3)
|
||||
// {
|
||||
// rtl_udp_tune (opts, state, state->trunk_chan_map[j+1]);
|
||||
// state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+1];
|
||||
// opts->p25_is_tuned = 1;
|
||||
// j = 11; //break loop
|
||||
// }
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
// SKIPCAP: ; //do nothing
|
||||
// }
|
||||
|
||||
//Connect+ Section
|
||||
if (csbk_fid == 0x06)
|
||||
{
|
||||
|
||||
if (csbk_o == 0x01)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
uint8_t nb1 = cs_pdu[2] & 0x3F;
|
||||
uint8_t nb2 = cs_pdu[3] & 0x3F;
|
||||
uint8_t nb3 = cs_pdu[4] & 0x3F;
|
||||
uint8_t nb4 = cs_pdu[5] & 0x3F;
|
||||
uint8_t nb5 = cs_pdu[6] & 0x3F;
|
||||
uint8_t nb5 = cs_pdu[6] & 0x3F;
|
||||
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Connect Plus Neighbors\n");
|
||||
fprintf (stderr, " NB1(%02d), NB2(%02d), NB3(%02d), NB4(%02d), NB5(%02d)", nb1, nb2, nb3, nb4, nb5);
|
||||
@@ -1012,14 +1067,14 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
|
||||
//if using rigctl we can set an unknown or updated cc frequency
|
||||
//by polling rigctl for the current frequency
|
||||
if (opts->use_rigctl == 1 && opts->p25_is_tuned == 0) //&& state->p25_cc_freq == 0
|
||||
if (opts->use_rigctl == 1 && opts->p25_is_tuned == 0)
|
||||
{
|
||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||
}
|
||||
|
||||
//if using rtl input, we can ask for the current frequency tuned
|
||||
if (opts->audio_in_type == 3 && opts->p25_is_tuned == 0) //&& state->p25_cc_freq == 0
|
||||
if (opts->audio_in_type == 3 && opts->p25_is_tuned == 0)
|
||||
{
|
||||
ccfreq = (long int)opts->rtlsdr_center_freq;
|
||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||
@@ -1065,6 +1120,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
state->is_con_plus = 1; //flag on
|
||||
state->last_vc_sync_time = time(NULL); //bugfix: set sync here so we don't immediately tune back to CC constantly.
|
||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||
}
|
||||
|
||||
//rtl_udp
|
||||
@@ -1075,6 +1131,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
opts->p25_is_tuned = 1;
|
||||
state->is_con_plus = 1; //flag on
|
||||
state->last_vc_sync_time = time(NULL); //bugfix: set sync here so we don't immediately tune back to CC constantly.
|
||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1087,7 +1144,240 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
} //end Connect+
|
||||
|
||||
//Hytera XPT -- Experimental, but values now seem very consistent and trunking is working on systems tested with
|
||||
if (csbk_fid == 0x68)
|
||||
{
|
||||
//XPT Site Status
|
||||
if (csbk_o == 0x0A)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
|
||||
uint8_t xpt_ch[6]; //one tg/call per LSN
|
||||
uint16_t tg = 0; //8-bit TG value or TGT hash
|
||||
int i, j;
|
||||
|
||||
//tg and channel info for trunking purposes
|
||||
uint8_t t_tg[6];
|
||||
memset (t_tg, 0, sizeof(t_tg));
|
||||
|
||||
uint8_t xpt_seq = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[0], 2); //this replaces the CSBK lb and pf
|
||||
uint8_t xpt_free = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 4); //free repeater channel LCN
|
||||
uint8_t xpt_bank = 0;
|
||||
uint8_t xpt_lcn = 1;
|
||||
|
||||
//determine starting position of LCN value
|
||||
if (xpt_seq == 1) xpt_lcn = 4;
|
||||
if (xpt_seq == 2) xpt_lcn = 7;
|
||||
|
||||
//determine xpt_bank value for LSN value/tuning
|
||||
if (xpt_seq) xpt_bank = xpt_seq*6;
|
||||
|
||||
//get 2-bit status values for each 6 LSNs
|
||||
for (i = 0; i < 6; i++) xpt_ch[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[20+(i*2)], 2);
|
||||
|
||||
fprintf (stderr, " Hytera XPT Site Status - Free LCN: %d SN: %d", xpt_free, xpt_seq);
|
||||
|
||||
//Print List of LCN with LSN Activity
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
//add LCN value for each LSN pair to help users differentiate between the two when seeing LCN on FLC
|
||||
if (i == 0 || i == 2 || i == 4)
|
||||
{
|
||||
fprintf (stderr, "\n LCN %d - ", xpt_lcn);
|
||||
xpt_lcn++;
|
||||
}
|
||||
|
||||
//LSN value here is logical slot, in flco, we get the logical channel (which is the repeater, does not include the slot value)
|
||||
fprintf (stderr, "LSN %02d: ", i+xpt_bank+1); //swapped out xpt_bank for all (xpt_seq*6) to simplify things
|
||||
tg = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[i*8+32], 8);
|
||||
fprintf (stderr, "ST-%X", xpt_ch[i]); //status bits value 0,1,2, or 3
|
||||
if (tg != 0) fprintf (stderr, " %03d; ", tg);
|
||||
else
|
||||
{
|
||||
if (xpt_ch[i] == 3) fprintf (stderr, " Null; "); //NULL on ST-3 indicates repeater is not active for the LSNs that would be covered by these bits
|
||||
else if (xpt_ch[i] == 2) fprintf (stderr, " Priv; "); //This seems to be used on both private data and private voice calls, but occassionally has a 0 TGT value
|
||||
else if (xpt_ch[i] == 1) fprintf (stderr, " Unk; "); //the 01 value has not been observed as of yet
|
||||
else if (xpt_ch[i] == 0) fprintf (stderr, " Idle; "); //Idle
|
||||
|
||||
//if Priv/Unk occurs, add t_tg value to it for tuning purposes
|
||||
if (xpt_ch[i] == 2)
|
||||
{
|
||||
if (opts->trunk_tune_private_calls == 1) t_tg[i] = 1;
|
||||
}
|
||||
|
||||
//could cause erroneous tuning since we don't yet know what the unk value really is (never observed)
|
||||
if (xpt_ch[i] == 1)
|
||||
{
|
||||
// if (opts->trunk_tune_data_calls == 1) t_tg[i] = 1; //disabled
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if (i == 2) fprintf (stderr, "\n ");
|
||||
|
||||
//add values to trunking tg/channel potentials
|
||||
if (tg != 0) t_tg[i+xpt_bank] = tg;
|
||||
|
||||
}
|
||||
|
||||
//debug ncurses terminal display
|
||||
// sprintf (state->dmr_lrrp_gps[0], "XPT SN %d LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d;", xpt_seq, xpt_bank+1, t_tg[0], xpt_bank+2, t_tg[1], xpt_bank+3, t_tg[2]);
|
||||
// sprintf (state->dmr_lrrp_gps[1], "XPT SN %d LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d;", xpt_seq, xpt_bank+4, t_tg[3], xpt_bank+5, t_tg[4], xpt_bank+6, t_tg[5]);
|
||||
|
||||
//add string for ncurses terminal display
|
||||
sprintf (state->dmr_site_parms, "Free LCN - %d ", xpt_free);
|
||||
|
||||
//assign to cc freq to follow during no sync
|
||||
long int ccfreq = 0;
|
||||
|
||||
//if using rigctl we can set an unknown or updated cc frequency
|
||||
if (opts->use_rigctl == 1 )
|
||||
{
|
||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||
if (ccfreq != 0)
|
||||
{
|
||||
state->p25_cc_freq = ccfreq;
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//if using rtl input, we can ask for the current frequency tuned
|
||||
if (opts->audio_in_type == 3 )
|
||||
{
|
||||
ccfreq = (long int)opts->rtlsdr_center_freq;
|
||||
if (ccfreq != 0)
|
||||
{
|
||||
state->p25_cc_freq = ccfreq;
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Skip tuning calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPXPT;
|
||||
|
||||
//don't tune if vc on the current channel
|
||||
if ( (time(NULL) - state->last_vc_sync_time) > 2 ) //parenthesis error fixed
|
||||
{
|
||||
for (j = 0; j < 6; j++) //go through the channels stored looking for active ones to tune to
|
||||
{
|
||||
char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
//this won't work properly on hashed TGT values
|
||||
//unless users load a TGT hash in a csv file
|
||||
//and hope it doesn't clash with other normal TG values
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == t_tg[j+xpt_bank])
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
//without priority, this will tune the first one it finds (if group isn't blocked)
|
||||
if (t_tg[j+xpt_bank] != 0 && state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0))
|
||||
{
|
||||
//debug print for tuning verification
|
||||
fprintf (stderr, "\n LSN/TG to tune to: %d - %d", j+xpt_bank+1, t_tg[j+xpt_bank]);
|
||||
|
||||
if (state->trunk_chan_map[j+xpt_bank+1] != 0) //if we have a valid frequency
|
||||
{
|
||||
//RIGCTL
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
//debug
|
||||
fprintf (stderr, " - Freq: %ld", state->trunk_chan_map[j+xpt_bank+1]);
|
||||
|
||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[j+xpt_bank+1]);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+xpt_bank+1];
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||
j = 11; //break loop
|
||||
}
|
||||
|
||||
//rtl_udp
|
||||
else if (opts->audio_in_type == 3)
|
||||
{
|
||||
//debug
|
||||
fprintf (stderr, " - Freq: %ld", state->trunk_chan_map[j+xpt_bank+1]);
|
||||
|
||||
rtl_udp_tune (opts, state, state->trunk_chan_map[j+xpt_bank+1]);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+xpt_bank+1];
|
||||
opts->p25_is_tuned = 1;
|
||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||
j = 11; //break loop
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} //end tuning
|
||||
|
||||
SKIPXPT: ;
|
||||
|
||||
sprintf (state->dmr_branding_sub, "XPT ");
|
||||
|
||||
//Notes: I had a few issues to fix in this CSBK, but it does appear that this is trunking on a few small systems now,
|
||||
//albeit a very quiet systems that makes it difficult to know for certain if every aspect is working correctly
|
||||
|
||||
//Notes: I've set XPT to set a CC frequency to whichever frequency its tuned to currently and getting
|
||||
//this particular CSBK, if the status portion does work correctly, then it shouldn't matter which frequency it is on
|
||||
//as long as this CSBK comes in and we can tune to other repeater lcns if they have activity and the frequency mapping
|
||||
//is correct in the csv file, assuming the current frequency doesn't have voice activity
|
||||
|
||||
} //end 0x0A
|
||||
|
||||
//XPT Adjacent Site Information -- Have yet to find a consistent indication of 'current site' identification in any CSBK/FLC/SLC payload
|
||||
if (csbk_o == 0x0B)
|
||||
{
|
||||
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
|
||||
int i;
|
||||
uint8_t xpt_site_id[4];
|
||||
uint8_t xpt_site_rp[4];
|
||||
uint8_t xpt_site_u1[4];
|
||||
uint8_t xpt_site_u2[4];
|
||||
|
||||
uint8_t xpt_sn = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[0], 2);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
xpt_site_id[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16+(i*16)], 5);
|
||||
xpt_site_u1[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[21+(i*16)], 3);
|
||||
xpt_site_rp[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24+(i*16)], 4);
|
||||
xpt_site_u2[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[28+(i*16)], 4);
|
||||
}
|
||||
|
||||
fprintf (stderr, " Hytera XPT CSBK 0x0B - SN: %d", xpt_sn);
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " XPT Adjacent ");
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (xpt_site_id[i] != 0)
|
||||
{
|
||||
fprintf (stderr, "Site:%d Free:%d; ", xpt_site_id[i], xpt_site_rp[i]);
|
||||
// fprintf (stderr, "RS1: %d RS2: %d - ", xpt_site_u1[i], xpt_site_u2[i]); //debug
|
||||
}
|
||||
}
|
||||
sprintf (state->dmr_branding_sub, "XPT ");
|
||||
|
||||
} //end 0x0B
|
||||
|
||||
} //end Hytera XPT section
|
||||
|
||||
}
|
||||
END:
|
||||
|
||||
@@ -76,11 +76,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||
else
|
||||
{
|
||||
cach_okay = -1;
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
SlotTypeOk = 0;
|
||||
goto END;
|
||||
}
|
||||
SlotTypeOk = 0;
|
||||
goto END;
|
||||
}
|
||||
|
||||
state->currentslot = tact_bits[1];
|
||||
@@ -262,11 +259,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||
else
|
||||
{
|
||||
SlotTypeOk = -1;
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
SlotTypeOk = 0;
|
||||
goto END;
|
||||
}
|
||||
SlotTypeOk = 0;
|
||||
goto END;
|
||||
}
|
||||
|
||||
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
||||
|
||||
112
src/dmr_dburst.c
112
src/dmr_dburst.c
@@ -9,9 +9,11 @@
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
//WIP: CRC9 on confirmed data blocks; CRC32 on completed data sf;
|
||||
//WIP: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC
|
||||
//WIP: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
|
||||
//WIP: Unified Single Block Data - USBD -- TODO: USBD LIP Protocol Handling
|
||||
//WIP: UDT Header and Blocks (coded - need samples)
|
||||
//WIP: CRC9/CRC32 on Rate 1 Data (need samples)
|
||||
//WIP: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC (rc interleave) and when to run RC and not SB
|
||||
//WIP: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
@@ -43,9 +45,9 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
uint8_t LC_DataBytes[10];
|
||||
int Burst = -1;
|
||||
|
||||
//Unified PDU (is this the best nomenclature) Bytes
|
||||
uint8_t DMR_PDU[25]; //using 25 bytes as the max for now
|
||||
uint8_t DMR_PDU_bits[196]; //bitwise version of unified payload, will probably be a lot easier to use this w/ manual
|
||||
//PDU Bytes and Bits
|
||||
uint8_t DMR_PDU[25];
|
||||
uint8_t DMR_PDU_bits[196];
|
||||
memset (DMR_PDU, 0, sizeof (DMR_PDU));
|
||||
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
||||
|
||||
@@ -61,9 +63,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
uint8_t is_trellis = 0;
|
||||
uint8_t is_emb = 0;
|
||||
uint8_t is_lc = 0;
|
||||
uint8_t is_full = 0;
|
||||
uint8_t is_full = 0;
|
||||
uint8_t is_udt = 0;
|
||||
uint8_t pdu_len = 0;
|
||||
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
||||
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
||||
|
||||
uint8_t usbd_st = 0; //usbd service type
|
||||
|
||||
switch(databurst){
|
||||
case 0x00: //PI
|
||||
@@ -127,7 +132,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
pdu_len = 10;
|
||||
pdu_start = 2; //start at plus two when assembling
|
||||
sprintf(state->fsubtype, " R12C ");
|
||||
}
|
||||
}
|
||||
if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks
|
||||
{
|
||||
is_udt = 1;
|
||||
sprintf(state->fsubtype, " UDTC ");
|
||||
}
|
||||
break;
|
||||
case 0x08: //3/4 Rate Data
|
||||
is_trellis = 1;
|
||||
@@ -159,12 +169,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
sprintf(state->fsubtype, " R_1C ");
|
||||
}
|
||||
break;
|
||||
case 0x0B: //Unified Data
|
||||
case 0x0B: //Unified Single Block Data USBD
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0x3333;
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " UDAT ");
|
||||
sprintf(state->fsubtype, " USBD ");
|
||||
break;
|
||||
|
||||
//special types (not real data 'sync' bursts)
|
||||
@@ -185,6 +195,9 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
|
||||
}
|
||||
|
||||
//flag off prop head when not looking at data blocks
|
||||
if (databurst != 0x6 && databurst != 0x7 && databurst != 0x8 && databurst != 0xA && databurst != 0xB) state->data_p_head[slot] = 0;
|
||||
|
||||
if (databurst != 0xEB)
|
||||
{
|
||||
if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
||||
@@ -199,7 +212,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
for (i = 0; i < 6; i++) //3 byte CACH
|
||||
{
|
||||
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||
fprintf (pFile, "%X", cach_byte);
|
||||
fprintf (pFile, "%X", cach_byte); //nibble, not a byte, next time I look at this and wonder why its not %02X
|
||||
}
|
||||
fprintf (pFile, "\n%d %02X ", slot+1, databurst); //use hex value of current data burst type
|
||||
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||
@@ -230,7 +243,6 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
//debug print
|
||||
//fprintf (stderr, " RAS? %X - %d %d %d", BPTCReservedBits, R[0], R[1], R[2]);
|
||||
|
||||
|
||||
/* Convert the 96 bits BPTC data into 12 bytes */
|
||||
k = 0;
|
||||
for(i = 0; i < 12; i++)
|
||||
@@ -266,10 +278,6 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
CRCCorrect = 1;
|
||||
}
|
||||
|
||||
//set CRC to correct on proprietary data header blocks (unsure if p_head data has valid crcs,
|
||||
//may be ras enabled, manufacturer specific, or none, currently unknown, but possibly RAS on samples I have)
|
||||
// else if (state->data_p_head[slot] == 1 && databurst == 0x06) CRCCorrect = 1;
|
||||
|
||||
//run CRC9 on intermediate and last 1/2 confirmed data blocks
|
||||
else if (state->data_conf_data[slot] == 1 && databurst == 0x7)
|
||||
{
|
||||
@@ -294,7 +302,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
}
|
||||
|
||||
//run CCITT on other data forms
|
||||
else //will still run on unconfirmed data (shouldn't matter other than a possible bogus warning message)
|
||||
else
|
||||
{
|
||||
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
||||
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||
@@ -302,12 +310,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
}
|
||||
|
||||
//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; //!=0, or == 0x4, or just R[2] == 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
|
||||
if (BPTCDmrDataByte[1] == 0x68) is_ras = 0;
|
||||
|
||||
//if this is a RAS system, set the CRC to okay if irrecoverable errors is 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 (is_ras == 1)
|
||||
{
|
||||
@@ -337,7 +345,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
//convert to unified DMR_PDU and DMR_PDU_bits
|
||||
//convert to DMR_PDU and DMR_PDU_bits
|
||||
for (i = 0; i < pdu_len; i++)
|
||||
{
|
||||
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
||||
@@ -468,7 +476,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
}
|
||||
else
|
||||
{
|
||||
IrrecoverableErrors = 9; //set as a shim since the trellis decoder is a pain to fix
|
||||
// IrrecoverableErrors = 9; //set as a shim since the trellis decoder is a pain to fix
|
||||
state->data_block_crc_valid[slot][blockcounter] = 0;
|
||||
}
|
||||
|
||||
@@ -545,14 +553,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC
|
||||
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 3); //EMB
|
||||
|
||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified) - type 1
|
||||
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
||||
if (databurst == 0x07) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //1/2 Rate Data
|
||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, UDT) - type 1
|
||||
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors);
|
||||
if (databurst == 0x08) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //3/4 Rate Data
|
||||
if (databurst == 0x0A) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Full Rate Data
|
||||
|
||||
//my best understanding is that unified data carries supplimentary MS data on TIII systems, not control signalling
|
||||
if (databurst == 0x0B) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Unified Data
|
||||
if (databurst == 0x07 && is_udt == 0) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //1/2 Rate Data
|
||||
if (databurst == 0x07 && is_udt == 1) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 3); //UDT with 1/2 Rate Encoding
|
||||
|
||||
//control signalling types (CSBK, MBC)
|
||||
if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
||||
@@ -561,25 +567,38 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
if (databurst == 0x04)
|
||||
{
|
||||
state->data_block_counter[slot] = 0; //zero block counter before running header
|
||||
state->data_header_valid[slot] = 1; //set valid header since we received one
|
||||
dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
||||
}
|
||||
if (databurst == 0x05) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
||||
|
||||
//Unified Single Data Block (USBD) -- Not to be confused with Unified Data Transport (UDT)
|
||||
if (databurst == 0x0B)
|
||||
{
|
||||
//TODO: provide more robust handling at a later date -- ETSI TS 102 361-4 V1.11.1 (2021-01) 6.6.11.3
|
||||
usbd_st = (uint8_t)ConvertBitIntoBytes(&DMR_PDU_bits[0], 4);
|
||||
fprintf (stderr, "%s\n", KYEL);
|
||||
fprintf (stderr, " Unified Single Block Data - ");
|
||||
if (usbd_st == 0) fprintf (stderr, "Location Information Protocol");
|
||||
else if (usbd_st > 8) fprintf (stderr, "Manufacturer Specific Service %d ", usbd_st);
|
||||
else fprintf (stderr, "Reserved %d ", usbd_st);
|
||||
}
|
||||
|
||||
//set the original CRCCorrect back to its original value if the RAS flag was tripped
|
||||
if (is_ras == 1) CRCCorrect = crc_original_validity;
|
||||
|
||||
//start printing relevant fec/crc/ras messages, don't print on idle or MBC continuation blocks (handled in dmr_block.c)
|
||||
if (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)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf(stderr, " (FEC OK)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
// if (IrrecoverableErrors == 0 && CRCCorrect == 0 && databurst != 0x09 && databurst != 0x05)
|
||||
// {
|
||||
// fprintf (stderr, "%s", KYEL);
|
||||
// fprintf(stderr, " (FEC OK)");
|
||||
// fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
if (IrrecoverableErrors != 0 && databurst != 0x09 && databurst != 0x05)
|
||||
if (IrrecoverableErrors != 0 && databurst != 0x09) //&& databurst != 0x05
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf(stderr, " (FEC ERR)");
|
||||
@@ -596,7 +615,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (CRCCorrect == 0 && databurst != 0x09 && databurst != 0x05)
|
||||
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " (CRC ERR) ");
|
||||
@@ -619,25 +638,6 @@ 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, "%s", KNRM);
|
||||
|
||||
//short data - DD_HEAD, SP_HEAD or R_HEAD (Might do on TMS as well)
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0xD )
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, "\n Hex to Ascii - ");
|
||||
for (i = 0; i < pdu_len; i++)
|
||||
{
|
||||
if (DMR_PDU[i] <= 0x7E && DMR_PDU[i] >=0x20)
|
||||
{
|
||||
fprintf (stderr, "%c", DMR_PDU[i]);
|
||||
}
|
||||
else fprintf (stderr, ".");
|
||||
}
|
||||
|
||||
fprintf (stderr,"%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
354
src/dmr_flco.c
354
src/dmr_flco.c
@@ -30,18 +30,43 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
int is_cap_plus = 0;
|
||||
int is_alias = 0;
|
||||
int is_gps = 0;
|
||||
|
||||
//XPT 'Things'
|
||||
int is_xpt = 0;
|
||||
uint8_t xpt_hand = 0; //handshake
|
||||
uint8_t xpt_free = 0; //free repeater
|
||||
uint8_t xpt_int = 0; //xpt channel to interrupt (channel/repeater call should occur on?)
|
||||
uint8_t xpt_res_a = 0; //unknown values of other bits of the XPT LC
|
||||
uint8_t xpt_res_b = 0; //unknown values of other bits of the XPT LC
|
||||
uint8_t xpt_res_c = 0; //unknown values of other bits of the XPT LC
|
||||
uint8_t target_hash[24]; //for XPT (and others if desired, get the hash and compare against SLC or XPT Status CSBKs)
|
||||
uint8_t tg_hash = 0; //value of the hashed TG
|
||||
|
||||
uint8_t slot = state->currentslot;
|
||||
uint8_t unk = 0; //flag for unknown FLCO + FID combo
|
||||
|
||||
|
||||
pf = (uint8_t)(lc_bits[0]); //Protect Flag
|
||||
reserved = (uint8_t)(lc_bits[1]); //Reserved
|
||||
pf = (uint8_t)(lc_bits[0]); //Protect Flag -- Hytera XPT uses this to signify which TS the PDU is on
|
||||
reserved = (uint8_t)(lc_bits[1]); //Reserved -- Hytera XPT G/I bit; 0 - Individual; 1 - Group;
|
||||
flco = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //Full Link Control Opcode
|
||||
fid = (uint8_t)ConvertBitIntoBytes(&lc_bits[8], 8); //Feature set ID (FID)
|
||||
so = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8); //Service Options
|
||||
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24); //Target or Talk Group
|
||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[48], 24);
|
||||
|
||||
//read ahead a little to get this for the xpt flag
|
||||
if (IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68)
|
||||
{
|
||||
sprintf (state->dmr_branding, "%s", " Hytera");
|
||||
sprintf (state->dmr_branding_sub, "XPT ");
|
||||
}
|
||||
|
||||
//look at the dmr_branding_sub for the XPT string
|
||||
//branding sub is set at CSBK(68-3A and 3B), SLCO 8, and here on 0x09
|
||||
if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1;
|
||||
|
||||
//hytera XPT -- disable the pf flag, is used for TS value in some Hytera XPT PDUs
|
||||
if (is_xpt) pf = 0;
|
||||
|
||||
//check protect flag
|
||||
if (pf == 1)
|
||||
{
|
||||
@@ -53,44 +78,49 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
goto END_FLCO;
|
||||
}
|
||||
|
||||
//look for Cap+ on VLC header, then set source and/or rest channel appropriately
|
||||
if (IrrecoverableErrors == 0 && type == 1 && fid == 0x10 && (flco == 0x04 || flco == 0x07) ) //0x07 appears to be a cap+ txi private call
|
||||
{
|
||||
is_cap_plus = 1;
|
||||
capsite = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 4); //can't verify, just speculating
|
||||
restchannel = (int)ConvertBitIntoBytes(&lc_bits[52], 4); //was 48,8
|
||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16);
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
||||
}
|
||||
|
||||
|
||||
if (IrrecoverableErrors == 0)
|
||||
{
|
||||
|
||||
|
||||
if (slot == 0) state->dmr_flco = flco;
|
||||
else state->dmr_flcoR = flco;
|
||||
|
||||
|
||||
//FID 0x10 and FLCO 0x14, 0x15, 0x16 and 0x17 confirmed as Moto EMB Alias
|
||||
//Can probably assume FID 0x10 FLCO 0x18 is Moto EMB GPS -- to be tested
|
||||
if ( fid == 0x10 && (flco == 0x14 || flco == 0x15 || flco == 0x16 || flco == 0x17 || flco == 0x18) )
|
||||
{
|
||||
flco = flco - 0x10;
|
||||
fid = 0;
|
||||
}
|
||||
|
||||
//Embedded Talker Alias Header Only (format and len storage)
|
||||
if (fid == 0 && type == 3 && flco == 0x04)
|
||||
if ( (fid == 0 || fid == 0x68) && type == 3 && flco == 0x04)
|
||||
{
|
||||
dmr_embedded_alias_header(opts, state, lc_bits);
|
||||
}
|
||||
|
||||
//Embedded Talker Alias Header (continuation) and Blocks
|
||||
if (fid == 0 && type == 3 && flco > 0x3 && flco < 0x08)
|
||||
if ( (fid == 0 || fid == 0x68) && type == 3 && flco > 0x03 && flco < 0x08)
|
||||
{
|
||||
is_alias = 1;
|
||||
dmr_embedded_alias_blocks(opts, state, lc_bits);
|
||||
}
|
||||
|
||||
//Embedded GPS
|
||||
if (fid == 0 && type == 3 && flco == 0x08)
|
||||
if ( (fid == 0 || fid == 0x68) && fid == 0 && type == 3 && flco == 0x08)
|
||||
{
|
||||
is_gps = 1;
|
||||
dmr_embedded_gps(opts, state, lc_bits);
|
||||
}
|
||||
|
||||
//look for Cap+ on VLC header, then set source and/or rest channel appropriately
|
||||
if (type == 1 && fid == 0x10 && (flco == 0x04 || flco == 0x07) ) //0x07 appears to be a cap+ txi private call
|
||||
{
|
||||
is_cap_plus = 1;
|
||||
capsite = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 4); //don't believe so
|
||||
restchannel = (int)ConvertBitIntoBytes(&lc_bits[52], 4); //
|
||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16);
|
||||
}
|
||||
|
||||
//Unknown CapMax/Moto Things
|
||||
if (fid == 0x10 && (flco == 0x08 || flco == 0x28))
|
||||
{
|
||||
@@ -113,8 +143,16 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
{
|
||||
fprintf (stderr, "%s \n", KRED);
|
||||
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||
fprintf (stderr, " Data Terminator (TD_LC)");
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
fprintf (stderr, " Data Terminator (TD_LC) ");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//reset data header format storage
|
||||
state->data_header_format[slot] = 7;
|
||||
//flag off data header validity
|
||||
state->data_header_valid[slot] = 0;
|
||||
//flag off conf data flag
|
||||
state->data_conf_data[slot] = 0;
|
||||
|
||||
goto END_FLCO;
|
||||
}
|
||||
|
||||
@@ -132,8 +170,111 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
goto END_FLCO;
|
||||
}
|
||||
|
||||
//look for any Hytera XPT system, adjust TG to 16-bit allocation
|
||||
//Groups use only 8 out of 16, but 16 always seems to be allocated
|
||||
//private calls use 16-bit target values hashed to 8-bit in the site status csbk
|
||||
//the TLC preceeds the VLC for a 'handshake' call setup in XPT
|
||||
|
||||
//truncate if XPT is set
|
||||
if (is_xpt == 1)
|
||||
{
|
||||
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[32], 16); //16-bit allocation
|
||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16); //16-bit allocation
|
||||
|
||||
//the crc8 hash is the value represented in the CSBK when dealing with private calls
|
||||
for (int i = 0; i < 16; i++) target_hash[i] = lc_bits[32+i];
|
||||
tg_hash = crc8 (target_hash, 16);
|
||||
}
|
||||
|
||||
//XPT Call 'Grant/Alert' Setup Occurs in TLC with a flco 0x09
|
||||
if (fid == 0x68 && flco == 0x09)
|
||||
{
|
||||
//The CSBK always uses an 8-bit TG/TGT; The White Papers (user manuals) say 8-bit TG and 16-bit SRC addressing
|
||||
//private calls and indiv data calls use a hash of their 8 bit tgt values in the CSBK
|
||||
xpt_int = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4); //This is consistent with an LCN value, not an LSN value
|
||||
xpt_free = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 4); //24 and 4 on 0x09
|
||||
xpt_hand = (uint8_t)ConvertBitIntoBytes(&lc_bits[28], 4); //handshake kind: 0 - ordinary; 1-2 Interrupts; 3-15 reserved;
|
||||
|
||||
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[32], 16); //16-bit allocation
|
||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16); //16-bit allocation
|
||||
|
||||
//the bits that are left behind
|
||||
xpt_res_a = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 4); //after the xpt_int channel, before the free repeater channel -- call being established = 7; call connected = 0; ??
|
||||
xpt_res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 8); //where the first 8 bits of the SRC would be
|
||||
|
||||
//the crc8 hash is the value represented in the CSBK when dealing with private calls
|
||||
for (int i = 0; i < 16; i++) target_hash[i] = lc_bits[32+i];
|
||||
tg_hash = crc8 (target_hash, 16);
|
||||
|
||||
fprintf (stderr, "%s \n", KGRN);
|
||||
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
||||
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X ", flco, fid);
|
||||
fprintf (stderr, "TGT=%u SRC=%u ", target, source);
|
||||
|
||||
fprintf (stderr, "Hytera XPT ");
|
||||
|
||||
//Group ID ranges from 1 to 240; emergency group call ID ranges from 250 to 254; all call ID is 255.
|
||||
if (reserved == 1)
|
||||
{
|
||||
fprintf (stderr, "Group "); //according to observation
|
||||
if (target > 248 && target < 255) fprintf (stderr, "Emergency ");
|
||||
if (target == 255) fprintf (stderr, "All ");
|
||||
}
|
||||
else fprintf (stderr, "Private "); //according to observation
|
||||
fprintf (stderr, "Call Alert "); //Alert or Grant
|
||||
|
||||
//reorganized all the 'extra' data to a second line and added extra verbosity
|
||||
if (opts->payload == 1) fprintf (stderr, "\n ");
|
||||
if (opts->payload == 1) fprintf (stderr, "%s", KYEL);
|
||||
//only display the hashed tgt value if its a private call and not a group call
|
||||
if (reserved == 0 && opts->payload == 1) fprintf(stderr, "TGT Hash=%d; ", tg_hash);
|
||||
if (opts->payload == 1) fprintf(stderr, "HSK=%X; ", xpt_hand);
|
||||
//extra verbosity on handshake types found in the patent
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "Handshake - ");
|
||||
if (xpt_hand == 0) fprintf (stderr, "Ordinary; ");
|
||||
else if (xpt_hand == 1) fprintf (stderr, "Callback/Alarm Interrupt; ");
|
||||
else if (xpt_hand == 2) fprintf (stderr, "Release Channel Interrupt; ");
|
||||
else fprintf (stderr, "Reserved; ");
|
||||
}
|
||||
|
||||
// if (opts->payload == 1)
|
||||
// {
|
||||
// if (xpt_res_a == 0) fprintf (stderr, "Call Connected; ");
|
||||
// if (xpt_res_a == 1) fprintf (stderr, "Data Call Request; ");
|
||||
// if (xpt_res_a == 7) fprintf (stderr, "Voice Call Request; ");
|
||||
// if (xpt_res_a == 2) fprintf (stderr, "Unknown Status; ");
|
||||
// }
|
||||
|
||||
//logical repeater channel, not the logical slot value in the CSBK
|
||||
if (opts->payload == 1) fprintf(stderr, "Call on LCN %d; ", xpt_int); //LCN channel call or 'interrupt' will occur on
|
||||
// if (opts->payload == 1) fprintf(stderr, "RS A[%01X]B[%02X]C[%02X]; ", xpt_res_a, xpt_res_b, xpt_res_c); //leftover bits
|
||||
if (opts->payload == 1) fprintf (stderr, "Free LCN %d; ", xpt_free); //current free repeater LCN channel
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
|
||||
//add string for ncurses terminal display
|
||||
sprintf (state->dmr_site_parms, "Free LCN - %d ", xpt_free);
|
||||
|
||||
is_xpt = 1;
|
||||
goto END_FLCO;
|
||||
}
|
||||
|
||||
//Hytera XPT 'Others' -- moved the patent opcodes here as well for now
|
||||
if ( fid == 0x68 && (flco == 0x13 || flco == 0x31 || flco == 0x2E || flco == 0x2F) )
|
||||
{
|
||||
if (type == 1) fprintf (stderr, "%s \n", KCYN);
|
||||
if (type == 2) fprintf (stderr, "%s \n", KCYN);
|
||||
if (type == 3) fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " SLOT %d", state->currentslot+1);
|
||||
fprintf (stderr, " Hytera ");
|
||||
unk = 1;
|
||||
goto END_FLCO;
|
||||
}
|
||||
|
||||
//unknown other manufacturer or OTA ENC, etc.
|
||||
if (fid != 0 && fid != 0x68 && fid != 0x10) //removed tait from the list
|
||||
//removed tait from the list, added hytera 0x08
|
||||
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08)
|
||||
{
|
||||
if (type == 1) fprintf (stderr, "%s \n", KYEL);
|
||||
if (type == 2) fprintf (stderr, "%s \n", KYEL);
|
||||
@@ -147,11 +288,9 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
}
|
||||
|
||||
//will want to continue to observe for different flco and fid combinations to find out their meaning
|
||||
//Standard Addressing/Cap+ Addressing (trying to avoid embedded alias and gps, etc)
|
||||
if(IrrecoverableErrors == 0 && is_alias == 0 && is_gps == 0)
|
||||
{
|
||||
//set overarching manufacturer in use when non-standard feature id set is up
|
||||
//may not want to set moto 0x10 here either, lots of radios use that set as well
|
||||
if (fid != 0) state->dmr_mfid = fid;
|
||||
|
||||
if (type != 2) //VLC and EMB, set our target, source, so, and fid per channel
|
||||
@@ -222,12 +361,18 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
|
||||
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
||||
fprintf(stderr, "TGT=%u SRC=%u ", target, source);
|
||||
if (opts->payload == 1 && is_xpt == 1 && flco == 0x3) fprintf(stderr, "HASH=%d ", tg_hash);
|
||||
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
|
||||
|
||||
//0x04 and 0x05 on a TLC seem to indicate a Cap + Private Call Terminator (perhaps one for each MS)
|
||||
//0x07 on a VLC seems to indicate a Cap+ Private Call Header
|
||||
//0x23 on the Embedded Voice Burst Sync seems to indicate a Cap+ Private Call in progress
|
||||
if (flco == 0x4 || flco == 0x5 || flco == 0x7 || flco == 0x23) //Cap+ Things
|
||||
//0x23 on the Embedded Voice Burst Sync seems to indicate a Cap+ or Cap+ TXI Private Call in progress
|
||||
//0x20 on the Embedded Voice Burst Sync seems to indicate a Moto (non-specific) Group Call in progress
|
||||
//its possible that both EMB FID 0x10 FLCO 0x20 and 0x23 are just Moto but non-specific (observed 0x20 on Tier 2)
|
||||
|
||||
if (fid == 0x68) sprintf (state->call_string[slot], " Hytera ");
|
||||
|
||||
else if (flco == 0x4 || flco == 0x5 || flco == 0x7 || flco == 0x23) //Cap+ Things
|
||||
{
|
||||
sprintf (state->call_string[slot], " Cap+");
|
||||
fprintf (stderr, "Cap+ ");
|
||||
@@ -247,7 +392,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
sprintf (state->call_string[slot], " Private ");
|
||||
fprintf (stderr, "Private ");
|
||||
}
|
||||
else //Grp_V_Ch_Usr
|
||||
else //Grp_V_Ch_Usr -- still valid on hytera VLC
|
||||
{
|
||||
sprintf (state->call_string[slot], " Group ");
|
||||
fprintf (stderr, "Group ");
|
||||
@@ -271,64 +416,74 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// else strcat (state->call_string[slot], " ");
|
||||
|
||||
/* Check the "Service Option" bits */
|
||||
if(so & 0x30)
|
||||
//Motorola FID 0x10 Only
|
||||
if (fid == 0x10)
|
||||
{
|
||||
/* Experimentally determined with DSD+,
|
||||
* is equal to 0x2, this is a TXI call */
|
||||
if((so & 0x30) == 0x20)
|
||||
/* Check the "Service Option" bits */
|
||||
if(so & 0x30)
|
||||
{
|
||||
/* Experimentally determined with DSD+,
|
||||
* is equal to 0x2, this is a TXI call */
|
||||
if((so & 0x30) == 0x20)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " TXI");
|
||||
fprintf(stderr, "TXI ");
|
||||
}
|
||||
else
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " RES");
|
||||
fprintf(stderr, "RS%d ", (so & 0x30) >> 4);
|
||||
}
|
||||
}
|
||||
if(so & 0x08)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " TXI");
|
||||
fprintf(stderr, "TXI ");
|
||||
// strcat (state->call_string[slot], "-BC ");
|
||||
fprintf(stderr, "Broadcast ");
|
||||
}
|
||||
else
|
||||
if(so & 0x04)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " RES");
|
||||
fprintf(stderr, "RS%d ", (so & 0x30) >> 4);
|
||||
// strcat (state->call_string[slot], "-OVCM ");
|
||||
fprintf(stderr, "OVCM ");
|
||||
}
|
||||
}
|
||||
if(so & 0x08)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-BC ");
|
||||
fprintf(stderr, "Broadcast ");
|
||||
}
|
||||
if(so & 0x04)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-OVCM ");
|
||||
fprintf(stderr, "OVCM ");
|
||||
}
|
||||
if(so & 0x03)
|
||||
{
|
||||
if((so & 0x03) == 0x01)
|
||||
if(so & 0x03)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P1");
|
||||
fprintf(stderr, "Priority 1 ");
|
||||
}
|
||||
else if((so & 0x03) == 0x02)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P2");
|
||||
fprintf(stderr, "Priority 2 ");
|
||||
}
|
||||
else if((so & 0x03) == 0x03)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P3");
|
||||
fprintf(stderr, "Priority 3 ");
|
||||
}
|
||||
else /* We should never go here */
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " ");
|
||||
fprintf(stderr, "No Priority ");
|
||||
}
|
||||
if((so & 0x03) == 0x01)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P1");
|
||||
fprintf(stderr, "Priority 1 ");
|
||||
}
|
||||
else if((so & 0x03) == 0x02)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P2");
|
||||
fprintf(stderr, "Priority 2 ");
|
||||
}
|
||||
else if((so & 0x03) == 0x03)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P3");
|
||||
fprintf(stderr, "Priority 3 ");
|
||||
}
|
||||
else /* We should never go here */
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " ");
|
||||
fprintf(stderr, "No Priority ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//should rework this back into the upper portion
|
||||
if (fid == 0x68) fprintf (stderr, "Hytera ");
|
||||
if (is_xpt) fprintf (stderr, "XPT ");
|
||||
if (fid == 0x68 && flco == 0x00) fprintf (stderr, "Group ");
|
||||
if (fid == 0x68 && flco == 0x03) fprintf (stderr, "Private ");
|
||||
|
||||
fprintf(stderr, "Call ");
|
||||
|
||||
|
||||
@@ -378,8 +533,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
END_FLCO:
|
||||
if (unk == 1 || pf == 1)
|
||||
{
|
||||
// fprintf(stderr, " FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
|
||||
fprintf(stderr, " FLCO=0x%02X FID=0x%02X ", flco, fid); //not all LC PDUs contain SVC bits (see TD_LC)
|
||||
fprintf(stderr, " FLCO=0x%02X FID=0x%02X ", flco, fid);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
@@ -646,10 +800,17 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
||||
//Con+
|
||||
uint8_t con_netid = (uint8_t)ConvertBitIntoBytes(&slco_bits[8], 8);
|
||||
uint8_t con_siteid = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 8);
|
||||
|
||||
//Cap+
|
||||
uint8_t capsite = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 4); //can't verify, just speculating
|
||||
uint8_t restchannel = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 4); //was 12,8
|
||||
uint8_t cap_reserved = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 8); //unknown if any significant value?
|
||||
uint8_t capsite = (uint8_t)ConvertBitIntoBytes(&slco_bits[22], 3); //Seems more consistent
|
||||
uint8_t restchannel = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 4);
|
||||
uint8_t cap_reserved = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 2); //significant value?
|
||||
|
||||
//Hytera XPT
|
||||
uint8_t xpt_free = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 4); //free repeater
|
||||
//the next two per SDRTrunk, but only 0 values ever observed here
|
||||
uint8_t xpt_pri = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 4); //priority repeater
|
||||
uint8_t xpt_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 8); //priority TG hash
|
||||
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
@@ -698,20 +859,12 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
||||
fprintf (stderr, " SLCO Activity Update TS1: %X Hash: %02X TS2: %X Hash: %02X", ts1_act, ts1_hash, ts2_act, ts2_hash); //102 361-2 7.1.3.2
|
||||
else if (slco == 0x9)
|
||||
{
|
||||
//remove all setting of branding/sub from SLCO, can cause false positives even with seeming good CRC
|
||||
|
||||
// state->dmr_mfid = 0x10;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf (state->dmr_branding_sub, "%s", "Con+ ");
|
||||
fprintf (stderr, " SLCO Connect Plus Voice Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
|
||||
sprintf (state->dmr_site_parms, "%d-%d ", con_netid, con_siteid);
|
||||
}
|
||||
|
||||
else if (slco == 0xA)
|
||||
{
|
||||
// state->dmr_mfid = 0x10;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf (state->dmr_branding_sub, "%s", "Con+ ");
|
||||
fprintf (stderr, " SLCO Connect Plus Control Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
|
||||
sprintf (state->dmr_site_parms, "%d-%d ", con_netid, con_siteid);
|
||||
|
||||
@@ -725,19 +878,24 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
||||
|
||||
else if (slco == 0xF)
|
||||
{
|
||||
// state->dmr_mfid = 0x10;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
||||
fprintf (stderr, " SLCO Capacity Plus Site: %d - Rest Channel %d - RS: %02X", capsite+1, restchannel, cap_reserved);
|
||||
// state->dmr_rest_channel = restchannel; //test without, may work better
|
||||
fprintf (stderr, " SLCO Capacity Plus Site: %d - Rest Channel %d - RS: %02X", capsite, restchannel, cap_reserved);
|
||||
//assign to cc freq if available
|
||||
if (state->trunk_chan_map[restchannel] != 0)
|
||||
{
|
||||
state->p25_cc_freq = state->trunk_chan_map[restchannel];
|
||||
}
|
||||
|
||||
//nullify any previous TIII data (bugfix for bad assignments or system type switching)
|
||||
// sprintf(state->dmr_site_parms, "%s", "");
|
||||
}
|
||||
else if (slco == 0x08)
|
||||
{
|
||||
//The Priority Repeater and Priority Hash values stem from SDRTrunk, but I've never seen these values not be zeroes
|
||||
fprintf (stderr, " SLCO Hytera XPT - Free LCN %d - PRI LCN %d - PRI HASH: %02X", xpt_free, xpt_pri, xpt_hash);
|
||||
//NOTE: on really busy systems, this free repeater assignment can lag due to the 4 TS requirment to get SLC
|
||||
// fprintf (stderr, " SLCO Hytera XPT - Free LCN %d ", xpt_free);
|
||||
sprintf (state->dmr_branding_sub, "XPT ");
|
||||
|
||||
//add string for ncurses terminal display
|
||||
sprintf (state->dmr_site_parms, "Free LCN - %d ", xpt_free);
|
||||
}
|
||||
|
||||
else fprintf (stderr, " SLCO Unknown - %d ", slco);
|
||||
@@ -789,6 +947,10 @@ void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_b
|
||||
uint8_t len = state->dmr_alias_len[slot];
|
||||
uint8_t start; //starting position depends on context of block and format
|
||||
|
||||
//Cap Max Variation
|
||||
uint8_t fid = (uint8_t)ConvertBitIntoBytes(&lc_bits[8], 8);
|
||||
if (fid == 0x10) block = block - 0x10; //CapMax adds 0x10 to its FLCO (block num) in its Embedded Aliasing
|
||||
|
||||
//there is some issue with the next three lines of code that prevent proper assignments, not sure what.
|
||||
//if (block > 4) start = 16;
|
||||
//else if (block == 4 && format > 0) start = 23; //8-bit and 16-bit chars
|
||||
|
||||
149
src/dmr_le.c
149
src/dmr_le.c
@@ -76,6 +76,12 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
||||
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
|
||||
state->payload_mi = mi_final;
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//run afterwards, or le verification won't match up properly
|
||||
if (state->payload_algid != 0x21)
|
||||
{
|
||||
LFSR64 (state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
if (slot == 1 && state->payload_algidR != 0)
|
||||
@@ -86,6 +92,12 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
||||
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
|
||||
state->payload_miR = mi_final;
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//run afterwards, or le verification won't match up properly
|
||||
if (state->payload_algidR != 0x21)
|
||||
{
|
||||
LFSR64 (state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +116,13 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||
state->DMRvcL = 0;
|
||||
}
|
||||
|
||||
if (state->payload_algid >= 0x21)
|
||||
if (state->payload_algid == 0x21)
|
||||
{
|
||||
LFSR (state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
|
||||
|
||||
}
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
@@ -119,11 +133,13 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||
state->DMRvcR = 0;
|
||||
}
|
||||
|
||||
if (state->payload_algidR >= 0x21)
|
||||
if (state->payload_algidR == 0x21)
|
||||
{
|
||||
LFSR (state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -134,12 +150,11 @@ void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
|
||||
state->dropR = 256;
|
||||
state->DMRvcL = 0;
|
||||
state->DMRvcR = 0;
|
||||
state->payload_miP = 0;
|
||||
state->payload_miP = 0;
|
||||
state->payload_miN = 0;
|
||||
}
|
||||
|
||||
//handle Single Burst (Voice Burst F) or Reverse Channel Signalling
|
||||
//(Embedded or Dedicated Outbound, not Stand Alone (MS sourced) or Direct Mode)
|
||||
//MS Sourced or Direct Mode probably would be handled poorly by the demodulator
|
||||
void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
{
|
||||
int i;
|
||||
@@ -164,11 +179,11 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
//power == 1 should be reverse channel -- still need to check the interleave inside of BPTC
|
||||
if (power == 1) irr_err = BPTC_16x2_Extract_Data(sbrc_interleaved, sbrc_return, 1);
|
||||
//bad emb burst, never set a valid power indicator value (probably 9)
|
||||
if (power > 1) goto SBRC_END;
|
||||
if (power > 1) goto SBRC_END;
|
||||
|
||||
//RC Channel CRC 7 Mask = 0x7A; CRC bits are used as privacy indicators on
|
||||
//Single Voice Burst F (see below), other moto values seem to exist there as well
|
||||
//unknown what the other values are (see Cap+ 0x313)
|
||||
//unknown what the other values are (see Cap+ 0x313 and 0x643)
|
||||
if (power == 1) //RC
|
||||
{
|
||||
crc_extracted = 0;
|
||||
@@ -206,70 +221,78 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||
}
|
||||
}
|
||||
|
||||
//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?)
|
||||
//I've observed it in the Private Cap+ TXI calls as well
|
||||
//there also seems to be a correlation between the SVC bits for TXI (reserved=3) and these extra cap+ values
|
||||
uint8_t sbrc_opcode = sbrc_hex;
|
||||
uint8_t alg = sbrc_hex & 3;
|
||||
uint16_t key = (sbrc_hex >> 3) & 0xFF;
|
||||
// uint8_t sbrc_opcode = sbrc_hex;
|
||||
// uint8_t alg = sbrc_hex & 0x7;
|
||||
// uint8_t key = (sbrc_hex >> 3) & 0xFF;
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
if (irr_err != 0) fprintf (stderr, "%s (FEC ERR) %d %s", KRED, irr_err, KNRM);
|
||||
if (irr_err == 0)
|
||||
{
|
||||
if (sbrc_hex == 0) ; //NULL
|
||||
else if (sbrc_hex == 0x313)
|
||||
{
|
||||
//Cap+ Thing? Observed On Cap+ Systems
|
||||
// fprintf (stderr, " Cap+ Thing?");
|
||||
}
|
||||
else
|
||||
{
|
||||
//this pattern seems to fit the multiple cap+ systems I've observed
|
||||
//test on site '7' to make sure (works on 1 and 4)
|
||||
// uint8_t cap_site = (sbrc_hex >> 4) & 0x7;
|
||||
|
||||
if (slot == 0)
|
||||
{
|
||||
//key and alg only present SOME times, not all,
|
||||
//also, intermixed with other signalling
|
||||
//needs more study first!
|
||||
if (state->dmr_so & 0x40 && key != 0 && state->payload_keyid == 0)
|
||||
{
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
fprintf (stderr, "\n Slot 1");
|
||||
fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
// if (opts->payload == 1)
|
||||
// {
|
||||
// if (irr_err != 0) fprintf (stderr, "%s (FEC ERR) %d %s", KRED, irr_err, KNRM);
|
||||
// if (irr_err == 0)
|
||||
// {
|
||||
// if (sbrc_hex == 0) ; //NULL
|
||||
// else if (strcmp (state->dmr_branding_sub, "Cap+ ") == 0)
|
||||
// {
|
||||
// //Cap+ Site ID seems to fit here on samples I've seen with the alternate SB signalling, but no idea what signals that info and not the emb alg/key info
|
||||
// fprintf (stderr, "%s ", KYEL);
|
||||
// fprintf (stderr, "\n ");
|
||||
// fprintf (stderr, " Capacity Plus Site %d", cap_site);
|
||||
// fprintf (stderr, "%s ", KNRM);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
|
||||
// if (slot == 0)
|
||||
// {
|
||||
// //key and alg only present SOME times, not all,
|
||||
// //also, intermixed with other signalling
|
||||
// //needs more study first!
|
||||
// if (state->dmr_so & 0x40 && key != 0 && state->payload_keyid == 0)
|
||||
// {
|
||||
// if (opts->payload == 1)
|
||||
// {
|
||||
// fprintf (stderr, "%s ", KYEL);
|
||||
// fprintf (stderr, "\n Slot 1");
|
||||
// fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
|
||||
// fprintf (stderr, "%s ", KNRM);
|
||||
// }
|
||||
|
||||
//needs more study before assignment
|
||||
//state->payload_keyid = key;
|
||||
//state->payload_algid = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||
}
|
||||
}
|
||||
if (slot == 1)
|
||||
{
|
||||
if (state->dmr_soR & 0x40 && key != 0 && state->payload_keyidR == 0)
|
||||
{
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
fprintf (stderr, "\n Slot 2");
|
||||
fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
// //needs more study before assignment
|
||||
// // state->payload_keyid = key;
|
||||
// // state->payload_algid = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (slot == 1)
|
||||
// {
|
||||
// if (state->dmr_soR & 0x40 && key != 0 && state->payload_keyidR == 0)
|
||||
// {
|
||||
// if (opts->payload == 1)
|
||||
// {
|
||||
// fprintf (stderr, "%s ", KYEL);
|
||||
// fprintf (stderr, "\n Slot 2");
|
||||
// fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
|
||||
// fprintf (stderr, "%s ", KNRM);
|
||||
// }
|
||||
|
||||
//needs more study before assignment
|
||||
//state->payload_keyidR = key;
|
||||
//state->payload_algidR = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||
}
|
||||
}
|
||||
// //needs more study before assignment
|
||||
// // state->payload_keyidR = key;
|
||||
// // state->payload_algidR = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
// }
|
||||
// }
|
||||
|
||||
SBRC_END:
|
||||
|
||||
|
||||
245
src/dmr_pdu.c
245
src/dmr_pdu.c
@@ -44,21 +44,20 @@ void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR
|
||||
uint8_t padding = state->data_header_padding[slot];
|
||||
uint8_t lrrp_conf = 0;
|
||||
|
||||
//consider disabling on prop_head data blocks if bad/false decodes
|
||||
//if (state->data_p_head[slot] == 0)
|
||||
lrrp_conf = dmr_lrrp (opts, state, block_len, DMR_PDU);
|
||||
//check for more available flag info, etc on these prior to running
|
||||
if (DMR_PDU[0] == 0x01) dmr_locn (opts, state, block_len, DMR_PDU);
|
||||
else dmr_lrrp (opts, state, block_len, DMR_PDU);
|
||||
|
||||
//maybe one day we will have more things to do here
|
||||
state->data_conf_data[slot] = 0; //flag off confirmed data after processing it
|
||||
state->data_p_head[slot] = 0; //flag off prop_head data after processing it
|
||||
state->data_header_format[slot] = 0; //zero out so we don't keep ascii printing
|
||||
|
||||
}
|
||||
|
||||
//The contents of this function are mostly my reversed engineered efforts by observing DSDPlus output and matching data bytes
|
||||
//combined with a few external sources such as OK-DMR for some token values and extra data values (rad and alt)
|
||||
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
|
||||
uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||
{
|
||||
int i, j;
|
||||
uint16_t message_len = 0;
|
||||
@@ -83,12 +82,15 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
|
||||
//lat-long-radius-alt
|
||||
uint32_t lat = 0;
|
||||
uint8_t lat_sign = 0;
|
||||
uint32_t lon = 0;
|
||||
uint16_t rad = 0;
|
||||
uint16_t rad = 0; //or Azimuth in Circle 3D?
|
||||
uint8_t alt = 0;
|
||||
double lat_unit = (double)180/(double)4294967295;
|
||||
double lon_unit = (double)360/(double)4294967295;
|
||||
double lat_fin = 0.0; //calculated values
|
||||
double lon_fin = 0.0; //calculated values
|
||||
int lat_sign = 1; //positive 1, or negative 1
|
||||
int lon_sign = 1; //positive 1, or negative 1
|
||||
|
||||
//speed/velocity
|
||||
uint16_t vel = 0;
|
||||
@@ -97,28 +99,19 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
|
||||
//track, direction, degrees
|
||||
uint8_t degrees = 0;
|
||||
uint8_t deg_set = 0;
|
||||
uint8_t deg_set = 0;
|
||||
|
||||
char deg_glyph[4];
|
||||
sprintf (deg_glyph, "%s", "°");
|
||||
// sprintf (deg_glyph, "%s", "d");
|
||||
|
||||
//triggered information report
|
||||
uint8_t report = 0;
|
||||
uint8_t pot_report = 0; //potential report by finding 0x0D and backtracking a few bytes
|
||||
|
||||
uint8_t is_p_head = 0;
|
||||
|
||||
//shim for getting LRRP out of some prop head data blocks
|
||||
if (state->data_p_head[slot] == 1)
|
||||
{
|
||||
if (DMR_PDU[1] != 0x10) goto LRRP_END; //check to see if FID is 0x10
|
||||
i = 0;
|
||||
message_len = blocks * block_len;
|
||||
source = state->dmr_lrrp_source[state->currentslot];
|
||||
is_p_head = 1;
|
||||
}
|
||||
else i = 12;
|
||||
|
||||
|
||||
//start looking for tokens (using my best understanding of the ok-dmr library xml and python files)
|
||||
for (i; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||
for (i = 0; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||
{
|
||||
|
||||
switch(DMR_PDU[i]){
|
||||
@@ -175,20 +168,22 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
}
|
||||
break;
|
||||
//RESULT TOKEN or Request ID!
|
||||
case 0x22: //same comment as below, observed followed the message len
|
||||
case 0x23: //this value has been seen after the 0x0D message len, appears to be a 2-byte value, was tripping a false on time stamp
|
||||
i += 2;
|
||||
break;
|
||||
//answer and report tokens
|
||||
case 0x51: //circle-2d
|
||||
case 0x54: //circle-3d
|
||||
case 0x55: //circle-3d <- false positives on one sample, so either an earlier token occurred (that we don't have) or just random data?
|
||||
case 0x55: //circle-3d
|
||||
if (message_len > 0 && lat == 0)
|
||||
{
|
||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
||||
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
rad = (DMR_PDU[i+9] << 8) + DMR_PDU[i+10];
|
||||
i += 10; //double check
|
||||
i += 10;
|
||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
||||
else lat = 0;
|
||||
}
|
||||
@@ -211,13 +206,14 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x66: //point-2d
|
||||
case 0x66: //point-2d
|
||||
if (message_len > 0 && lat == 0)
|
||||
{
|
||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
||||
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
i += 8; //check
|
||||
i += 8;
|
||||
lrrp_confidence++;
|
||||
}
|
||||
break;
|
||||
@@ -225,12 +221,13 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
case 0x6A: //point-3d
|
||||
if (message_len > 0 && lat == 0)
|
||||
{
|
||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
||||
lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
alt = DMR_PDU[i+9];
|
||||
i += 9; //check
|
||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
||||
i += 9;
|
||||
if (lat > 0 && lon > 0) lrrp_confidence++;
|
||||
else lat = 0;
|
||||
}
|
||||
break;
|
||||
@@ -274,7 +271,7 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
||||
if (lrrp_confidence >= 4) //find the sweet magical number
|
||||
if (lrrp_confidence >= 3) //find the sweet magical number
|
||||
{
|
||||
//now we can open our lrrp file and write to it as well
|
||||
FILE * pFile; //file pointer
|
||||
@@ -286,9 +283,10 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
if (!year) fprintf (pFile, "%s\t", getDateL() ); //current date, only add this IF no included timestamp in LRRP data?
|
||||
if (!year) fprintf (pFile, "%s\t", getTimeL() ); //current timestamp, only add this IF no included timestamp in LRRP data?
|
||||
if (year) fprintf (pFile, "%04d/%02d/%02d\t%02d:%02d:%02d\t", year, month, day, hour, minute, second); //add timestamp from decoded audio if available
|
||||
//write data header source (or destination?) if not available in lrrp data
|
||||
//write data header source if not available in lrrp data
|
||||
if (!source) fprintf (pFile, "%08lld\t", state->dmr_lrrp_source[state->currentslot]); //source address from data header
|
||||
if (source) fprintf (pFile, "%08d\t", source); //add source form decoded audio if available, else its from the header
|
||||
|
||||
}
|
||||
|
||||
if (pot_report)
|
||||
@@ -328,31 +326,45 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
if (lat)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
if (lat_sign) //print a '-' in front of the value
|
||||
fprintf (stderr, " LRRP - Lat: -%.5lf", (double)lat * lat_unit);
|
||||
else fprintf (stderr, " LRRP - Lat: %.5lf", (double)lat * lat_unit);
|
||||
fprintf (stderr, " Lon: %.5lf", (double)lon * lon_unit);
|
||||
if (lat_sign)
|
||||
fprintf (stderr, " (-%.5lf, %.5lf)", (double)lat * lat_unit , (double)lon * lon_unit);
|
||||
else fprintf (stderr, " (%.5lf, %.5lf)", (double)lat * lat_unit , (double)lon * lon_unit);
|
||||
//need to check these new calcs for accuracy accross the globe, both lat and lon
|
||||
|
||||
//two's compliment-ish testing on these bytes
|
||||
if (lat & 0x80000000) //8
|
||||
{
|
||||
lat = lat & 0x7FFFFFFF;
|
||||
lat_sign = -1;
|
||||
// lat = 0x80000000 - lat; //not sure why this doesn't work here like it does on lon, extra bit?
|
||||
}
|
||||
if (lon & 0x80000000)
|
||||
{
|
||||
lon = lon & 0x7FFFFFFF;
|
||||
lon_sign = -1;
|
||||
lon = 0x80000000 - lon;
|
||||
}
|
||||
|
||||
lat_fin = (double)lat * lat_unit * lat_sign;
|
||||
lon_fin = (double)lon * lon_unit * lon_sign;
|
||||
|
||||
fprintf (stderr, " LRRP - Lat: %.5lf", lat_fin);
|
||||
fprintf (stderr, " Lon: %.5lf", lon_fin);
|
||||
fprintf (stderr, " (%.5lf, %.5lf)", lat_fin , lon_fin);
|
||||
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
if (lat_sign) fprintf (pFile, "-");
|
||||
fprintf (pFile, "%.5lf\t", (double)lat * lat_unit);
|
||||
fprintf (pFile, "%.5lf\t", (double)lon * lon_unit);
|
||||
fprintf (pFile, "%.5lf\t", lat_fin);
|
||||
fprintf (pFile, "%.5lf\t", lon_fin);
|
||||
}
|
||||
|
||||
}
|
||||
if (rad)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " LRRP - Radius: %d", rad); //unsure of 'units' or calculation for radius (meters?)
|
||||
fprintf (stderr, " LRRP - Radius: %dm", rad); //unsure of 'units' or calculation for radius (meters?)
|
||||
}
|
||||
if (alt)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " LRRP - Altitude: %d", alt); //unsure of 'units' or calculation for alt (meters?)
|
||||
fprintf (stderr, " LRRP - Altitude: %dm", alt); //unsure of 'units' or calculation for alt (meters?)
|
||||
}
|
||||
if (vel_set)
|
||||
{
|
||||
@@ -363,7 +375,7 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
if (deg_set)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " LRRP - Track: %d Degrees", degrees);
|
||||
fprintf (stderr, " LRRP - Track: %d%s", degrees, deg_glyph);
|
||||
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
||||
}
|
||||
|
||||
@@ -374,20 +386,17 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
fclose (pFile);
|
||||
}
|
||||
|
||||
//save to array for ncurses
|
||||
//save to string for ncurses
|
||||
if (!source) source = state->dmr_lrrp_source[state->currentslot];
|
||||
char sign[8];
|
||||
char velstr[20];
|
||||
char degstr[20];
|
||||
char lrrpstr[100];
|
||||
sprintf (lrrpstr, "%s", "");
|
||||
sprintf (velstr, "%s", "");
|
||||
sprintf (degstr, "%s", "");
|
||||
if (lat_sign) sprintf (sign, "%s", "-");
|
||||
else sprintf (sign, "%s", "");
|
||||
if (lat) sprintf (lrrpstr, "LRRP %0d (%s%lf, %lf)", source, sign, (double)lat * lat_unit, (double)lon * lon_unit);
|
||||
if (lat) sprintf (lrrpstr, "LRRP %0d (%lf, %lf)", source, lat_fin, lon_fin);
|
||||
if (vel_set) sprintf (velstr, " %.4lf km/h", velocity * 3.6);
|
||||
if (deg_set) sprintf (degstr, " %d deg", degrees);
|
||||
if (deg_set) sprintf (degstr, " %d%s ", degrees, deg_glyph);
|
||||
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
||||
|
||||
}
|
||||
@@ -400,16 +409,126 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
||||
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||
}
|
||||
|
||||
// else //debug only, disabled
|
||||
// {
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
||||
// }
|
||||
|
||||
LRRP_END:
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
|
||||
return (lrrp_confidence);
|
||||
}
|
||||
|
||||
void dmr_locn (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||
{
|
||||
int i, j;
|
||||
uint8_t slot = state->currentslot;
|
||||
uint8_t blocks = state->data_header_blocks[slot];
|
||||
uint8_t padding = state->data_header_padding[slot];
|
||||
uint8_t source = state->dmr_lrrp_source[slot];
|
||||
|
||||
//flags if certain data type is present
|
||||
uint8_t time = 0;
|
||||
uint8_t lat = 0;
|
||||
uint8_t lon = 0;
|
||||
|
||||
//date-time variables
|
||||
uint8_t hour = 0;
|
||||
uint8_t minute = 0;
|
||||
uint8_t second = 0;
|
||||
uint8_t year = 0;
|
||||
uint8_t month = 0;
|
||||
uint8_t day = 0;
|
||||
|
||||
//need to experiment best way to do this portion
|
||||
uint8_t lat_deg = 0;
|
||||
uint8_t lat_min = 0;
|
||||
uint16_t lat_sec = 0;
|
||||
|
||||
uint8_t lon_deg = 0;
|
||||
uint8_t lon_min = 0;
|
||||
uint16_t lon_sec = 0;
|
||||
|
||||
char lat_ord[2];
|
||||
char lon_ord[2];
|
||||
sprintf (lat_ord, "%s", "N");
|
||||
sprintf (lon_ord, "%s", "E");
|
||||
|
||||
char deg_glyph[4];
|
||||
sprintf (deg_glyph, "%s", "°");
|
||||
// sprintf (deg_glyph, "%s", "d");
|
||||
|
||||
//more strings...
|
||||
char locnstr[50];
|
||||
char latstr[75];
|
||||
char lonstr[75];
|
||||
sprintf (locnstr, "%s", " ");
|
||||
sprintf (latstr, "%s", " ");
|
||||
sprintf (lonstr, "%s", " ");
|
||||
|
||||
//TODO: conversion to decimal format
|
||||
//DD = d + (min/60) + (sec/3600)
|
||||
//N is positive, E is positive?? Assuming its like a 2D plane
|
||||
int lat_sign = 1; //positive 1 or negative 1
|
||||
int lon_sign = 1; //positive 1 or negative 1
|
||||
double lat_fin = 0;
|
||||
double lon_fin = 0;
|
||||
|
||||
//start looking for specific bytes corresponding to 'letters' A (time), NSEW (ordinal directions), etc
|
||||
for (i = 0; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||
{
|
||||
switch(DMR_PDU[i]){
|
||||
case 0x41: //A -- time and date
|
||||
time = 1;
|
||||
hour = ((DMR_PDU[i+1] - 0x30) << 4) | (DMR_PDU[i+2] - 0x30);
|
||||
minute = ((DMR_PDU[i+3] - 0x30) << 4) | (DMR_PDU[i+4] - 0x30);
|
||||
second = ((DMR_PDU[i+5] - 0x30) << 4) | (DMR_PDU[i+6] - 0x30);
|
||||
//think this is in day, mon, year format
|
||||
day = ((DMR_PDU[i+7] - 0x30) << 4) | (DMR_PDU[i+8] - 0x30);
|
||||
month = ((DMR_PDU[i+9] - 0x30) << 4) | (DMR_PDU[i+10] - 0x30);
|
||||
year = ((DMR_PDU[i+11] - 0x30) << 4) | (DMR_PDU[i+12] - 0x30);
|
||||
i += 12;
|
||||
break;
|
||||
|
||||
case 0x53: //S -- South
|
||||
sprintf (lat_ord, "%s", "S");
|
||||
lat_sign = -1;
|
||||
case 0x4E: //N -- North
|
||||
lat = 1;
|
||||
lat_deg = ((DMR_PDU[i+1] - 0x30) << 4) | (DMR_PDU[i+2] - 0x30);
|
||||
lat_min = ((DMR_PDU[i+3] - 0x30) << 4) | (DMR_PDU[i+4] - 0x30);
|
||||
lat_sec = ((DMR_PDU[i+6] - 0x30) << 12) | ((DMR_PDU[i+7] - 0x30) << 8) | ((DMR_PDU[i+8] - 0x30) << 4) | ((DMR_PDU[i+9] - 0x30) << 0);
|
||||
i += 8;
|
||||
break;
|
||||
|
||||
case 0x57: //W -- West
|
||||
sprintf (lon_ord, "%s", "W");
|
||||
lon_sign = -1;
|
||||
case 0x45: //E -- East
|
||||
lon = 1;
|
||||
lon_deg = ((DMR_PDU[i+1] - 0x30) << 8) | ((DMR_PDU[i+2] - 0x30) << 4) | ((DMR_PDU[i+3] - 0x30) << 0);
|
||||
lon_min = ((DMR_PDU[i+4] - 0x30) << 4) | (DMR_PDU[i+5] - 0x30);
|
||||
lon_sec = ((DMR_PDU[i+7] - 0x30) << 12) | ((DMR_PDU[i+8] - 0x30) << 8) | ((DMR_PDU[i+9] - 0x30) << 4) | ((DMR_PDU[i+10] - 0x30) << 0);
|
||||
i += 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
//do nothing
|
||||
break;
|
||||
|
||||
} //end switch
|
||||
|
||||
} //for i
|
||||
|
||||
if (lat && lon)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, "\n LOCN Report - Source: [%d]\n", source);
|
||||
if (time) fprintf (stderr, " 20%02X/%02X/%02X %02X:%02X:%02X ", year, month, day, hour, minute, second);
|
||||
fprintf (stderr, "Lat: %s %02X%s%02X\"%04X' Lon: %s %02X%s%02X\"%04X' ", lat_ord, lat_deg, deg_glyph, lat_min, lat_sec, lon_ord, lon_deg, deg_glyph, lon_min, lon_sec);
|
||||
|
||||
//string manip for ncurses terminal display
|
||||
sprintf (locnstr, "LOCN %d ", source);
|
||||
sprintf (latstr, "%s %02X%s%02X\"%04X' ", lat_ord, lat_deg, deg_glyph, lat_min, lat_sec);
|
||||
sprintf (lonstr, "%s %02X%s%02X\"%04X' ", lon_ord, lon_deg, deg_glyph, lon_min, lon_sec);
|
||||
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", locnstr, latstr, lonstr);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
114
src/dmr_pi.c
114
src/dmr_pi.c
@@ -15,15 +15,11 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
|
||||
if((IrrecoverableErrors == 0))
|
||||
{
|
||||
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
|
||||
if (0 == 0)
|
||||
{
|
||||
state->payload_algid = PI_BYTE[0];
|
||||
state->payload_keyid = PI_BYTE[2];
|
||||
}
|
||||
|
||||
state->payload_algid = PI_BYTE[0];
|
||||
state->payload_keyid = PI_BYTE[2];
|
||||
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||
if (state->payload_algid < 0x26)
|
||||
{
|
||||
@@ -31,27 +27,26 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
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)
|
||||
{
|
||||
if (0 == 0)
|
||||
{
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
state->payload_mi = 0;
|
||||
}
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
state->payload_mi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
if (0 == 0)
|
||||
{
|
||||
state->payload_algidR = PI_BYTE[0];
|
||||
state->payload_keyidR = PI_BYTE[2];
|
||||
}
|
||||
|
||||
state->payload_algidR = PI_BYTE[0];
|
||||
state->payload_keyidR = PI_BYTE[2];
|
||||
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||
if (state->payload_algidR < 0x26)
|
||||
{
|
||||
@@ -59,20 +54,23 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||
fprintf (stderr, "\n Slot 2");
|
||||
fprintf (stderr, " DMR PI H- ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algidR, state->payload_keyidR, state->payload_miR);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
if (state->payload_algidR != 0x21)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
LFSR64 (state);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->payload_algidR >= 0x26)
|
||||
{
|
||||
if (0== 0)
|
||||
{
|
||||
state->payload_algidR = 0;
|
||||
state->payload_keyidR = 0;
|
||||
state->payload_miR = 0;
|
||||
}
|
||||
state->payload_algidR = 0;
|
||||
state->payload_keyidR = 0;
|
||||
state->payload_miR = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LFSR(dsd_state * state)
|
||||
@@ -95,27 +93,67 @@ void LFSR(dsd_state * state)
|
||||
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
if (1 == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 1");
|
||||
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 1");
|
||||
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||
fprintf(stderr, " MI: 0x%08X", lfsr);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
state->payload_mi = lfsr;
|
||||
}
|
||||
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
if (1 == 1)
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 2");
|
||||
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
||||
fprintf(stderr, " MI: 0x%08X", lfsr);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
state->payload_miR = lfsr;
|
||||
}
|
||||
}
|
||||
|
||||
void LFSR64(dsd_state * state)
|
||||
{
|
||||
{
|
||||
uint64_t lfsr = 0;
|
||||
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
lfsr = (uint64_t) state->payload_mi;
|
||||
}
|
||||
else lfsr = (uint64_t) state->payload_miR;
|
||||
|
||||
uint8_t cnt = 0;
|
||||
|
||||
for(cnt=0;cnt<32;cnt++)
|
||||
{
|
||||
uint64_t bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
|
||||
lfsr = (lfsr << 1) | bit;
|
||||
}
|
||||
|
||||
// if (state->currentslot == 0) state->payload_miP = lfsr;
|
||||
// else state->payload_miN = lfsr;
|
||||
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 1");
|
||||
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||
fprintf (stderr, " MI: 0x%016lX", lfsr);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
state->payload_mi = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
|
||||
}
|
||||
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 2");
|
||||
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
||||
fprintf (stderr, " MI: 0x%016lX", lfsr);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
state->payload_miR = lfsr;
|
||||
}
|
||||
state->payload_miR = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -545,6 +545,9 @@ openAudioInDevice (dsd_opts * opts)
|
||||
const char ch = '.';
|
||||
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
||||
|
||||
//if no extension set, give default of .wav -- bugfix for github issue #105
|
||||
// if (extension == NULL) extension = ".wav";
|
||||
|
||||
// get info of device/file
|
||||
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
||||
{
|
||||
@@ -607,6 +610,25 @@ openAudioInDevice (dsd_opts * opts)
|
||||
{
|
||||
opts->audio_in_type = 5;
|
||||
}
|
||||
|
||||
//if no extension set, treat as named pipe or extensionless wav file -- bugfix for github issue #105
|
||||
else if (extension == NULL)
|
||||
{
|
||||
opts->audio_in_type = 2;
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate = opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels = 1;
|
||||
opts->audio_in_file_info->seekable = 0;
|
||||
opts->audio_in_file_info->format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||
opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||
|
||||
if (opts->audio_in_file == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open file/pipe with libsndfile: %s\n", sf_strerror(NULL));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
else if (strncmp(extension, ".bin", 3) == 0)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
|
||||
@@ -283,12 +283,11 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||
|
||||
valid = 0;
|
||||
|
||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
||||
//honestly can't say I've seen any improvement when using heuristics, but is detrimental to playing back multiple p25 samples
|
||||
if (state->synctype == 1 && opts->p25_trunk == 0)
|
||||
//disabling again, causing issues with trunking P25
|
||||
if (state->synctype == 1 && opts->p25_trunk == 1)
|
||||
{
|
||||
// Use the P25 heuristics if available
|
||||
//valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||
// valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||
}
|
||||
|
||||
if (valid == 0)
|
||||
@@ -348,12 +347,11 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||
|
||||
valid = 0;
|
||||
|
||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
||||
//honestly can't say I've seen any improvement when using heuristics, but is detrimental to playing back multiple p25 samples
|
||||
if (state->synctype == 0 && opts->p25_trunk == 0)
|
||||
//disabling again, causing issues with trunking P25
|
||||
if (state->synctype == 0 && opts->p25_trunk == 1)
|
||||
{
|
||||
// Use the P25 heuristics if available
|
||||
//valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||
// valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||
}
|
||||
|
||||
if (valid == 0)
|
||||
|
||||
@@ -486,10 +486,6 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
state->currentslot = 0;
|
||||
sprintf (state->fsubtype, " LDU1 ");
|
||||
state->numtdulc = 0;
|
||||
if (state->payload_algid == 0x81)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
processLDU1 (opts, state);
|
||||
}
|
||||
@@ -542,8 +538,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||
}
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
// state->lasttg = 0;
|
||||
// state->lastsrc = 0;
|
||||
state->lastp25type = 0;
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->fsubtype, " TDULC ");
|
||||
@@ -604,10 +600,11 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else if (strcmp (duid, "30") == 0)
|
||||
{
|
||||
state->dmrburstL = 29;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," PDU\n"); //multi block/packet PDU
|
||||
fprintf (stderr," MBF\n"); //multi block format PDU
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
@@ -619,79 +616,11 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
resumeScan (opts, state);
|
||||
}
|
||||
state->lastp25type = 4;
|
||||
sprintf (state->fsubtype, " PDU ");
|
||||
sprintf (state->fsubtype, " MBF ");
|
||||
|
||||
processMPDU(opts, state);
|
||||
}
|
||||
|
||||
// try to guess based on previous frame if unknown type
|
||||
// disabling the guessing game on P1
|
||||
|
||||
// else if (state->lastp25type == 1)
|
||||
// {
|
||||
// if (opts->errorbars == 1)
|
||||
// {
|
||||
// printFrameInfo (opts, state);
|
||||
// fprintf (stderr,"(LDU2) ");
|
||||
// }
|
||||
// // if (opts->mbe_out_dir[0] != 0)
|
||||
// // {
|
||||
// // if (opts->mbe_out_f == NULL)
|
||||
// // {
|
||||
// // openMbeOutFile (opts, state);
|
||||
// // }
|
||||
// // }
|
||||
// //state->lastp25type = 0;
|
||||
// // Guess that the state is LDU2
|
||||
// state->lastp25type = 2;
|
||||
// sprintf (state->fsubtype, "(LDU2) ");
|
||||
// state->numtdulc = 0;
|
||||
// processLDU2 (opts, state);
|
||||
// }
|
||||
// else if (state->lastp25type == 2)
|
||||
// {
|
||||
// if (opts->errorbars == 1)
|
||||
// {
|
||||
// printFrameInfo (opts, state);
|
||||
// fprintf (stderr,"(LDU1) ");
|
||||
// }
|
||||
// if (opts->mbe_out_dir[0] != 0)
|
||||
// {
|
||||
// if (opts->mbe_out_f == NULL)
|
||||
// {
|
||||
// openMbeOutFile (opts, state);
|
||||
// }
|
||||
// }
|
||||
// //state->lastp25type = 0;
|
||||
// // Guess that the state is LDU1
|
||||
// state->lastp25type = 1;
|
||||
// sprintf (state->fsubtype, "(LDU1) ");
|
||||
// state->numtdulc = 0;
|
||||
// processLDU1 (opts, state);
|
||||
// }
|
||||
// else if (state->lastp25type == 3)
|
||||
// {
|
||||
// if (opts->errorbars == 1)
|
||||
// {
|
||||
// printFrameInfo (opts, state);
|
||||
// fprintf (stderr," (TSBK)\n");
|
||||
// }
|
||||
// //state->lastp25type = 0;
|
||||
// // Guess that the state is TSBK
|
||||
// state->lastp25type = 3;
|
||||
// sprintf (state->fsubtype, "(TSBK) ");
|
||||
|
||||
// // Now processing NID
|
||||
// skipDibit (opts, state, 328-25);
|
||||
// }
|
||||
// else if (state->lastp25type == 4)
|
||||
// {
|
||||
// if (opts->errorbars == 1)
|
||||
// {
|
||||
// printFrameInfo (opts, state);
|
||||
// fprintf (stderr," (PDU)\n");
|
||||
// }
|
||||
// state->lastp25type = 0;
|
||||
// }
|
||||
|
||||
else
|
||||
{
|
||||
state->lastp25type = 0;
|
||||
@@ -699,8 +628,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," duid:%s *Unknown DUID*\n", duid); //prints on dPMR frame 3
|
||||
// fprintf (stderr, "\n"); //prints on dPMR frame 3
|
||||
// fprintf (stderr," duid:%s *Unknown DUID*\n", duid);
|
||||
fprintf (stderr," duid:%s \n", duid); //DUID ERR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//start control channel hunting if using trunking, time needs updating on each successful sync
|
||||
//will need to assign frequencies to a CC array for P25 since that isn't imported from CSV
|
||||
if (state->dmr_rest_channel == -1 && opts->p25_is_tuned == 0 && opts->p25_trunk == 1 && ( (time(NULL) - state->last_cc_sync_time) > (opts->trunk_hangtime + 2) ) )
|
||||
if (state->dmr_rest_channel == -1 && opts->p25_is_tuned == 0 && opts->p25_trunk == 1 && ( (time(NULL) - state->last_cc_sync_time) > (opts->trunk_hangtime + 0) ) ) //was 3, go to hangtime value
|
||||
{
|
||||
|
||||
//test to switch back to 10/4 P1 QPSK for P25 FDMA CC
|
||||
@@ -163,10 +163,23 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
//start going through the lcn/frequencies CC/signal hunting
|
||||
fprintf (stderr, "Control Channel Signal Lost. Searching for Control Channel.\n");
|
||||
//make sure our current roll value doesn't exceed value of frequencies imported
|
||||
if (state->lcn_freq_roll > state->lcn_freq_count)
|
||||
if (state->lcn_freq_roll >= state->lcn_freq_count) //fixed this to skip the extra wait out at the end of the list
|
||||
{
|
||||
state->lcn_freq_roll = 0; //reset to zero
|
||||
}
|
||||
//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)
|
||||
{
|
||||
@@ -182,7 +195,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
rtl_udp_tune (opts, state, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
||||
}
|
||||
|
||||
fprintf (stderr, "Tuning to Control Channel Frequency: %.06lf MHz\n",
|
||||
fprintf (stderr, "Tuning to Frequency: %.06lf MHz\n",
|
||||
(double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000);
|
||||
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support for NXDN
|
||||
int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support
|
||||
{
|
||||
char filename[1024] = "filename.csv";
|
||||
sprintf (filename, "%s", opts->key_in_file);
|
||||
@@ -160,7 +160,13 @@ int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support for NXD
|
||||
}
|
||||
int row_count = 0;
|
||||
int field_count = 0;
|
||||
unsigned long long int keynumber;
|
||||
|
||||
unsigned long long int keynumber = 0;
|
||||
unsigned long long int keyvalue = 0;
|
||||
|
||||
uint16_t hash = 0;
|
||||
uint8_t hash_bits[24];
|
||||
memset (hash_bits, 0, sizeof(hash_bits));
|
||||
|
||||
while (fgets(buffer, BSIZE, fp)) {
|
||||
field_count = 0;
|
||||
@@ -173,11 +179,24 @@ int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support for NXD
|
||||
if (field_count == 0)
|
||||
{
|
||||
sscanf (field, "%lld", &keynumber);
|
||||
if (keynumber > 0xFFFF) //if larger than 16-bits, get its hash instead
|
||||
{
|
||||
keynumber = keynumber & 0xFFFFFF; //truncate to 24-bits (max allowed)
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
hash_bits[i] = ((keynumber << i) & 0x800000) >> 23; //load into array for CRC16
|
||||
}
|
||||
hash = ComputeCrcCCITT16d (hash_bits, 24);
|
||||
keynumber = hash & 0xFFFF; //make sure its no larger than 16-bits
|
||||
fprintf (stderr, "Hashed ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (field_count == 1)
|
||||
{
|
||||
sscanf (field, "%lld", &state->rkey_array[keynumber]);
|
||||
sscanf (field, "%lld", &keyvalue);
|
||||
state->rkey_array[keynumber] = keyvalue & 0xFFFFFFFFFF; //doesn't exceed 40-bit value
|
||||
}
|
||||
|
||||
field = strtok(NULL, ",");
|
||||
@@ -185,6 +204,7 @@ int csvKeyImport(dsd_opts * opts, dsd_state * state) //multi-key support for NXD
|
||||
}
|
||||
fprintf (stderr, "Key [%03lld] [%05lld]", keynumber, state->rkey_array[keynumber]);
|
||||
fprintf (stderr, "\n");
|
||||
hash = 0;
|
||||
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
375
src/dsd_main.c
375
src/dsd_main.c
@@ -52,11 +52,10 @@ char * FM_banner[9] = {
|
||||
" ██║ ██║ ╚═══██╗██║ ██║ ██╔══╝ ██║╚██╔╝██║██╔══╝ ",
|
||||
" ██████╔╝██████╔╝██████╔╝ ██║ ██║ ╚═╝ ██║███████╗",
|
||||
" ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝",
|
||||
" 'Lite' Edition v2.0.0-28-ga02f992 Windows 32-bit RC3 "
|
||||
" 'Aero' Edition v2.0.0-84-gacd2b0b Windows 32-bit "
|
||||
};
|
||||
|
||||
int
|
||||
comp (const void *a, const void *b)
|
||||
int comp (const void *a, const void *b)
|
||||
{
|
||||
if (*((const int *) a) == *((const int *) b))
|
||||
return 0;
|
||||
@@ -78,6 +77,67 @@ void
|
||||
noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
// // experimental conventional frequency scanner mode
|
||||
// if (opts->scanner_mode == 1)
|
||||
// {
|
||||
|
||||
// if (state->lcn_freq_roll > state->lcn_freq_count) //with >= we were cutting one short
|
||||
// {
|
||||
// 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]);
|
||||
// }
|
||||
// //rtludp
|
||||
// if (opts->audio_in_type == 3)
|
||||
// {
|
||||
// rtl_udp_tune (opts, state, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
||||
// }
|
||||
|
||||
// if (state->lastsynctype != -1) fprintf (stderr, "Resume Scanning Mode\n");
|
||||
|
||||
// }
|
||||
// state->lcn_freq_roll++;
|
||||
// }
|
||||
// //end experimental conventional frequency scanner mode
|
||||
|
||||
//experimental conventional frequency scanner mode
|
||||
if (opts->scanner_mode == 1 && ( (time(NULL) - state->last_cc_sync_time) > opts->trunk_hangtime))
|
||||
{
|
||||
|
||||
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]);
|
||||
}
|
||||
//rtludp
|
||||
if (opts->audio_in_type == 3)
|
||||
{
|
||||
rtl_udp_tune (opts, state, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
||||
}
|
||||
|
||||
if (state->lastsynctype != -1) fprintf (stderr, "Resume Scanning Mode\n");
|
||||
|
||||
}
|
||||
state->lcn_freq_roll++;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
}
|
||||
//end experimental conventional frequency scanner mode
|
||||
|
||||
//tune back to last known CC when using trunking after x second hangtime
|
||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && ( (time(NULL) - state->last_cc_sync_time) > opts->trunk_hangtime) )
|
||||
{
|
||||
@@ -174,6 +234,12 @@ noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
state->nxdn_location_sys_code = 0;
|
||||
sprintf (state->nxdn_location_category, "%s", " ");
|
||||
|
||||
//channel access information
|
||||
state->nxdn_rcn = 0;
|
||||
state->nxdn_base_freq = 0;
|
||||
state->nxdn_step = 0;
|
||||
state->nxdn_bw = 0;
|
||||
|
||||
//dmr mfid branding and site parms
|
||||
sprintf(state->dmr_branding_sub, "%s", "");
|
||||
sprintf(state->dmr_branding, "%s", "");
|
||||
@@ -255,12 +321,12 @@ noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
state->data_header_blocks[1] = 1; //when trying to fill the superframe and 0-1 blocks give us an overflow
|
||||
state->data_header_padding[0] = 0;
|
||||
state->data_header_padding[1] = 0;
|
||||
state->data_header_format[0] = 0;
|
||||
state->data_header_format[1] = 0;
|
||||
state->data_header_format[0] = 7;
|
||||
state->data_header_format[1] = 7;
|
||||
state->data_header_sap[0] = 0;
|
||||
state->data_header_sap[1] = 0;
|
||||
state->data_block_counter[0] = 0;
|
||||
state->data_block_counter[1] = 0;
|
||||
state->data_block_counter[0] = 1;
|
||||
state->data_block_counter[1] = 1;
|
||||
state->data_p_head[0] = 0;
|
||||
state->data_p_head[1] = 0;
|
||||
|
||||
@@ -289,7 +355,7 @@ noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
state->nxdn_sf = 0;
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
|
||||
state->nxdn_sacch_non_superframe = TRUE;
|
||||
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
||||
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||
state->nxdn_alias_block_number = 0;
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
sprintf (state->nxdn_call_type, "%s", "");
|
||||
@@ -303,7 +369,11 @@ noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
//initialize unified dmr pdu 'superframe'
|
||||
memset (state->dmr_pdu_sf, 0, sizeof (state->dmr_pdu_sf));
|
||||
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||
memset (state->data_header_valid, 0, sizeof(state->data_header_valid));
|
||||
|
||||
//initialize cap+ bits and block num storage
|
||||
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||
state->cap_plus_block_num = 0;
|
||||
|
||||
//init confirmed data individual block crc as invalid
|
||||
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
|
||||
@@ -498,7 +568,9 @@ initOpts (dsd_opts * opts)
|
||||
|
||||
opts->p25_trunk = 0; //0 disabled, 1 is enabled
|
||||
opts->p25_is_tuned = 0; //set to 1 if currently on VC, set back to 0 on carrier drop
|
||||
opts->trunk_hangtime = 1; //1 second hangtime by default before tuning back to CC
|
||||
opts->trunk_hangtime = 1; //1 second hangtime by default before tuning back to CC, going sub 1 sec causes issues with cc slip
|
||||
|
||||
opts->scanner_mode = 0; //0 disabled, 1 is enabled
|
||||
|
||||
//reverse mute
|
||||
opts->reverse_mute = 0;
|
||||
@@ -736,12 +808,12 @@ initState (dsd_state * state)
|
||||
state->data_header_blocks[1] = 1; //when trying to fill the superframe and 0-1 blocks give us an overflow
|
||||
state->data_header_padding[0] = 0;
|
||||
state->data_header_padding[1] = 0;
|
||||
state->data_header_format[0] = 0;
|
||||
state->data_header_format[1] = 0;
|
||||
state->data_header_format[0] = 7;
|
||||
state->data_header_format[1] = 7;
|
||||
state->data_header_sap[0] = 0;
|
||||
state->data_header_sap[1] = 0;
|
||||
state->data_block_counter[0] = 0;
|
||||
state->data_block_counter[1] = 0;
|
||||
state->data_block_counter[0] = 1;
|
||||
state->data_block_counter[1] = 1;
|
||||
state->data_p_head[0] = 0;
|
||||
state->data_p_head[1] = 0;
|
||||
|
||||
@@ -812,16 +884,24 @@ initState (dsd_state * state)
|
||||
state->nxdn_sf = 0;
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
|
||||
state->nxdn_sacch_non_superframe = TRUE;
|
||||
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
||||
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||
state->nxdn_alias_block_number = 0;
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
|
||||
//site/srv/cch info
|
||||
state->nxdn_location_site_code = 0;
|
||||
state->nxdn_location_sys_code = 0;
|
||||
sprintf (state->nxdn_location_category, "%s", " ");
|
||||
|
||||
//channel access information
|
||||
state->nxdn_rcn = 0;
|
||||
state->nxdn_base_freq = 0;
|
||||
state->nxdn_step = 0;
|
||||
state->nxdn_bw = 0;
|
||||
|
||||
//multi-key array
|
||||
memset (state->rkey_array, 0, sizeof(state->rkey_array));
|
||||
memset (state->rkey_array, 0, sizeof(state->rkey_array));
|
||||
state->keyloader = 0; //keyloader off
|
||||
|
||||
//Remus DMR End Call Alert Beep
|
||||
state->dmr_end_alert[0] = 0;
|
||||
@@ -833,7 +913,11 @@ initState (dsd_state * state)
|
||||
|
||||
//initialize unified dmr pdu 'superframe'
|
||||
memset (state->dmr_pdu_sf, 0, sizeof (state->dmr_pdu_sf));
|
||||
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||
memset (state->data_header_valid, 0, sizeof(state->data_header_valid));
|
||||
|
||||
//initialize cap+ bits and block num storage
|
||||
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||
state->cap_plus_block_num = 0;
|
||||
|
||||
//init confirmed data individual block crc as invalid
|
||||
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
|
||||
@@ -877,13 +961,13 @@ usage ()
|
||||
{
|
||||
|
||||
printf ("\n");
|
||||
printf ("Usage: dsd-fme-lite [options] Decoder/Trunking Mode\n");
|
||||
printf (" or: dsd-fme-lite [options] -r <files> Read/Play saved mbe data from file(s)\n");
|
||||
printf (" or: dsd-fme-lite -h Show help\n");
|
||||
printf ("Usage: dsd-fme-aero [options] Decoder/Trunking Mode\n");
|
||||
printf (" or: dsd-fme-aero [options] -r <files> Read/Play saved mbe data from file(s)\n");
|
||||
printf (" or: dsd-fme-aero -h Show help\n");
|
||||
printf ("\n");
|
||||
printf ("Display Options:\n");
|
||||
printf (" -N Use NCurses Terminal\n");
|
||||
printf (" dsd-fme-lite -N 2> log.ans \n");
|
||||
printf (" dsd-fme-aero -N 2> log.ans \n");
|
||||
printf (" -Z Log MBE/PDU Payloads to console\n");
|
||||
printf ("\n");
|
||||
printf ("Input/Output options:\n");
|
||||
@@ -928,7 +1012,7 @@ usage ()
|
||||
printf (" bw <num> RTL-SDR VFO Bandwidth kHz (default = 12)(6, 8, 12, 24) \n");
|
||||
printf (" sq <num> RTL-SDR Squelch Level (0 - Open, 25 - Little, 50 - Higher)\n");
|
||||
printf (" udp <num> RTL-SDR UDP Remote Port (default = 6020)\n");
|
||||
printf (" Example: dsd-fme-lite -fp -i rtl:0:851.375M:22:-2:12:0:6021\n");
|
||||
printf (" Example: dsd-fme-aero -fp -i rtl:0:851.375M:22:-2:12:0:6021\n");
|
||||
printf ("\n");
|
||||
printf ("Decoder options:\n");
|
||||
printf (" -fa Legacy Auto Detection (old methods default)\n");
|
||||
@@ -970,8 +1054,10 @@ usage ()
|
||||
//printf (" (4 Level, not 8 Level LSM) (this is honestly unknown since I can't verify what local systems are using)\n");
|
||||
printf (" -F Relax P25 Phase 2 MAC_SIGNAL CRC Checksum Pass/Fail\n");
|
||||
printf (" Use this feature to allow MAC_SIGNAL even if CRC errors.\n");
|
||||
printf (" -F Relax DMR CACH/Burst FEC or RAS/CRC CSBK Pass/Fail\n");
|
||||
printf (" -F Relax DMR RAS/CRC CSBK/DATA Pass/Fail\n");
|
||||
printf (" Enabling on some systems could lead to bad voice/data decoding if bad or marginal signal\n");
|
||||
printf (" -F Relax NXDN SACCH/FACCH/CAC/F2U CRC Pass/Fail\n");
|
||||
printf (" Not recommended on NXDN, but can be bypassed if desired.\n");
|
||||
printf ("\n");
|
||||
printf (" -K <dec> Manually Enter Basic Privacy Key (Decimal Value of Key Number)\n");
|
||||
printf ("\n");
|
||||
@@ -982,7 +1068,10 @@ usage ()
|
||||
printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n");
|
||||
printf ("\n");
|
||||
printf (" -R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)\n");
|
||||
printf (" -k <file> Import NXDN Scrambler Key List from csv file.\n");
|
||||
printf (" \n");
|
||||
printf (" -k <file> Import Key List from csv file.\n");
|
||||
printf (" Only supports NXDN, DMR Basic Privacy and **tera 10-Char (decimal value) only. \n");
|
||||
printf (" (dPMR and **tera 32/64 char not supported, DMR uses TG value as key id -- EXPERIMENTAL!!). \n");
|
||||
printf (" \n");
|
||||
printf (" -4 Force Privacy Key over FID and SVC bits \n");
|
||||
printf ("\n");
|
||||
@@ -992,6 +1081,9 @@ usage ()
|
||||
printf (" -G <file> Import Group List Allow/Block and Label from csv file.\n");
|
||||
printf (" (See group.csv for example)\n");
|
||||
printf (" -T Enable Trunking Features (NXDN/P25/EDACS/DMR) with RIGCTL/TCP or RTL Input\n");
|
||||
printf (" -Y Enable Fast Scanning Mode with RIGCTL/TCP or RTL Input (Lower z) \n");
|
||||
printf (" Experimental -- Can only scan for sync with enabled decoders, don't mix NXDN and DMR/P25!\n");
|
||||
printf (" This is not a Trunking Feature, just scans through conventional frequencies fast!\n");
|
||||
printf (" -W Use Imported Group List as a Trunking Allow/White List -- Only Tune with Mode A\n");
|
||||
printf (" -p Disable Tune to Private Calls (DMR TIII and P25)\n");
|
||||
printf (" -E Disable Tune to Group Calls (DMR TIII, Con+, Cap+ and P25)\n");
|
||||
@@ -1002,10 +1094,10 @@ usage ()
|
||||
printf (" -B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF)\n");
|
||||
printf (" P25 - 7000-12000; P25 (QPSK) - 12000; NXDN48 - 4000; DMR - 7000; EDACS/PV - 12500;\n");
|
||||
printf (" May vary based on system stregnth, etc.\n");
|
||||
printf (" -t <secs> Set Trunking VC/sync loss hangtime in seconds. (default = 1 second)\n");
|
||||
printf (" -t <secs> Set Trunking or Fast Scan VC/sync loss hangtime in seconds. (default = 1 second) (decimal values permitted) \n");
|
||||
printf ("\n");
|
||||
printf (" Trunking Example TCP: dsd-fme-lite -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans\n");
|
||||
printf (" Trunking Example RTL: dsd-fme-lite -fs -i rtl:0:450M:26:-2:8:0:6020 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans\n");
|
||||
printf (" Trunking Example TCP: dsd-fme-aero -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans\n");
|
||||
printf (" Trunking Example RTL: dsd-fme-aero -fs -i rtl:0:450M:26:-2:8:0:6020 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans\n");
|
||||
printf ("\n");
|
||||
exit (0);
|
||||
}
|
||||
@@ -1147,22 +1239,6 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state)
|
||||
exit (0);
|
||||
}
|
||||
|
||||
// void
|
||||
// sigfun (int sig) //not convinced this is truly required, maybe some environments require it?
|
||||
// {
|
||||
// exitflag = 1;
|
||||
// #ifdef USE_RTLSDR
|
||||
// // if (opts->audio_in_type == 3)
|
||||
// //rtlsdr_sighandler();
|
||||
// #endif
|
||||
// signal (SIGINT, SIG_DFL);
|
||||
// ncursesClose ();
|
||||
|
||||
// // #ifdef USE_RTLSDR
|
||||
// // if (opts->audio_in_type == 3) rtlsdr_sighandler();
|
||||
// // #endif
|
||||
// }
|
||||
|
||||
double atofs(char *s)
|
||||
{
|
||||
char last;
|
||||
@@ -1217,9 +1293,8 @@ main (int argc, char **argv)
|
||||
InitAllFecFunction();
|
||||
|
||||
exitflag = 0;
|
||||
// signal (SIGINT, sigfun);
|
||||
|
||||
while ((c = getopt (argc, argv, "haepPqs:t:v:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:L:VU:Y:K:H:X:NQ:WrlZTF01:2:345:6:7:89:Ek:")) != -1)
|
||||
while ((c = getopt (argc, argv, "haepPqs:t:v:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:L:VU:YK:H:X:NQ:WrlZTF01:2:345:6:7:89:Ek:")) != -1)
|
||||
{
|
||||
opterr = 0;
|
||||
switch (c)
|
||||
@@ -1231,16 +1306,20 @@ main (int argc, char **argv)
|
||||
opts.call_alert = 1;
|
||||
break;
|
||||
|
||||
//Free'd up switches include Q,Y,z
|
||||
//Free'd up switches include
|
||||
//make sure to put a colon : after each if they need an argument
|
||||
//or remove colon if no argument required
|
||||
|
||||
//Disabled the Serial Port Dev and Baud Rate, etc, If somebody uses that function, sorry...
|
||||
case 'Y': //conventional scanner mode
|
||||
opts.scanner_mode = 1; //enable scanner
|
||||
opts.p25_trunk = 0; //turn off trunking mode if user enabled it
|
||||
break;
|
||||
|
||||
case 'k': //NXDN multi-key loader
|
||||
strncpy(opts.key_in_file, optarg, 1023);
|
||||
opts.key_in_file[1023] = '\0';
|
||||
csvKeyImport(&opts, &state);
|
||||
state.keyloader = 1;
|
||||
break;
|
||||
|
||||
case 'Q': //'DSP' Structured Output file for OKDMRlib
|
||||
@@ -1323,6 +1402,7 @@ main (int argc, char **argv)
|
||||
case 'T': //new letter assignment for trunking, flow down to allow temp numbers
|
||||
case '3':
|
||||
opts.p25_trunk = 1;
|
||||
opts.scanner_mode = 0; //turn off scanner mode if user enabled it
|
||||
break;
|
||||
|
||||
case 'U': //New letter assignment for RIGCTL TCP port, flow down to allow temp numbers
|
||||
@@ -1331,9 +1411,14 @@ main (int argc, char **argv)
|
||||
if (opts.rigctlportno != 0) opts.use_rigctl = 1;
|
||||
break;
|
||||
|
||||
//NOTE: I changed trunk_hangtime to a float, BUT! time(NULL) returns in second whole numbers
|
||||
//so using anything but whole numbers won't affect the outcome (rounded up?), in the future though,
|
||||
//may change to a different return on sync times and this will matter!
|
||||
|
||||
case 't': //New letter assignment for Trunk Hangtime, flow down to allow temp numbers
|
||||
case '6': //hangtime in seconds, default is 1;
|
||||
sscanf (optarg, "%d", &opts.trunk_hangtime);
|
||||
case '6': //hangtime in seconds, default is 1;
|
||||
sscanf (optarg, "%f", &opts.trunk_hangtime); //updated for float/decimal values
|
||||
fprintf (stderr, "Trunking or Fast Scanner Hang Time set to: %.02f sec\n", opts.trunk_hangtime);
|
||||
break;
|
||||
|
||||
case 'q': //New letter assignment for Reverse Mute, flow down to allow temp numbers
|
||||
@@ -1348,41 +1433,6 @@ main (int argc, char **argv)
|
||||
if (opts.setmod_bw > 25000) opts.setmod_bw = 25000; //not too high
|
||||
break;
|
||||
|
||||
// case 'e':
|
||||
// opts.errorbars = 1;
|
||||
// opts.datascope = 0;
|
||||
// break;
|
||||
// case 'p':
|
||||
// if (optarg[0] == 'e')
|
||||
// {
|
||||
// opts.p25enc = 1;
|
||||
// }
|
||||
// else if (optarg[0] == 'l')
|
||||
// {
|
||||
// opts.p25lc = 1;
|
||||
// }
|
||||
// else if (optarg[0] == 's')
|
||||
// {
|
||||
// opts.p25status = 1;
|
||||
// }
|
||||
// else if (optarg[0] == 't')
|
||||
// {
|
||||
// opts.p25tg = 1;
|
||||
// }
|
||||
// else if (optarg[0] == 'u')
|
||||
// {
|
||||
// opts.unmute_encrypted_p25 = 1;
|
||||
// fprintf(stderr, "P25 Encrypted Audio Unmuted\n");
|
||||
// }
|
||||
// break;
|
||||
// case 'P':
|
||||
// sscanf (optarg, "%d", &opts.rtlsdr_ppm_error);
|
||||
// break;
|
||||
// case 'q':
|
||||
// opts.errorbars = 0;
|
||||
// opts.verbose = 0;
|
||||
// break;
|
||||
|
||||
case 's':
|
||||
sscanf (optarg, "%d", &opts.wav_sample_rate);
|
||||
opts.wav_interpolator = opts.wav_sample_rate / opts.wav_decimator;
|
||||
@@ -1390,12 +1440,6 @@ main (int argc, char **argv)
|
||||
state.symbolCenter = state.symbolCenter * opts.wav_interpolator;
|
||||
break;
|
||||
|
||||
// case 't':
|
||||
// opts.symboltiming = 1;
|
||||
// opts.errorbars = 1;
|
||||
// opts.datascope = 0;
|
||||
// break;
|
||||
|
||||
case 'v':
|
||||
sscanf (optarg, "%d", &opts.verbose);
|
||||
break;
|
||||
@@ -1421,17 +1465,9 @@ main (int argc, char **argv)
|
||||
|
||||
case 'R':
|
||||
sscanf (optarg, "%lld", &state.R);
|
||||
if (state.R > 0x7FFF)
|
||||
{
|
||||
state.R = 0x7FFF;
|
||||
}
|
||||
// opts.dmr_mute_encL = 0;
|
||||
// opts.dmr_mute_encR = 0;
|
||||
if (state.R == 0)
|
||||
{
|
||||
// opts.dmr_mute_encL = 1;
|
||||
// opts.dmr_mute_encR = 1;
|
||||
}
|
||||
if (state.R > 0x7FFF) state.R = 0x7FFF;
|
||||
//disable keyloader in case user tries to use this and it at the same time
|
||||
state.keyloader = 0;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
@@ -1474,30 +1510,6 @@ main (int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
|
||||
// case 'G': //Set rtl device gain
|
||||
// sscanf (optarg, "%d", &opts.rtl_gain_value); //multiple value by ten to make it consitent with the way rtl_fm really works
|
||||
// break;
|
||||
|
||||
// case 'L': //Set rtl squelch level
|
||||
// sscanf (optarg, "%d", &opts.rtl_squelch_level); //set squelch by user to prevent false positives on NXDN and others
|
||||
// break;
|
||||
|
||||
// case 'V': //Set rtl voltage level
|
||||
// sscanf (optarg, "%d", &opts.rtl_volume_multiplier); //set 'volume' multiplier for rtl-sdr samples
|
||||
// break;
|
||||
|
||||
// case 'U': //Set rtl udp remote port
|
||||
// sscanf (optarg, "%d", &opts.rtl_udp_port); //set udp port number for RTL remote
|
||||
// break;
|
||||
|
||||
// case 'D': //Set rtl device index number
|
||||
// sscanf (optarg, "%d", &opts.rtl_dev_index);
|
||||
// break;
|
||||
|
||||
// case 'Y': //Set rtl VFO bandwidth --recommend 6, 12, 24, 36, 48, default 48? or 12?
|
||||
// sscanf (optarg, "%d", &opts.rtl_bandwidth);
|
||||
// break;
|
||||
|
||||
case 'N':
|
||||
opts.use_ncurses_terminal = 1;
|
||||
fprintf (stderr,"Enabling NCurses Terminal.\n");
|
||||
@@ -1537,22 +1549,11 @@ main (int argc, char **argv)
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
//fprintf (stderr,"DMR Stereo Aggressive Resync Disabled!\n");
|
||||
fprintf (stderr, "Relax P25 Phase 2 MAC_SIGNAL CRC Checksum Pass/Fail\n");
|
||||
fprintf (stderr, "Relax DMR CACH/Burst FEC or RAS/CRC CSBK Pass/Fail\n");
|
||||
fprintf (stderr, "Relax DMR RAS/CRC CSBK/DATA Pass/Fail\n");
|
||||
fprintf (stderr, "Relax NXDN SACCH/FACCH/CAC/F2U CRC Pass/Fail\n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
break;
|
||||
|
||||
// case 'z':
|
||||
// sscanf (optarg, "%d", &opts.scoperate);
|
||||
// opts.errorbars = 0;
|
||||
// opts.p25enc = 0;
|
||||
// opts.p25lc = 0;
|
||||
// opts.p25status = 0;
|
||||
// opts.p25tg = 0;
|
||||
// opts.datascope = 1;
|
||||
// opts.symboltiming = 0;
|
||||
// fprintf (stderr,"Setting datascope frame rate to %i frame per second.\n", opts.scoperate);
|
||||
// break;
|
||||
|
||||
case 'i':
|
||||
strncpy(opts.audio_in_dev, optarg, 2047);
|
||||
opts.audio_in_dev[2047] = '\0';
|
||||
@@ -1574,12 +1575,7 @@ main (int argc, char **argv)
|
||||
// fprintf (stderr,"Writing mbe data temporarily disabled!\n");
|
||||
break;
|
||||
|
||||
// case 'c':
|
||||
// opts.rtlsdr_center_freq = (uint32_t)atofs(optarg);
|
||||
// fprintf (stderr,"Tuning to frequency: %i Hz\n", opts.rtlsdr_center_freq);
|
||||
// break;
|
||||
|
||||
case 'c': //now is symbol capture bin output like FME Lite
|
||||
case 'c':
|
||||
strncpy(opts.symbol_out_file, optarg, 1023);
|
||||
opts.symbol_out_file[1023] = '\0';
|
||||
fprintf (stderr,"Writing + Appending symbol capture to file %s\n", opts.symbol_out_file);
|
||||
@@ -1617,15 +1613,6 @@ main (int argc, char **argv)
|
||||
openWavOutFile (&opts, &state);
|
||||
break;
|
||||
|
||||
// case 'B':
|
||||
// sscanf (optarg, "%d", &opts.serial_baud);
|
||||
// break;
|
||||
|
||||
// case 'C':
|
||||
// strncpy(opts.serial_dev, optarg, 1023);
|
||||
// opts.serial_dev[1023] = '\0';
|
||||
// break;
|
||||
|
||||
case 'f':
|
||||
if (optarg[0] == 'a')
|
||||
{
|
||||
@@ -1644,7 +1631,7 @@ main (int argc, char **argv)
|
||||
opts.dmr_stereo = 1; //switching in 'stereo' for 'mono'
|
||||
opts.dmr_mono = 0;
|
||||
state.dmr_stereo = 0;
|
||||
opts.setmod_bw = 7000;
|
||||
//opts.setmod_bw = 7000;
|
||||
sprintf (opts.output_name, "Legacy Auto");
|
||||
}
|
||||
else if (optarg[0] == 'd')
|
||||
@@ -1711,7 +1698,7 @@ main (int argc, char **argv)
|
||||
opts.dmr_stereo = 0;
|
||||
opts.dmr_mono = 0;
|
||||
state.dmr_stereo = 0;
|
||||
opts.setmod_bw = 12500;
|
||||
// opts.setmod_bw = 12500;
|
||||
sprintf (opts.output_name, "EDACS/PV");
|
||||
fprintf (stderr,"Setting symbol rate to 9600 / second\n");
|
||||
fprintf (stderr,"Decoding only ProVoice frames.\n");
|
||||
@@ -1738,7 +1725,9 @@ main (int argc, char **argv)
|
||||
opts.dmr_mono = 0;
|
||||
opts.pulse_digi_rate_out = 48000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.setmod_bw = 7000;
|
||||
// opts.setmod_bw = 12000;
|
||||
opts.ssize = 36; //128 current default, fall back to old default on P1 only systems
|
||||
opts.msize = 15; //1024 current default, fall back to old default on P1 only systems
|
||||
sprintf (opts.output_name, "P25P1");
|
||||
fprintf (stderr,"Decoding only P25 Phase 1 frames.\n");
|
||||
}
|
||||
@@ -1765,7 +1754,7 @@ main (int argc, char **argv)
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
opts.dmr_mono = 0;
|
||||
opts.setmod_bw = 4000;
|
||||
// opts.setmod_bw = 4000; //causing issues
|
||||
sprintf (opts.output_name, "NXDN48");
|
||||
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
|
||||
fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n");
|
||||
@@ -1819,7 +1808,7 @@ main (int argc, char **argv)
|
||||
opts.dmr_stereo = 1;
|
||||
state.dmr_stereo = 0;
|
||||
opts.dmr_mono = 0;
|
||||
opts.setmod_bw = 7000;
|
||||
// opts.setmod_bw = 12000;
|
||||
sprintf (opts.output_name, "P25P2");
|
||||
fprintf (stderr,"Decoding P25-P2 frames C4FM or OP25 Symbol Captures!\n");
|
||||
}
|
||||
@@ -1842,7 +1831,7 @@ main (int argc, char **argv)
|
||||
state.rf_mod = 0;
|
||||
opts.dmr_stereo = 1;
|
||||
opts.dmr_mono = 0;
|
||||
opts.setmod_bw = 7000;
|
||||
// opts.setmod_bw = 7000;
|
||||
opts.pulse_digi_rate_out = 24000;
|
||||
opts.pulse_digi_out_channels = 2;
|
||||
sprintf (opts.output_name, "DMR Stereo");
|
||||
@@ -1870,7 +1859,7 @@ main (int argc, char **argv)
|
||||
state.rf_mod = 0;
|
||||
opts.dmr_stereo = 1;
|
||||
opts.dmr_mono = 0;
|
||||
opts.setmod_bw = 7000;
|
||||
// opts.setmod_bw = 12000; //safe default on both DMR and P25
|
||||
opts.pulse_digi_rate_out = 24000;
|
||||
opts.pulse_digi_out_channels = 2;
|
||||
sprintf (opts.output_name, "XDMA");
|
||||
@@ -1897,7 +1886,7 @@ main (int argc, char **argv)
|
||||
opts.dmr_stereo = 0;
|
||||
opts.dmr_mono = 0;
|
||||
state.dmr_stereo = 0;
|
||||
opts.setmod_bw = 7000;
|
||||
// opts.setmod_bw = 7000; //causing issues
|
||||
sprintf (opts.output_name, "NXDN96");
|
||||
fprintf (stderr,"Decoding only NXDN 9600 baud frames.\n");
|
||||
}
|
||||
@@ -1922,32 +1911,11 @@ main (int argc, char **argv)
|
||||
opts.dmr_mono = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0; //0
|
||||
opts.setmod_bw = 7000;
|
||||
// opts.setmod_bw = 7000;
|
||||
sprintf (opts.output_name, "DMR Mono");
|
||||
//fprintf(stderr, "Notice: DMR cannot autodetect polarity. \n Use -xr option if Inverted Signal expected.\n");
|
||||
fprintf(stderr, "Notice: DMR cannot autodetect polarity. \n Use -xr option if Inverted Signal expected.\n");
|
||||
fprintf (stderr,"Decoding only DMR Mono. \nUsing DMR Stereo '-fs' or XDMA '-ft' is highly encouraged.\n");
|
||||
//dmr stereo below
|
||||
// opts.frame_dstar = 0;
|
||||
// opts.frame_x2tdma = 0;
|
||||
// opts.frame_p25p1 = 0;
|
||||
// opts.frame_p25p2 = 0;
|
||||
// opts.inverted_p2 = 0;
|
||||
// opts.frame_nxdn48 = 0;
|
||||
// opts.frame_nxdn96 = 0;
|
||||
// opts.frame_dmr = 1;
|
||||
// opts.frame_dpmr = 0;
|
||||
// opts.frame_provoice = 0;
|
||||
// opts.frame_ysf = 0;
|
||||
// opts.mod_c4fm = 1;
|
||||
// opts.mod_qpsk = 0;
|
||||
// opts.mod_gfsk = 0;
|
||||
// state.rf_mod = 0;
|
||||
// opts.dmr_stereo = 1;
|
||||
// state.dmr_stereo = 0;
|
||||
// opts.pulse_digi_rate_out = 24000;
|
||||
// opts.pulse_digi_out_channels = 2;
|
||||
// sprintf (opts.output_name, "DMR Stereo");
|
||||
// fprintf (stderr,"Decoding DMR Stereo BS/MS Simplex\n");
|
||||
|
||||
}
|
||||
else if (optarg[0] == 'm')
|
||||
{
|
||||
@@ -1977,14 +1945,14 @@ main (int argc, char **argv)
|
||||
fprintf(stderr, "Decoding only dPMR frames.\n");
|
||||
}
|
||||
break;
|
||||
//don't mess with the modulations, doesn't really do anything meaningful anyways
|
||||
//don't mess with the modulations unless you really need to
|
||||
case 'm':
|
||||
if (optarg[0] == 'a')
|
||||
{
|
||||
// opts.mod_c4fm = 1;
|
||||
// opts.mod_qpsk = 1;
|
||||
// opts.mod_gfsk = 1;
|
||||
// state.rf_mod = 0;
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 1;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 0;
|
||||
fprintf (stderr,"Don't use the -ma switch.\n");
|
||||
}
|
||||
else if (optarg[0] == 'c')
|
||||
@@ -2009,7 +1977,7 @@ main (int argc, char **argv)
|
||||
opts.mod_qpsk = 1;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 1;
|
||||
opts.setmod_bw = 12000;
|
||||
// opts.setmod_bw = 12000;
|
||||
fprintf (stderr,"Enabling only QPSK modulation optimizations.\n");
|
||||
}
|
||||
else if (optarg[0] == '2')
|
||||
@@ -2020,9 +1988,32 @@ main (int argc, char **argv)
|
||||
state.rf_mod = 1;
|
||||
state.samplesPerSymbol = 8;
|
||||
state.symbolCenter = 3;
|
||||
opts.setmod_bw = 12000;
|
||||
// opts.setmod_bw = 12000;
|
||||
fprintf (stderr,"Enabling 6000 sps P25p2 CQPSK.\n");
|
||||
}
|
||||
//test
|
||||
else if (optarg[0] == '3')
|
||||
{
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0;
|
||||
state.samplesPerSymbol = 10;
|
||||
state.symbolCenter = 4;
|
||||
// opts.setmod_bw = 12000;
|
||||
fprintf (stderr,"Enabling 6000 sps P25p2 C4FM.\n");
|
||||
}
|
||||
else if (optarg[0] == '4')
|
||||
{
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 1;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 0;
|
||||
state.samplesPerSymbol = 8;
|
||||
state.symbolCenter = 3;
|
||||
// opts.setmod_bw = 12000;
|
||||
fprintf (stderr,"Enabling 6000 sps P25p2 all optimizations.\n");
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
sscanf (optarg, "%i", &opts.uvquality);
|
||||
|
||||
@@ -140,6 +140,11 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
unsigned long long int k;
|
||||
int x;
|
||||
|
||||
//24-bit TG to 16-bit hash
|
||||
uint32_t hash = 0;
|
||||
uint8_t hash_bits[24];
|
||||
memset (hash_bits, 0, sizeof(hash_bits));
|
||||
|
||||
int preempt = 0; //TDMA dual voice slot preemption(when using OSS output)
|
||||
|
||||
|
||||
@@ -293,6 +298,34 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||
state->errs2 = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
//EXPERIMENTAL!!
|
||||
//load basic privacy key number from array by the tg value (if not forced)
|
||||
//currently only Moto BP and Hytera 10 Char BP (converted to decimal)
|
||||
if (state->M == 0)
|
||||
{
|
||||
//see if we need to hash a value larger than 16-bits
|
||||
hash = state->lasttg & 0xFFFFFF;
|
||||
// fprintf (stderr, "TG: %lld Hash: %ld ", state->lasttg, hash);
|
||||
if (hash > 0xFFFF) //if greater than 16-bits
|
||||
{
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
hash_bits[i] = ((hash << i) & 0x800000) >> 23; //load into array for CRC16
|
||||
}
|
||||
hash = ComputeCrcCCITT16d (hash_bits, 24);
|
||||
hash = hash & 0xFFFF; //make sure its no larger than 16-bits
|
||||
// fprintf (stderr, "Hash: %d ", hash);
|
||||
}
|
||||
if (state->rkey_array[hash] != 0)
|
||||
{
|
||||
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
|
||||
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
|
||||
opts->dmr_mute_encL = 0;
|
||||
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
|
||||
}
|
||||
// else opts->dmr_mute_encL = 1; //may cause issues for manual key entry (non-csv)
|
||||
}
|
||||
|
||||
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||
(state->K > 0 && state->M == 1) )
|
||||
{
|
||||
@@ -395,6 +428,34 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||
state->errs2R = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
//EXPERIMENTAL!!
|
||||
//load basic privacy key number from array by the tg value (if not forced)
|
||||
//currently only Moto BP and Hytera 10 Char BP (converted to decimal)
|
||||
if (state->M == 0)
|
||||
{
|
||||
//see if we need to hash a value larger than 16-bits
|
||||
hash = state->lasttgR & 0xFFFFFF;
|
||||
// fprintf (stderr, "TG: %lld Hash: %ld ", state->lasttgR, hash);
|
||||
if (hash > 0xFFFF) //if greater than 16-bits
|
||||
{
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
hash_bits[i] = ((hash << i) & 0x800000) >> 23; //load into array for CRC16
|
||||
}
|
||||
hash = ComputeCrcCCITT16d (hash_bits, 24);
|
||||
hash = hash & 0xFFFF; //make sure its no larger than 16-bits
|
||||
// fprintf (stderr, "Hash: %d ", hash);
|
||||
}
|
||||
if (state->rkey_array[hash] != 0)
|
||||
{
|
||||
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
|
||||
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
|
||||
opts->dmr_mute_encR = 0;
|
||||
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
|
||||
}
|
||||
// else opts->dmr_mute_encR = 1; //may cause issues for manual key entry (non-csv)
|
||||
}
|
||||
|
||||
if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
||||
(state->K > 0 && state->M == 1) )
|
||||
{
|
||||
|
||||
1010
src/dsd_ncurses.c
1010
src/dsd_ncurses.c
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,8 @@ static const int PARITY[] = {
|
||||
//decoding functions here
|
||||
void nxdn_descramble(uint8_t dibits[], int len)
|
||||
{
|
||||
for (int i=0; i<sizeof(scramble_t); i++) {
|
||||
for (int i=0; i<len; i++)
|
||||
{
|
||||
if (scramble_t[i] >= len)
|
||||
break;
|
||||
dibits[scramble_t[i]] ^= 0x2; // invert sign of scrambled dibits
|
||||
@@ -51,13 +52,15 @@ void nxdn_descramble(uint8_t dibits[], int len)
|
||||
|
||||
void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
||||
{
|
||||
uint8_t deperm[144];
|
||||
uint8_t depunc[192];
|
||||
uint8_t trellis_buf[96];
|
||||
uint8_t deperm[200]; //144
|
||||
uint8_t depunc[200]; //192
|
||||
uint8_t trellis_buf[400]; //96
|
||||
uint16_t crc = 0; //crc calculated by function
|
||||
uint16_t check = 0; //crc from payload for comparison
|
||||
int out;
|
||||
char buf[128];
|
||||
|
||||
memset (deperm, 0, sizeof(deperm));
|
||||
memset (depunc, 0, sizeof(depunc));
|
||||
|
||||
for (int i=0; i<144; i++)
|
||||
deperm[PERM_16_9[i]] = bits[i];
|
||||
@@ -73,7 +76,7 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
||||
uint8_t temp[210];
|
||||
uint8_t s0;
|
||||
uint8_t s1;
|
||||
uint8_t m_data[13];
|
||||
uint8_t m_data[20]; //13
|
||||
memset (temp, 0, sizeof(temp));
|
||||
memset (m_data, 0, sizeof(m_data));
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
@@ -111,17 +114,23 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
//load tail 16 bits into check variable to compare vs computed crc value
|
||||
crc = crc12f (trellis_buf, 84); //80
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
check = check << 1;
|
||||
check = check + trellis_buf[80+i];
|
||||
check = check | trellis_buf[84+i]; //80
|
||||
}
|
||||
|
||||
crc = crc12f (trellis_buf, 80);
|
||||
if (crc != check)
|
||||
{
|
||||
fprintf (stderr, " FACCH");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " (CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
||||
//else if (opts->payload == 1) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
|
||||
else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
@@ -131,18 +140,23 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
||||
{
|
||||
fprintf (stderr, "[%02X]", m_data[i]);
|
||||
}
|
||||
fprintf (stderr, " - %03X %03X", crc, check);
|
||||
// fprintf (stderr, " - %03X %03X", check, crc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//new sacch
|
||||
//sacch
|
||||
void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
{
|
||||
uint8_t deperm[60];
|
||||
uint8_t depunc[72];
|
||||
uint8_t trellis_buf[32];
|
||||
uint8_t answer[26];
|
||||
//see about initializing these variables
|
||||
uint8_t deperm[200]; //60
|
||||
uint8_t depunc[200]; //72
|
||||
uint8_t trellis_buf[400]; //32
|
||||
|
||||
memset (deperm, 0, sizeof(deperm));
|
||||
memset (depunc, 0, sizeof(depunc));
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
|
||||
int o = 0;
|
||||
uint8_t crc = 0; //value computed by crc6 on payload
|
||||
uint8_t check = 0; //value pulled from last 6 bits
|
||||
@@ -171,7 +185,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
uint8_t temp[90];
|
||||
uint8_t s0;
|
||||
uint8_t s1;
|
||||
uint8_t m_data[5];
|
||||
uint8_t m_data[10]; //5
|
||||
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
@@ -188,7 +202,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
}
|
||||
|
||||
CNXDNConvolution_start();
|
||||
for (int i = 0U; i < 40U; i++)
|
||||
for (int i = 0U; i < 36U; i++) //40
|
||||
{
|
||||
s0 = temp[(2*i)];
|
||||
s1 = temp[(2*i)+1];
|
||||
@@ -197,7 +211,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
}
|
||||
|
||||
//stored as 5 bytes, will need to convert to trellis_buf after running
|
||||
CNXDNConvolution_chainback(m_data, 36U);
|
||||
CNXDNConvolution_chainback(m_data, 32U); //36
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
@@ -211,11 +225,11 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
crc = crc6(trellis_buf, 32);
|
||||
crc = crc6(trellis_buf, 26); //32
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
check = check << 1;
|
||||
check = check + trellis_buf[i+26];
|
||||
check = check | trellis_buf[i+26];
|
||||
}
|
||||
|
||||
//FIRST! If part of a non_superframe, and CRC is good, send directly to NXDN_Elements_Content_decode
|
||||
@@ -224,24 +238,43 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
||||
else fprintf (stderr, " ");
|
||||
|
||||
uint8_t nsf_sacch[26];
|
||||
uint8_t nsf_sacch[400];
|
||||
memset (nsf_sacch, 0, sizeof(nsf_sacch));
|
||||
for (int i = 0; i < 26; i++)
|
||||
{
|
||||
nsf_sacch[i] = trellis_buf[i+8];
|
||||
}
|
||||
|
||||
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch);
|
||||
if (crc == check)
|
||||
{
|
||||
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
||||
state->nxdn_last_ran = ran;
|
||||
}
|
||||
|
||||
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch);
|
||||
else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, nsf_sacch);
|
||||
|
||||
if (crc != check)
|
||||
{
|
||||
fprintf (stderr, " SACCH NSF");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " (CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "\n SACCH NSF ");
|
||||
for (int i = 0; i < 5; i++)
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", m_data[i]);
|
||||
}
|
||||
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
||||
// if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", check, crc);
|
||||
}
|
||||
|
||||
//reset the sacch field -- Github Issue #118
|
||||
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||
|
||||
}
|
||||
|
||||
@@ -251,7 +284,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
//sf and ran together are denoted as SR in the manual (more confusing acronyms)
|
||||
//sf (structure field) and RAN will always exist in first 8 bits of each SACCH, then the next 18 bits are the fragment of the superframe
|
||||
sf = (trellis_buf[0] << 1) | trellis_buf[1];
|
||||
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
||||
|
||||
if (sf == 3) part_of_frame = 0;
|
||||
else if (sf == 2) part_of_frame = 1;
|
||||
else if (sf == 1) part_of_frame = 2;
|
||||
@@ -262,12 +295,21 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
||||
else fprintf (stderr, " ");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
if (crc != check)
|
||||
{
|
||||
fprintf (stderr, " SACCH SF%d", sf);
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " (CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
//reset scrambler seed to key value on new superframe
|
||||
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
|
||||
|
||||
if (crc == 0)
|
||||
if (crc == check)
|
||||
{
|
||||
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
||||
state->nxdn_ran = state->nxdn_last_ran = ran;
|
||||
state->nxdn_sf = sf;
|
||||
state->nxdn_part_of_frame = part_of_frame;
|
||||
@@ -294,24 +336,27 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " SACCH SF Segment #%d ", part_of_frame+1);
|
||||
for (int i = 0; i < 5; i++)
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", m_data[i]);
|
||||
}
|
||||
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
||||
// if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348])
|
||||
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type)
|
||||
{
|
||||
|
||||
uint8_t deperm[348];
|
||||
uint8_t depunc[406];
|
||||
uint8_t trellis_buf[199];
|
||||
uint8_t deperm[500]; //348
|
||||
uint8_t depunc[500]; //406
|
||||
uint8_t trellis_buf[400]; //199
|
||||
int id = 0;
|
||||
uint16_t crc = 0;
|
||||
uint16_t check = 0;
|
||||
|
||||
memset (deperm, 0, sizeof(deperm));
|
||||
memset (depunc, 0, sizeof(depunc));
|
||||
|
||||
for (int i=0; i<348; i++) {
|
||||
deperm[PERM_12_29[i]] = bits[i];
|
||||
@@ -334,10 +379,10 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
|
||||
}
|
||||
|
||||
//switch to the convolutional decoder
|
||||
uint8_t temp[220];
|
||||
uint8_t temp[500]; //220, this one was way too small
|
||||
uint8_t s0;
|
||||
uint8_t s1;
|
||||
uint8_t m_data[26];
|
||||
uint8_t m_data[52]; //26
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
@@ -377,8 +422,23 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
|
||||
}
|
||||
|
||||
crc = crc15(trellis_buf, 199);
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
check = check << 1;
|
||||
check = check | trellis_buf[i+184];
|
||||
}
|
||||
|
||||
uint8_t f2u_message_buffer[199];
|
||||
if (crc != check)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
if (type == 0) fprintf (stderr, " UDCH");
|
||||
if (type == 1) fprintf (stderr, " FACCH2");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " (CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
uint8_t f2u_message_buffer[400]; //199
|
||||
memset (f2u_message_buffer, 0, sizeof(f2u_message_buffer));
|
||||
|
||||
//just going to leave this all here in case its needed, like in cac,
|
||||
@@ -388,36 +448,58 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
|
||||
f2u_message_buffer[i] = trellis_buf[i+8];
|
||||
}
|
||||
|
||||
if (crc == 0)
|
||||
if (crc == check)
|
||||
{
|
||||
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
|
||||
if (type == 0) ; //need handling for user data (text messages and AVL)
|
||||
}
|
||||
else if (opts->aggressive_framesync == 0)
|
||||
{
|
||||
fprintf (stderr, " F2/U ");
|
||||
//NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
||||
NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
|
||||
if (type == 1) NXDN_Elements_Content_decode(opts, state, 0, f2u_message_buffer);
|
||||
if (type == 0) ; //need handling for user data (text messages and AVL)
|
||||
}
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " F2/U Payload\n ");
|
||||
if (type == 0) fprintf (stderr, " UDCH");
|
||||
if (type == 1) fprintf (stderr, " FACCH2");
|
||||
fprintf (stderr, " Payload\n ");
|
||||
for (int i = 0; i < 26; i++)
|
||||
{
|
||||
if (i == 13) fprintf (stderr, "\n ");
|
||||
fprintf (stderr, "[%02X]", m_data[i]);
|
||||
if (i == 12) fprintf (stderr, "\n ");
|
||||
}
|
||||
if (crc != 0) fprintf (stderr, " CRC ERR ");
|
||||
// if (crc != check) fprintf (stderr, " CRC ERR ");
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
fprintf (stderr, "\n UDCH Data: ASCII - " );
|
||||
for (int i = 0; i < 24; i++) //remove crc portion
|
||||
{
|
||||
if (m_data[i] <= 0x7E && m_data[i] >=0x20)
|
||||
{
|
||||
fprintf (stderr, "%c", m_data[i]);
|
||||
}
|
||||
else fprintf (stderr, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
||||
{
|
||||
|
||||
uint8_t deperm[300];
|
||||
uint8_t depunc[350];
|
||||
uint8_t trellis_buf[171];
|
||||
uint8_t deperm[500]; //300
|
||||
uint8_t depunc[500]; //350
|
||||
uint8_t trellis_buf[400]; //171
|
||||
int id = 0;
|
||||
int ran = 0;
|
||||
uint16_t crc = 0;
|
||||
|
||||
memset (deperm, 0, sizeof(deperm));
|
||||
memset (depunc, 0, sizeof(depunc));
|
||||
|
||||
for (int i=0; i<300; i++) {
|
||||
deperm[PERM_12_25[i]] = bits[i];
|
||||
}
|
||||
@@ -442,7 +524,7 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
||||
uint8_t temp[360];
|
||||
uint8_t s0;
|
||||
uint8_t s1;
|
||||
uint8_t m_data[26];
|
||||
uint8_t m_data[52]; //26
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
@@ -452,11 +534,6 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
||||
temp[i] = depunc[i] << 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
temp[i+350] = 0;
|
||||
}
|
||||
|
||||
CNXDNConvolution_start();
|
||||
for (int i = 0U; i < 179U; i++)
|
||||
{
|
||||
@@ -482,30 +559,38 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
||||
|
||||
crc = crc16cac(trellis_buf, 171);
|
||||
|
||||
//message type will probably be neccesary beforehand on single/dual meessage runs
|
||||
//run message, check len, load into a seperate buffer, send to element, do same with other piece?
|
||||
uint8_t MessageType = 0;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
MessageType = MessageType << 1;
|
||||
MessageType = MessageType + trellis_buf[i+2]; //double check this on CAC
|
||||
}
|
||||
|
||||
uint8_t cac_message_buffer[171];
|
||||
uint8_t cac_message_buffer[400]; //171
|
||||
memset (cac_message_buffer, 0, sizeof(cac_message_buffer));
|
||||
|
||||
//shift the cac_message into the appropriate byte arrangement for element_decoder
|
||||
for (int i = 0; i < 160; i++) //in future, we can use this to send multiple messages to decoder, if present
|
||||
//shift the cac_message into the appropriate byte arrangement for element_decoder -- skip SR field
|
||||
for (int i = 0; i < 160; i++)
|
||||
{
|
||||
cac_message_buffer[i] = trellis_buf[i+8];
|
||||
}
|
||||
|
||||
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
||||
else fprintf (stderr, " ");
|
||||
|
||||
if (crc == 0)
|
||||
{
|
||||
fprintf (stderr, " CAC ");
|
||||
NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
|
||||
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
||||
state->nxdn_last_ran = ran;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " CAC");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
if (crc != 0)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " (CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
|
||||
else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, cac_message_buffer);
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
@@ -515,32 +600,320 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
||||
fprintf (stderr, "[%02X]", m_data[i]);
|
||||
if (i == 10) fprintf (stderr, "\n ");
|
||||
}
|
||||
if (crc != 0) fprintf (stderr, " CRC ERR ");
|
||||
// if (crc != 0) fprintf (stderr, " CRC ERR ");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Type-D "IDAS"
|
||||
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " SCCH");
|
||||
|
||||
//see about initializing these variables
|
||||
uint8_t deperm[200]; //60
|
||||
uint8_t depunc[200]; //72
|
||||
uint8_t trellis_buf[400]; //32
|
||||
|
||||
memset (deperm, 0, sizeof(deperm));
|
||||
memset (depunc, 0, sizeof(depunc));
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
|
||||
int o = 0;
|
||||
uint8_t crc = 0; //value computed by crc7 on payload
|
||||
uint8_t check = 0; //value pulled from last 7 bits
|
||||
int sf = 0;
|
||||
int part_of_frame = 0;
|
||||
|
||||
for (int i=0; i<60; i++)
|
||||
deperm[PERM_12_5[i]] = bits[i];
|
||||
for (int p=0; p<60; p+= 10) {
|
||||
depunc[o++] = deperm[p+0];
|
||||
depunc[o++] = deperm[p+1];
|
||||
depunc[o++] = deperm[p+2];
|
||||
depunc[o++] = deperm[p+3];
|
||||
depunc[o++] = deperm[p+4];
|
||||
depunc[o++] = 0;
|
||||
depunc[o++] = deperm[p+5];
|
||||
depunc[o++] = deperm[p+6];
|
||||
depunc[o++] = deperm[p+7];
|
||||
depunc[o++] = deperm[p+8];
|
||||
depunc[o++] = deperm[p+9];
|
||||
depunc[o++] = 0;
|
||||
}
|
||||
|
||||
//switch to the convolutional decoder
|
||||
uint8_t temp[90];
|
||||
uint8_t s0;
|
||||
uint8_t s1;
|
||||
uint8_t m_data[10]; //5
|
||||
|
||||
memset (temp, 0, sizeof (temp));
|
||||
memset (m_data, 0, sizeof (m_data));
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
|
||||
for (int i = 0; i < 72; i++)
|
||||
{
|
||||
temp[i] = depunc[i] << 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
temp[i+72] = 0;
|
||||
}
|
||||
|
||||
CNXDNConvolution_start();
|
||||
for (int i = 0U; i < 36U; i++)
|
||||
{
|
||||
s0 = temp[(2*i)];
|
||||
s1 = temp[(2*i)+1];
|
||||
|
||||
CNXDNConvolution_decode(s0, s1);
|
||||
}
|
||||
|
||||
CNXDNConvolution_chainback(m_data, 32U);
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
||||
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
||||
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
||||
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
||||
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
||||
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
||||
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
crc = crc7_scch(trellis_buf, 25);
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
check = check << 1;
|
||||
check = check | trellis_buf[i+25];
|
||||
}
|
||||
|
||||
//check the sf early for scrambler reset, if required
|
||||
sf = (trellis_buf[0] << 1) | trellis_buf[1];
|
||||
if (sf == 3) part_of_frame = 0;
|
||||
else if (sf == 2) part_of_frame = 1;
|
||||
else if (sf == 1) part_of_frame = 2;
|
||||
else if (sf == 0) part_of_frame = 3;
|
||||
else part_of_frame = 0;
|
||||
|
||||
//reset scrambler seed to key value on new superframe
|
||||
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
|
||||
|
||||
/*
|
||||
4.3.2. Mapping to Functional Channel
|
||||
When Subscriber Unit performs voice communication in RTCH2, SCCH is allocated as
|
||||
described below.
|
||||
|
||||
SCCH has super Frame Structure with 4 frame unit during communication, and has non
|
||||
super Frame Structure with single frame unit at the start time and end time of sending.
|
||||
Allocation of the other channel is not specified particularly.
|
||||
*/
|
||||
|
||||
//English Translation: Superframe when voice, Non Superframe when not voice
|
||||
|
||||
//What I've found is that by not using the superframe structure, and decoding each 'unit'
|
||||
//individually instead, we can get more expedient decoding on elements without having to
|
||||
//sacrifice an entire superframe for one bad CRC, also each element cleanly divides into a
|
||||
//single 'unit' except for enc parms IV, which can be stored seperately if needed
|
||||
|
||||
//NOTE: scch has its own message format, and thus, doesn't go to content element decoding
|
||||
//like everything else does
|
||||
if (crc != check)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " (CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (crc == check) NXDN_decode_scch (opts, state, trellis_buf, direction);
|
||||
else if (opts->aggressive_framesync == 0) NXDN_decode_scch (opts, state, trellis_buf, direction);
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "\n SCCH Payload ");
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", m_data[i]);
|
||||
}
|
||||
// if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", check, crc);
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type)
|
||||
{
|
||||
uint8_t deperm[300]; //144
|
||||
uint8_t depunc[300]; //192
|
||||
uint8_t trellis_buf[600]; //96
|
||||
uint8_t f3_udch2[288]; //completed bitstream without crc and tailing bits attached
|
||||
uint8_t f3_udch2_bytes[36]; //completed bytes - with crc and tail
|
||||
uint16_t crc[2]; //crc calculated by function
|
||||
uint16_t check[2]; //crc from payload for comparison
|
||||
int out;
|
||||
|
||||
//switch to the convolutional decoder
|
||||
uint8_t temp[500];
|
||||
uint8_t s0;
|
||||
uint8_t s1;
|
||||
uint8_t m_data[40]; //13
|
||||
memset (temp, 0, sizeof(temp));
|
||||
memset (m_data, 0, sizeof(m_data));
|
||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
||||
memset (deperm, 0, sizeof(deperm));
|
||||
memset (depunc, 0, sizeof(depunc));
|
||||
memset (crc, 0, sizeof(crc));
|
||||
memset (check, 0, sizeof(check));
|
||||
memset (f3_udch2, 0, sizeof(f3_udch2));
|
||||
memset (f3_udch2_bytes, 0, sizeof(f3_udch2_bytes));
|
||||
|
||||
for (int j=0; j<2; j++)
|
||||
{
|
||||
for (int i=0; i<144; i++)
|
||||
deperm[PERM_16_9[i]] = bits[i+(j*144)];
|
||||
out = 0;
|
||||
for (int i=0; i<144; i+=3) {
|
||||
depunc[out++] = deperm[i+0];
|
||||
depunc[out++] = 0;
|
||||
depunc[out++] = deperm[i+1];
|
||||
depunc[out++] = deperm[i+2];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 192; i++)
|
||||
{
|
||||
temp[i] = depunc[i] << 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
temp[i+192] = 0;
|
||||
}
|
||||
|
||||
CNXDNConvolution_start();
|
||||
for (int i = 0U; i < 100U; i++)
|
||||
{
|
||||
s0 = temp[(2*i)];
|
||||
s1 = temp[(2*i)+1];
|
||||
|
||||
CNXDNConvolution_decode(s0, s1);
|
||||
}
|
||||
|
||||
CNXDNConvolution_chainback(m_data, 96U);
|
||||
|
||||
for(int i = 0; i < 12; i++)
|
||||
{
|
||||
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
||||
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
||||
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
||||
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
||||
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
||||
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
||||
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
||||
}
|
||||
|
||||
crc[j] = crc12f (trellis_buf, 84); //84
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
check[j] = check[j] << 1;
|
||||
check[j] = check[j] | trellis_buf[84+i]; //84
|
||||
|
||||
}
|
||||
//transfer to storage sans crc and tail bits
|
||||
for (int i = 0; i < 80; i++) f3_udch2[i+(j*80)] = trellis_buf[i];
|
||||
for (int i = 0; i < 12; i++) f3_udch2_bytes[i+(j*12)] = m_data[i];
|
||||
}
|
||||
|
||||
if (crc[0] != check[0] || crc[1] != check[1])
|
||||
{
|
||||
if (type == 0) fprintf (stderr, " UDCH2");
|
||||
if (type == 1) fprintf (stderr, " FACCH3");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " (CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (crc[0] == check[0] && crc[1] == check[1])
|
||||
{
|
||||
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
||||
if (type == 0) ; //need handling for user data (text messages and AVL)
|
||||
}
|
||||
else if (opts->aggressive_framesync == 0)
|
||||
{
|
||||
if (type == 1) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
|
||||
if (type == 0) ; //need handling for user data (text messages and AVL)
|
||||
}
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
if (type == 0) fprintf (stderr, " UDCH2");
|
||||
if (type == 1) fprintf (stderr, " FACCH3");
|
||||
fprintf (stderr, " Payload \n ");
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
if (i == 12) fprintf (stderr, "\n ");
|
||||
fprintf (stderr, "[%02X]", f3_udch2_bytes[i]);
|
||||
}
|
||||
// fprintf (stderr, " - %03X %03X", check[0], crc[0]);
|
||||
// fprintf (stderr, " - %03X %03X", check[1], crc[1]);
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
fprintf (stderr, "\n UDCH2 Data: ASCII - " );
|
||||
for (int i = 0; i < 22; i++) //all but last crc portion
|
||||
{
|
||||
if (i == 12) i = 14; //skip first crc portion
|
||||
if (f3_udch2_bytes[i] <= 0x7E && f3_udch2_bytes[i] >=0x20)
|
||||
{
|
||||
fprintf (stderr, "%c", f3_udch2_bytes[i]);
|
||||
}
|
||||
else fprintf (stderr, " ");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
||||
{
|
||||
|
||||
//NOTE: Most Req/Resp (request and respone) share same message type but differ depending on channel type
|
||||
//RTCH Outbound will take precedent when differences may occur (except CALL_ASSGN)
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
if (MessageType == 0x10) fprintf(stderr, " IDLE");
|
||||
else if (MessageType == 0x11) fprintf(stderr, " DISC"); //disconnect
|
||||
else if (MessageType == 0x00) fprintf(stderr, " CALL_RESP");
|
||||
else if (MessageType == 0x01) fprintf(stderr, " VCALL");
|
||||
else if (MessageType == 0x03) fprintf(stderr, " VCALL IV");
|
||||
|
||||
else if (MessageType == 0x07) fprintf(stderr, " TX_REL_EX");
|
||||
else if (MessageType == 0x08) fprintf(stderr, " TX_REL");
|
||||
|
||||
else if (MessageType == 0x02) fprintf(stderr, " VCALL_REC_REQ");
|
||||
else if (MessageType == 0x03) fprintf(stderr, " VCALL_IV");
|
||||
else if (MessageType == 0x04) fprintf(stderr, " VCALL_ASSGN");
|
||||
else if (MessageType == 0x05) fprintf(stderr, " VCALL_ASSGN_DUP");
|
||||
else if (MessageType == 0x06) fprintf(stderr, " CALL_CONN_RESP");
|
||||
else if (MessageType == 0x07) fprintf(stderr, " TX_REL_EX");
|
||||
else if (MessageType == 0x08) fprintf(stderr, " TX_REL");
|
||||
else if (MessageType == 0x09) fprintf(stderr, " DCALL_HEADER");
|
||||
else if (MessageType == 0x0A) fprintf(stderr, " DCALL_REC_REQ");
|
||||
else if (MessageType == 0x0B) fprintf(stderr, " DCALL_UDATA");
|
||||
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
|
||||
else if (MessageType == 0x0D) fprintf(stderr, " DCALL_ASSGN_DUP");
|
||||
else if (MessageType == 0x0E) fprintf(stderr, " DCALL_ASSGN");
|
||||
else if (MessageType == 0x11) fprintf(stderr, " DISC");
|
||||
else if (MessageType == 0x17) fprintf(stderr, " DST_ID_INFO");
|
||||
else if (MessageType == 0x18) fprintf(stderr, " SITE_INFO");
|
||||
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
|
||||
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO");
|
||||
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
|
||||
else if (MessageType == 0x1A) fprintf(stderr, " CCH_INFO");
|
||||
else if (MessageType == 0x1B) fprintf(stderr, " ADJ_SITE_INFO");
|
||||
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO");
|
||||
else if (MessageType == 0x20) fprintf(stderr, " REG_RESP");
|
||||
else if (MessageType == 0x22) fprintf(stderr, " REG_C_RESP");
|
||||
else if (MessageType == 0x24) fprintf(stderr, " GRP_REG_RESP");
|
||||
@@ -549,24 +922,22 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
||||
else if (MessageType == 0x38) fprintf(stderr, " SDCALL_REQ_HEADER");
|
||||
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
|
||||
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
|
||||
|
||||
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
|
||||
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
|
||||
else fprintf(stderr, " Unknown M-%02X", MessageType);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//zero out stale values so they won't persist after a transmit release
|
||||
//disable if random messages wipe out the alias
|
||||
if (MessageType == 0x08 || MessageType == 0x10) //idle, tx_rel
|
||||
//Zero out stale values on DISC or TX_REL only (IDLE messaages occur often on NXDN96 VCH, and randomly on Type-C FACCH1 steals for some reason)
|
||||
if (MessageType == 0x08 || MessageType == 0x11)
|
||||
{
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
state->nxdn_last_rid = 0;
|
||||
state->nxdn_last_tg = 0;
|
||||
state->nxdn_cipher_type = 0;
|
||||
if (state->M == 0) state->nxdn_cipher_type = 0;
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
||||
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||
sprintf (state->nxdn_call_type, "%s", "");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//voice descrambler
|
||||
@@ -669,13 +1040,32 @@ static uint16_t crc15(const uint8_t buf[], int len)
|
||||
|
||||
static uint16_t crc16cac(const uint8_t buf[], int len)
|
||||
{
|
||||
int crc = 0xc3ee;
|
||||
int poly = (1<<12) + (1<<5) + 1;
|
||||
for (int i=0;i<len;i++) {
|
||||
crc = ((crc << 1) | buf[i]) & 0x1ffff;
|
||||
if(crc & 0x10000)
|
||||
crc = (crc & 0xffff) ^ poly;
|
||||
uint32_t crc = 0xc3ee; //not sure why this though
|
||||
uint32_t poly = (1<<12) + (1<<5) + 1; //poly is fine
|
||||
for (int i=0;i<len;i++)
|
||||
{
|
||||
crc = ((crc << 1) | buf[i]) & 0x1ffff;
|
||||
if(crc & 0x10000) crc = (crc & 0xffff) ^ poly;
|
||||
}
|
||||
crc = crc ^ 0xffff;
|
||||
return crc & 0xffff;
|
||||
crc = crc ^ 0xffff;
|
||||
return crc & 0xffff;
|
||||
}
|
||||
|
||||
static uint8_t crc7_scch(uint8_t bits[], int len)
|
||||
{
|
||||
uint8_t s[7];
|
||||
uint8_t a;
|
||||
for (int i=0;i<7;i++)
|
||||
s[i] = 1;
|
||||
for (int i=0;i<len;i++) {
|
||||
a = bits[i] ^ s[0];
|
||||
s[0] = s[1];
|
||||
s[1] = s[2];
|
||||
s[2] = s[3];
|
||||
s[3] = a ^ s[4];
|
||||
s[4] = s[5];
|
||||
s[5] = s[6];
|
||||
s[6] = a;
|
||||
}
|
||||
return load_i(s, 7);
|
||||
}
|
||||
1218
src/nxdn_element.c
1218
src/nxdn_element.c
File diff suppressed because it is too large
Load Diff
295
src/nxdn_frame.c
295
src/nxdn_frame.c
@@ -32,53 +32,73 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
// length is implicitly 192, with frame sync in first 10 dibits
|
||||
uint8_t dbuf[182];
|
||||
uint8_t lich;
|
||||
int answer_len=0;
|
||||
int answer_len = 0;
|
||||
uint8_t answer[32];
|
||||
uint8_t sacch_answer[32];
|
||||
int lich_parity_received;
|
||||
int lich_parity_computed;
|
||||
int voice=0;
|
||||
int facch=0;
|
||||
int facch2=0;
|
||||
int sacch=0;
|
||||
int cac=0;
|
||||
int voice = 0;
|
||||
int facch = 0;
|
||||
int facch2 = 0;
|
||||
int udch = 0;
|
||||
int sacch = 0;
|
||||
int cac = 0;
|
||||
int sr_structure;
|
||||
int sr_ran;
|
||||
|
||||
uint8_t lich_dibits[8] = {0};
|
||||
uint8_t sacch_bits[60] = {0};
|
||||
uint8_t facch_bits_a[144] = {0};
|
||||
uint8_t facch_bits_b[144] = {0};
|
||||
uint8_t cac_bits[300] = {0};
|
||||
uint8_t facch2_bits[348] = {0}; //facch2 or udch, same amount of bits
|
||||
//new, and even more confusing NXDN Type-D / "IDAS" acronyms
|
||||
int idas = 0;
|
||||
int scch = 0;
|
||||
int facch3 = 0;
|
||||
int udch2 = 0;
|
||||
|
||||
//nxdn bit buffer, for easy assignment handling
|
||||
int nxdn_bit_buffer[364] = {0};
|
||||
int nxdn_dibit_buffer[182] = {0};
|
||||
//new breakdown of lich codes
|
||||
uint8_t lich_rf = 0; //RF Channel Type
|
||||
uint8_t lich_fc = 0; //Functional Channel Type
|
||||
uint8_t lich_op = 0; //Options
|
||||
uint8_t direction; //inbound or outbound direction
|
||||
|
||||
uint8_t lich_dibits[8];
|
||||
uint8_t sacch_bits[60];
|
||||
uint8_t facch_bits_a[144];
|
||||
uint8_t facch_bits_b[144];
|
||||
uint8_t cac_bits[300];
|
||||
uint8_t facch2_bits[348]; //facch2 or udch, same amount of bits
|
||||
uint8_t facch3_bits[288]; //facch3 or udch2, same amoount of bits
|
||||
|
||||
//nxdn bit buffer, for easy assignment handling
|
||||
int nxdn_bit_buffer[364];
|
||||
int nxdn_dibit_buffer[182];
|
||||
|
||||
//init all arrays
|
||||
memset (dbuf, 0, sizeof(dbuf));
|
||||
memset (answer, 0, sizeof(answer));
|
||||
memset (sacch_answer, 0, sizeof(sacch_answer));
|
||||
memset (lich_dibits, 0, sizeof(lich_dibits));
|
||||
memset (sacch_bits, 0, sizeof(sacch_bits));
|
||||
memset (facch_bits_b, 0, sizeof(facch_bits_b));
|
||||
memset (facch_bits_a, 0, sizeof(facch_bits_a));
|
||||
memset (cac_bits, 0, sizeof(cac_bits));
|
||||
memset (facch2_bits, 0, sizeof(facch2_bits));
|
||||
|
||||
memset (nxdn_bit_buffer, 0, sizeof(nxdn_bit_buffer));
|
||||
memset (nxdn_dibit_buffer, 0, sizeof(nxdn_dibit_buffer));
|
||||
|
||||
//collect lich bits first, if they are good, then we can collect the rest of them
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
||||
|
||||
nxdn_descramble (lich_dibits, 8);
|
||||
|
||||
lich = 0;
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
lich |= (lich_dibits[i] >> 1) << (7-i);
|
||||
}
|
||||
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
|
||||
|
||||
lich_parity_received = lich & 1;
|
||||
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1;
|
||||
lich = lich >> 1;
|
||||
if (lich_parity_received != lich_parity_computed)
|
||||
{
|
||||
//fprintf(stderr, "NXDN lich parity error, ignoring frame \n");
|
||||
return;
|
||||
{
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
goto END;
|
||||
}
|
||||
|
||||
voice = 0;
|
||||
@@ -86,6 +106,16 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
facch2 = 0;
|
||||
sacch = 0;
|
||||
cac = 0;
|
||||
|
||||
//test for inbound direction lich when trunking (false positive) and skip
|
||||
//all inbound lich are even value (lsb is set to 0 for inbound direction)
|
||||
if (lich % 2 == 0 && opts->p25_trunk == 1)
|
||||
{
|
||||
if (opts->payload == 1) fprintf(stderr, " Simplex/Inbound NXDN lich on trunking system - type 0x%02X\n", lich);
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
goto END;
|
||||
}
|
||||
|
||||
switch(lich) { //cases without breaks continue to flow downwards until they hit the break
|
||||
case 0x01: // CAC types
|
||||
case 0x05:
|
||||
@@ -93,21 +123,20 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
break;
|
||||
case 0x28: //facch2 types
|
||||
case 0x29:
|
||||
case 0x2e:
|
||||
case 0x2f:
|
||||
case 0x48:
|
||||
case 0x49:
|
||||
facch2 = 1;
|
||||
break;
|
||||
case 0x2e: //udch types
|
||||
case 0x2f:
|
||||
case 0x4e:
|
||||
case 0x4f:
|
||||
case 0x69:
|
||||
case 0x6f:
|
||||
facch2 = 1;
|
||||
udch = 1;
|
||||
break;
|
||||
case 0x32: //facch in 1, vch in 2
|
||||
case 0x33:
|
||||
case 0x52:
|
||||
case 0x53:
|
||||
case 0x73:
|
||||
voice = 2;
|
||||
facch = 1;
|
||||
sacch = 1;
|
||||
@@ -115,17 +144,15 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
case 0x34: //vch in 1, facch in 2
|
||||
case 0x35:
|
||||
case 0x54:
|
||||
case 0x55:
|
||||
case 0x75:
|
||||
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
|
||||
voice = 1;
|
||||
facch = 2;
|
||||
sacch = 1;
|
||||
break;
|
||||
case 0x36: //vch in both
|
||||
case 0x37:
|
||||
case 0x56:
|
||||
case 0x56:
|
||||
case 0x57:
|
||||
case 0x77:
|
||||
voice = 3;
|
||||
facch = 0;
|
||||
sacch = 1;
|
||||
@@ -138,8 +165,6 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
case 0x41:
|
||||
case 0x50:
|
||||
case 0x51:
|
||||
case 0x61:
|
||||
case 0x71:
|
||||
voice = 0;
|
||||
facch = 3;
|
||||
sacch = 1;
|
||||
@@ -148,10 +173,63 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
case 0x39:
|
||||
sacch = 1;
|
||||
break;
|
||||
|
||||
//NXDN "Type-D" or "IDAS" Specific Lich Codes
|
||||
case 0x76: //normal vch voice (in one and two)
|
||||
case 0x77:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 3;
|
||||
break;
|
||||
case 0x74: //vch in 1, facch1 in 2 (facch 2 steal)
|
||||
case 0x75:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 1;
|
||||
facch = 2;
|
||||
break;
|
||||
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
|
||||
case 0x73:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
voice = 2;
|
||||
facch = 1;
|
||||
break;
|
||||
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
|
||||
case 0x71:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 3;
|
||||
break;
|
||||
case 0x6E: //udch2
|
||||
case 0x6F:
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
udch2 = 1;
|
||||
break;
|
||||
case 0x68:
|
||||
case 0x69: //facch3
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch3 = 1;
|
||||
break;
|
||||
case 0x62:
|
||||
case 0x63: //facch1 in 1, null data and post field in 2
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 1;
|
||||
break;
|
||||
case 0x60:
|
||||
case 0x61: //facch1 in both (non vch)
|
||||
idas = 1;
|
||||
scch = 1;
|
||||
facch = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (opts->payload == 1) fprintf(stderr, " false sync or unsupported NXDN lich type 0x%02X\n", lich);
|
||||
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
|
||||
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
||||
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
voice = 0;
|
||||
@@ -160,16 +238,24 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
} // end of switch(lich)
|
||||
|
||||
//printframesync after determining we have a good lich and it has something in it
|
||||
if (voice || facch || sacch || facch2 || cac)
|
||||
if (idas)
|
||||
{
|
||||
if (opts->frame_nxdn48 == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "IDAS D ", 0, "-");
|
||||
}
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "L%02X - ", lich);
|
||||
}
|
||||
else if (voice || facch || sacch || facch2 || cac)
|
||||
{
|
||||
// if (state->synctype == 28) fprintf (stderr, " +");
|
||||
// else fprintf (stderr, " -");
|
||||
if (opts->frame_nxdn48 == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "NXDN48 ", 0, "-");
|
||||
}
|
||||
else printFrameSync (opts, state, "NXDN96 ", 0, "-");
|
||||
if (opts->payload == 1) fprintf (stderr, "L%02X -", lich);
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "L%02X - ", lich);
|
||||
}
|
||||
|
||||
//now that we have a good LICH, we can collect all of our dibits
|
||||
@@ -188,7 +274,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
|
||||
}
|
||||
|
||||
//sacch
|
||||
//sacch or scch bits
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
||||
@@ -213,23 +299,71 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
facch2_bits[i] = nxdn_bit_buffer[i+16];
|
||||
}
|
||||
|
||||
//udch2 or facch3
|
||||
for (int i = 0; i < 288; i++)
|
||||
{
|
||||
facch3_bits[i] = nxdn_bit_buffer[i+16+60];
|
||||
}
|
||||
|
||||
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
|
||||
//sacch needs extra handling depending on superframe or non-superframe variety
|
||||
|
||||
if (voice)
|
||||
//Add advanced decoding of LICH (RF, FC, OPT, and Direction
|
||||
lich_rf = (lich >> 5) & 0x3;
|
||||
lich_fc = (lich >> 3) & 0x3;
|
||||
lich_op = (lich >> 1) & 0x3;
|
||||
if (lich % 2 == 0) direction = 0;
|
||||
else direction = 1;
|
||||
|
||||
// RF Channel Type
|
||||
if (lich_rf == 0) fprintf (stderr, "RCCH ");
|
||||
else if (lich_rf == 1) fprintf (stderr, "RTCH ");
|
||||
else if (lich_rf == 2) fprintf (stderr, "RDCH ");
|
||||
else
|
||||
{
|
||||
if (lich < 0x60) fprintf (stderr, "RTCH_C ");
|
||||
else fprintf (stderr, "RTCH2 ");
|
||||
}
|
||||
|
||||
// Functional Channel Type -- things start to get really convoluted here
|
||||
// These will echo when handled, either with the decoded message type, or relevant crc err
|
||||
// if (lich_rf == 0) //CAC Type
|
||||
// {
|
||||
// //Technically, we should be checking direction as well, but the fc never has split meaning on CAC
|
||||
// if (lich_fc == 0) fprintf (stderr, "CAC ");
|
||||
// else if (lich_fc == 1) fprintf (stderr, "Long CAC ");
|
||||
// else if (lich_fc == 3) fprintf (stderr, "Short CAC ");
|
||||
// else fprintf (stderr, "Reserved ");
|
||||
// }
|
||||
// else //USC Type
|
||||
// {
|
||||
// if (lich_fc == 0) fprintf (stderr, "NSF SACCH ");
|
||||
// else if (lich_fc == 1) fprintf (stderr, "UDCH ");
|
||||
// else if (lich_fc == 2) fprintf (stderr, "SF SACCH ");
|
||||
// else if (lich_fc == 3) fprintf (stderr, "SF SACCH/IDLE ");
|
||||
// }
|
||||
|
||||
//Option/Steal Flags echoed in Voice, V+F, or Data
|
||||
if (voice && !facch) //voice only, no facch steal
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, " Voice ");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
else
|
||||
else if (voice && facch) //voice with facch1 steal
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, " V%d+F%d ", 3 - facch, facch); //print which position on each
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
else //Covers FACCH1 in both, FACCH2, UDCH, UDCH2, CAC
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " Data ");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//roll the voice scrambler LFSR here if key available to advance seed (usually just needed on NXDN96)
|
||||
if (state->nxdn_cipher_type == 0x1 && state->R != 0) //!voice, or voice == 0
|
||||
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||
{
|
||||
char ambe_temp[49] = {0};
|
||||
char ambe_d[49] = {0};
|
||||
@@ -239,39 +373,88 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
state->nxdn_sacch_non_superframe = (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) ? true : false;
|
||||
if (sacch)nxdn_deperm_sacch(opts, state, sacch_bits);
|
||||
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
||||
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits);
|
||||
if (voice && facch == 1) //facch steal 1 -- before voice
|
||||
{
|
||||
//force scrambler here, but with unspecified key (just use what's loaded)
|
||||
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
//roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal
|
||||
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||
{
|
||||
char ambe_temp[49] = {0};
|
||||
char ambe_d[49] = {0};
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
LFSRN(ambe_temp, ambe_d, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) state->nxdn_sacch_non_superframe = TRUE;
|
||||
else state->nxdn_sacch_non_superframe = FALSE;
|
||||
|
||||
//TODO Later: Add Direction and/or LICH to all decoding functions
|
||||
if (scch) nxdn_deperm_scch (opts, state, sacch_bits, direction);
|
||||
|
||||
if (udch2) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 0);
|
||||
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
|
||||
|
||||
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
|
||||
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
||||
|
||||
//Seperated UDCH user data from facch2 data
|
||||
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
|
||||
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
|
||||
|
||||
//SHOULD be okay to run facch1's again on steal frames, will need testing
|
||||
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
||||
if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
|
||||
|
||||
if (voice)
|
||||
{
|
||||
//restore MBE file open here
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||
//update last voice sync time
|
||||
state->last_vc_sync_time = time(NULL);
|
||||
//turn on scrambler if forced by user option
|
||||
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||
//process voice frame
|
||||
nxdn_voice (opts, state, voice, dbuf);
|
||||
}
|
||||
|
||||
//close MBE file if no voice and its open
|
||||
if (!voice)
|
||||
{
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
if (opts->frame_nxdn96 == 1) //nxdn96 has voice and data mixed together, so we will need to do a time check first
|
||||
if (opts->frame_nxdn96 == 1) //nxdn96 has pure voice and data frames mixed together, so we will need to do a time check first
|
||||
{
|
||||
if ( (time(NULL) - state->last_vc_sync_time) > 1) //test for optimal time, 1 sec should be okay
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
if (opts->frame_nxdn48 == 1) closeMbeOutFile (opts, state); //okay to close right away if nxdn48, no data/voice mixing
|
||||
if (opts->frame_nxdn48 == 1) closeMbeOutFile (opts, state); //okay to close right away if nxdn48, no data/voice frames mixing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (voice && facch == 2) //facch steal 2 -- after voice 1
|
||||
{
|
||||
//roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal
|
||||
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||
{
|
||||
char ambe_temp[49] = {0};
|
||||
char ambe_d[49] = {0};
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
LFSRN(ambe_temp, ambe_d, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->payload == 1 && !voice) fprintf (stderr, "\n");
|
||||
else if (opts->payload == 0) fprintf (stderr, "\n");
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
END: ; //do nothing
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* p25_frequency.c
|
||||
* P25 Channel to Frequency Calculator
|
||||
*
|
||||
* NXDN Channel to Frequency, Courtesy of IcomIcR20 and EricCottrell (base values)
|
||||
* NXDN Channel to Frequency Calculator
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-11 DSD-FME Florida Man Edition
|
||||
@@ -59,16 +59,16 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
|
||||
|
||||
}
|
||||
|
||||
//NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums
|
||||
long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t channel)
|
||||
{
|
||||
long int freq;
|
||||
long int base = 0;
|
||||
long int step = 0;
|
||||
|
||||
//the base freq value is per EricCottrell in the Understanding NXDN Trunking Forum Thread
|
||||
//will need to do some research or tests to confirm these values are accurate
|
||||
//reworked to include Direct Frequency Assignment if available
|
||||
|
||||
//first, check channel map
|
||||
//first, check channel map for imported value, DFA systems most likely won't need an import,
|
||||
//unless it has 'system definable' attributes
|
||||
if (state->trunk_chan_map[channel] != 0)
|
||||
{
|
||||
freq = state->trunk_chan_map[channel];
|
||||
@@ -76,33 +76,41 @@ long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t
|
||||
return (freq);
|
||||
}
|
||||
|
||||
//if not found, attempt to find it via calculation
|
||||
else
|
||||
//then, let's see if its DFA instead -- 6.5.36
|
||||
else if (state->nxdn_rcn == 1)
|
||||
{
|
||||
if ((channel > 0) && (channel <= 400))
|
||||
{
|
||||
if (opts->frame_nxdn48 == 1) base = 450000000;
|
||||
else base = 451000000;
|
||||
//determine the base frequency in Hz
|
||||
if (state->nxdn_base_freq == 1) base = 100000000; //100 MHz
|
||||
else if (state->nxdn_base_freq == 2) base = 330000000; //330 Mhz
|
||||
else if (state->nxdn_base_freq == 3) base = 400000000; //400 Mhz
|
||||
else if (state->nxdn_base_freq == 4) base = 750000000; //750 Mhz
|
||||
else base = 0; //just set to zero, will be system definable most likely and won't be able to calc
|
||||
|
||||
freq = base + (channel - 1) * 12500;
|
||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||
//determine the step value in Hz
|
||||
if (state->nxdn_step = 2) step = 1250; //1.25 kHz
|
||||
else if (state->nxdn_step = 3) step = 3125; //3.125 kHz
|
||||
else step = 0; //just set to zero, will be system definable most likely and won't be able to calc
|
||||
|
||||
//if we have a valid base and step, then calc frequency
|
||||
//6.5.45. Outbound/Inbound Frequency Number (OFN/IFN)
|
||||
if (base && step)
|
||||
{
|
||||
freq = base + (channel * step);
|
||||
fprintf (stderr, "\n DFA Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||
return (freq);
|
||||
}
|
||||
else if ((channel >= 401) && (channel <= 800))
|
||||
else
|
||||
{
|
||||
if (opts->frame_nxdn48 == 1) base = 460000000;
|
||||
else base = 461000000;
|
||||
|
||||
freq = base + (channel - 401) * 12500;
|
||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||
return (freq);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "\n Non-standard frequency or channel not found in import file");
|
||||
fprintf(stderr, "\n Custom DFA Settings -- Unknown Freq;");
|
||||
return (0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "\n Channel not found in import file");
|
||||
return (0);
|
||||
}
|
||||
|
||||
}
|
||||
460
src/p25_lcw.c
460
src/p25_lcw.c
@@ -1,160 +1,340 @@
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo)
|
||||
//new p25_lcw function here -- TIA-102.AABF-D LCW Format Messages (if anybody wants to fill the rest out)
|
||||
void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t irrecoverable_errors)
|
||||
{
|
||||
uint8_t lc_format = (uint8_t)ConvertBitIntoBytes(&LCW_bits[0], 8); //format
|
||||
uint8_t lc_opcode = (uint8_t)ConvertBitIntoBytes(&LCW_bits[2], 8); //opcode portion of format
|
||||
uint8_t lc_mfid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 8); //mfid
|
||||
uint8_t lc_svcopt = (uint8_t)ConvertBitIntoBytes(&LCW_bits[16], 8); //service options
|
||||
uint8_t lc_pf = LCW_bits[0]; //protect flag
|
||||
uint8_t lc_sf = LCW_bits[1]; //Implicit / Explicit MFID Format
|
||||
|
||||
char tgid[17], tmpstr[255];
|
||||
long talkgroup, source;
|
||||
int i, j;
|
||||
if (lc_pf == 1) //check the protect flag -- if set, its an encrypted lcw
|
||||
{
|
||||
fprintf (stderr, "%s",KRED);
|
||||
fprintf (stderr, " LCW Protected ");
|
||||
fprintf (stderr, "%s",KNRM);
|
||||
}
|
||||
|
||||
tgid[16] = 0;
|
||||
if (lc_pf == 0) //not protected/encrypted lcw
|
||||
{
|
||||
|
||||
if (opts->p25lc == 1)
|
||||
{
|
||||
fprintf (stderr, "lcformat: %s mfid: %s lcinfo: %s ", lcformat, mfid, lcinfo);
|
||||
if (opts->p25tg == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
if (opts->payload == 1) fprintf (stderr, " LCW");
|
||||
|
||||
if (strcmp (lcformat, "00000100") == 0)
|
||||
//check to see if we need to run these as MFID 0 or 1 only (standard)
|
||||
if (lc_mfid == 0 || lc_mfid == 1) //lc_mfid == 0
|
||||
{
|
||||
|
||||
// first tg is the active channel
|
||||
j = 0;
|
||||
for (i = 40; i < 52; i++)
|
||||
//check the service options on applicable formats
|
||||
if (lc_format == 0x4A || lc_format == 0x46 || lc_format == 0x45
|
||||
|| lc_format == 0x44 || lc_format == 0x03 || lc_format == 0x00)
|
||||
{
|
||||
|
||||
if (lc_svcopt & 0x80) fprintf (stderr, " Emergency");
|
||||
if (lc_svcopt & 0x40) fprintf (stderr, " Encrypted");
|
||||
|
||||
if (opts->payload == 1) //hide behind payload due to len
|
||||
{
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tg[state->tgcount][j] = lcinfo[i];
|
||||
}
|
||||
tmpstr[j] = lcinfo[i];
|
||||
j++;
|
||||
if (lc_svcopt & 0x20) fprintf (stderr, " Duplex");
|
||||
if (lc_svcopt & 0x10) fprintf (stderr, " Packet");
|
||||
else fprintf (stderr, " Circuit");
|
||||
if (lc_svcopt & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||
fprintf (stderr, " Priority %d", lc_svcopt & 0x7); //call priority
|
||||
}
|
||||
tmpstr[12] = 48;
|
||||
tmpstr[13] = 48;
|
||||
tmpstr[14] = 48;
|
||||
tmpstr[15] = 48;
|
||||
tmpstr[16] = 0;
|
||||
talkgroup = strtol (tmpstr, NULL, 2);
|
||||
state->lasttg = talkgroup;
|
||||
if (state->tgcount < 24)
|
||||
}
|
||||
|
||||
if (lc_format == 0x00)
|
||||
{
|
||||
fprintf (stderr, " Group Voice Channel User");
|
||||
uint8_t res = (uint8_t)ConvertBitIntoBytes(&LCW_bits[24], 7);
|
||||
uint8_t explicit = LCW_bits[24]; //explicit source id required == 1, full SUID on seperate LCW
|
||||
uint16_t group = (uint16_t)ConvertBitIntoBytes(&LCW_bits[32], 16);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||
fprintf (stderr, " - Group %d Source %d", group, source);
|
||||
|
||||
//don't set this when zero, annoying blink occurs in ncurses
|
||||
if (group != 0) state->lasttg = group;
|
||||
if (source != 0) state->lastsrc = source;
|
||||
|
||||
sprintf (state->call_string[0], " Group ");
|
||||
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
|
||||
else if (lc_svcopt & 0x40) strcat (state->call_string[0], " Encrypted ");
|
||||
else strcat (state->call_string[0], " ");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x03)
|
||||
{
|
||||
fprintf (stderr, " Unit to Unit Voice Channel User");
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[24], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||
fprintf (stderr, " - Target %d Source %d", target, source);
|
||||
|
||||
//don't set this when zero, annoying blink occurs in ncurses
|
||||
if (target != 0) state->lasttg = target;
|
||||
if (source != 0) state->lastsrc = source;
|
||||
|
||||
sprintf (state->call_string[0], " Private ");
|
||||
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
|
||||
else if (lc_svcopt & 0x40) strcat (state->call_string[0], " Encrypted ");
|
||||
else strcat (state->call_string[0], " ");
|
||||
|
||||
}
|
||||
|
||||
//uses explicit bit?
|
||||
else if (lc_format == 0x42) //is this group only, or group and private?
|
||||
{
|
||||
fprintf (stderr, " Group Voice Channel Update - ");
|
||||
uint16_t channel1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[8], 16);
|
||||
uint16_t group1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||||
uint16_t channel2 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[40], 16);
|
||||
uint16_t group2 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||||
|
||||
if (channel1 && group1)
|
||||
{
|
||||
state->tgcount = state->tgcount + 1;
|
||||
}
|
||||
if (opts->p25tg == 1)
|
||||
{
|
||||
fprintf (stderr, "tg: %li ", talkgroup);
|
||||
fprintf (stderr, "Ch: %04X TG: %d; ", channel1, group1);
|
||||
sprintf (state->dmr_lrrp_gps[0], "Active Ch: %04X TG: %d;", channel1, group1);
|
||||
}
|
||||
|
||||
if (opts->p25tg == 1)
|
||||
if (channel2 && group2 && group1 != group2)
|
||||
{
|
||||
fprintf (stderr, "tg: %li ", talkgroup);
|
||||
|
||||
// the remaining 3 appear to be other active tg's on the system
|
||||
j = 0;
|
||||
for (i = 28; i < 40; i++)
|
||||
{
|
||||
tmpstr[j] = lcinfo[i];
|
||||
j++;
|
||||
}
|
||||
tmpstr[12] = 48;
|
||||
tmpstr[13] = 48;
|
||||
tmpstr[14] = 48;
|
||||
tmpstr[15] = 48;
|
||||
tmpstr[16] = 0;
|
||||
talkgroup = strtol (tmpstr, NULL, 2);
|
||||
fprintf (stderr, "%li ", talkgroup);
|
||||
j = 0;
|
||||
for (i = 16; i < 28; i++)
|
||||
{
|
||||
tmpstr[j] = lcinfo[i];
|
||||
j++;
|
||||
}
|
||||
tmpstr[12] = 48;
|
||||
tmpstr[13] = 48;
|
||||
tmpstr[14] = 48;
|
||||
tmpstr[15] = 48;
|
||||
tmpstr[16] = 0;
|
||||
talkgroup = strtol (tmpstr, NULL, 2);
|
||||
fprintf (stderr, "%li ", talkgroup);
|
||||
j = 0;
|
||||
for (i = 4; i < 16; i++)
|
||||
{
|
||||
tmpstr[j] = lcinfo[i];
|
||||
j++;
|
||||
}
|
||||
tmpstr[12] = 48;
|
||||
tmpstr[13] = 48;
|
||||
tmpstr[14] = 48;
|
||||
tmpstr[15] = 48;
|
||||
tmpstr[16] = 0;
|
||||
talkgroup = strtol (tmpstr, NULL, 2);
|
||||
fprintf (stderr, "%li\n", talkgroup);
|
||||
fprintf (stderr, "Ch: %04X TG: %d; ", channel2, group2);
|
||||
sprintf (state->dmr_lrrp_gps[1], "Active Ch: %04X TG: %d;", channel2, group2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (lc_format == 0x44)
|
||||
{
|
||||
fprintf (stderr, " Group Voice Channel Update – Explicit");
|
||||
uint16_t group1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||||
uint16_t channelt = (uint16_t)ConvertBitIntoBytes(&LCW_bits[40], 16);
|
||||
uint16_t channelr = (uint16_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||||
fprintf (stderr, "Ch: %04X TG: %d; ", channelt, group1);
|
||||
}
|
||||
|
||||
else if (lc_format == 0x45)
|
||||
{
|
||||
fprintf (stderr, " Unit to Unit Answer Request");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x46)
|
||||
{
|
||||
fprintf (stderr, " Telephone Interconnect Voice Channel User");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x47)
|
||||
{
|
||||
fprintf (stderr, " Telephone Interconnect Answer Request");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x49)
|
||||
{
|
||||
fprintf (stderr, " Souce ID Extension");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x4A)
|
||||
{
|
||||
fprintf (stderr, " Unit to Unit Voice Channel User – Extended");
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 24);
|
||||
uint32_t suid = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 24);
|
||||
fprintf (stderr, "TGT: %04X SUID: %d; ", target, suid);
|
||||
}
|
||||
|
||||
else if (lc_format == 0x50)
|
||||
{
|
||||
fprintf (stderr, " Group Affiliation Query");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x51)
|
||||
{
|
||||
fprintf (stderr, " Unit Registration Command");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x52) //wonder if anybody uses this if its deleted/obsolete
|
||||
{
|
||||
fprintf (stderr, " Unit Authentication Command - OBSOLETE");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x53)
|
||||
{
|
||||
fprintf (stderr, " Status Query");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x54)
|
||||
{
|
||||
fprintf (stderr, " Status Update");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x55)
|
||||
{
|
||||
fprintf (stderr, " Status Update");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x56)
|
||||
{
|
||||
fprintf (stderr, " Call Alert");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x57)
|
||||
{
|
||||
fprintf (stderr, " Extended Function Command");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x58)
|
||||
{
|
||||
fprintf (stderr, " Channel Identifier Update");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x59)
|
||||
{
|
||||
fprintf (stderr, " Channel Identifier Update VU");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x5A)
|
||||
{
|
||||
fprintf (stderr, " Status Update – Source ID Extension Required");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x5C)
|
||||
{
|
||||
fprintf (stderr, " Extended Function Command – Source ID Extension Required");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x60)
|
||||
{
|
||||
fprintf (stderr, " System Service Broadcast");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x61)
|
||||
{
|
||||
fprintf (stderr, " Secondary Control Channel Broadcast");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x62)
|
||||
{
|
||||
fprintf (stderr, " Adjacent Site Status Broadcast");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x63)
|
||||
{
|
||||
fprintf (stderr, " RFSS Status Broadcast");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x64)
|
||||
{
|
||||
fprintf (stderr, " Network Status Broadcast");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x65)
|
||||
{
|
||||
fprintf (stderr, " Protection Parameter Broadcast - OBSOLETE");
|
||||
}
|
||||
|
||||
|
||||
else if (lc_format == 0x66)
|
||||
{
|
||||
fprintf (stderr, " Secondary Control Channel Broadcast – Explicit (LCSCBX)");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x67) //explicit
|
||||
{
|
||||
fprintf (stderr, " Adjacent Site Status (LCASBX)");
|
||||
uint8_t lra = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 8);
|
||||
uint16_t channelt = (uint16_t)ConvertBitIntoBytes(&LCW_bits[16], 16);
|
||||
uint8_t rfssid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[32], 8);
|
||||
uint8_t siteid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[40], 8);
|
||||
uint16_t channelr = (uint16_t)ConvertBitIntoBytes(&LCW_bits[48], 16);
|
||||
uint8_t cfva = (uint8_t)ConvertBitIntoBytes(&LCW_bits[64], 4);
|
||||
fprintf (stderr, " - RFSS %d Site %d CH %04X", rfssid, siteid, channelt);
|
||||
|
||||
//debug print only
|
||||
// fprintf (stderr, "\n ");
|
||||
// fprintf (stderr, " LRA [%02X] RFSS [%03d] SITE [%03d] CHAN-T [%04X] CHAN-R [%02X] CFVA [%X]\n ", lra, rfssid, siteid, channelt, channelr, cfva);
|
||||
// if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||
// if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||
// if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||
// else fprintf (stderr, " Last Known");
|
||||
if (cfva & 0x1) fprintf (stderr, "- Connection Active");
|
||||
// process_channel_to_freq (opts, state, channelt);
|
||||
//end debug, way too much for a simple link control line
|
||||
|
||||
|
||||
}
|
||||
|
||||
else if (lc_format == 0x68)
|
||||
{
|
||||
fprintf (stderr, " RFSS Status Broadcast – Explicit (LCRSBX)");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x69)
|
||||
{
|
||||
fprintf (stderr, " Network Status Broadcast – Explicit (LCNSBX)");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x6A)
|
||||
{
|
||||
fprintf (stderr, " Conventional Fallback");
|
||||
}
|
||||
|
||||
else if (lc_format == 0x6B)
|
||||
{
|
||||
fprintf (stderr, " Message Update – Source ID Extension Required");
|
||||
}
|
||||
|
||||
//tune back to CC here - save about 1-2 seconds
|
||||
else if (lc_format == 0x4F) //# Call Termination/Cancellation
|
||||
{
|
||||
fprintf (stderr, " Call Termination/Cancellation");
|
||||
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
|
||||
{
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
// state->payload_miP = 0;
|
||||
//reset some strings
|
||||
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||
sprintf (state->dmr_lrrp_gps[0], "%s", ""); //this is used for active ch in p25 trunking
|
||||
sprintf (state->dmr_lrrp_gps[1], "%s", ""); //this is used for active ch in p25 trunking
|
||||
opts->p25_is_tuned = 0;
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||||
}
|
||||
//rtl_udp
|
||||
else if (opts->audio_in_type == 3)
|
||||
{
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
// state->payload_miP = 0;
|
||||
//reset some strings
|
||||
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||
sprintf (state->dmr_lrrp_gps[0], "%s", ""); //this is used for active ch in p25 trunking
|
||||
sprintf (state->dmr_lrrp_gps[1], "%s", ""); //this is used for active ch in p25 trunking
|
||||
opts->p25_is_tuned = 0;
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||
rtl_udp_tune (opts, state, state->p25_cc_freq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else fprintf (stderr, " Unknown Format %02X MFID %02X SVC %02X", lc_format, lc_mfid, lc_svcopt);
|
||||
}
|
||||
|
||||
else if (strcmp (lcformat, "00000000") == 0)
|
||||
{
|
||||
j = 0;
|
||||
if (strcmp (mfid, "10010000") == 0)
|
||||
{
|
||||
for (i = 20; i < 32; i++)
|
||||
{
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tg[state->tgcount][j] = lcinfo[i];
|
||||
}
|
||||
tmpstr[j] = lcinfo[i];
|
||||
j++;
|
||||
}
|
||||
tmpstr[12] = 48;
|
||||
tmpstr[13] = 48;
|
||||
tmpstr[14] = 48;
|
||||
tmpstr[15] = 48;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 16; i < 32; i++)
|
||||
{
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tg[state->tgcount][j] = lcinfo[i];
|
||||
}
|
||||
tmpstr[j] = lcinfo[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
tmpstr[16] = 0;
|
||||
talkgroup = strtol (tmpstr, NULL, 2);
|
||||
state->lasttg = talkgroup;
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tgcount = state->tgcount + 1;
|
||||
}
|
||||
if (opts->p25tg == 1)
|
||||
{
|
||||
fprintf (stderr, "tg: %li ", talkgroup);
|
||||
}
|
||||
//not a duplicate, this one will print if not MFID 0 or 1
|
||||
else fprintf (stderr, " Unknown Format %02X MFID %02X ", lc_format, lc_mfid);
|
||||
}
|
||||
|
||||
//ending line break
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
j = 0;
|
||||
for (i = 32; i < 56; i++)
|
||||
{
|
||||
tmpstr[j] = lcinfo[i];
|
||||
j++;
|
||||
}
|
||||
tmpstr[24] = 0;
|
||||
source = strtol (tmpstr, NULL, 2);
|
||||
state->lastsrc = source;
|
||||
if (opts->p25tg == 1)
|
||||
{
|
||||
fprintf (stderr, "src: %li emr: %c\n", source, lcinfo[0]);
|
||||
}
|
||||
}
|
||||
else if ((opts->p25tg == 1) && (opts->p25lc == 1))
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ static int find_min(uint8_t list[], int len) {
|
||||
index = i;
|
||||
unique = 1;
|
||||
} else if (list[i] == min) {
|
||||
unique = 0;
|
||||
//unique = 0;
|
||||
; //do nothing, still can be correct and pass CRC
|
||||
}
|
||||
}
|
||||
/* return -1 if a minimum can't be found */
|
||||
|
||||
107
src/p25p1_hdu.c
107
src/p25p1_hdu.c
@@ -453,93 +453,40 @@ processHDU(dsd_opts* opts, dsd_state* state)
|
||||
status = getDibit (opts, state);
|
||||
//TODO: Do something useful with the status bits...
|
||||
|
||||
//if (state->carrier == 1 && state->errs == 0) //only update when carrier is present, and no errors??
|
||||
if (state->carrier == 1)
|
||||
{
|
||||
algidhex = strtol (algid, NULL, 2);
|
||||
kidhex = strtol (kid, NULL, 2);
|
||||
|
||||
state->payload_algid = algidhex;
|
||||
state->payload_keyid = kidhex;
|
||||
//state->payload_mfid = ConvertBitIntoBytes(&mfid[0], 7);
|
||||
//state->payload_mfid = strtol (mfid, NULL, 2);
|
||||
|
||||
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
||||
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
||||
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
||||
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
||||
state->payload_miP = (mihex1 << 32) | (mihex2);
|
||||
}
|
||||
algidhex = strtol (algid, NULL, 2);
|
||||
kidhex = strtol (kid, NULL, 2);
|
||||
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
||||
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
||||
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
||||
|
||||
//set vc counter to 0
|
||||
state->p25vc = 0;
|
||||
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
if (irrecoverable_errors == 0)
|
||||
{
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX MFID: 0x%02X", algidhex, kidhex, mihex1, mihex2, mihex3, state->payload_mfid);
|
||||
fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX", algidhex, kidhex, mihex1, mihex2, mihex3);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
state->payload_algid = algidhex;
|
||||
state->payload_keyid = kidhex;
|
||||
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
||||
state->payload_miP = (mihex1 << 32) | (mihex2);
|
||||
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " ENC");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " ENC \n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (opts->p25enc == 1 && opts->payload == 0)
|
||||
{
|
||||
algidhex = strtol (algid, NULL, 2);
|
||||
kidhex = strtol (kid, NULL, 2);
|
||||
}
|
||||
if (opts->p25lc == 1)
|
||||
{
|
||||
fprintf (stderr, "mfid: %s tgid: %s ", mfid, tgid);
|
||||
if (opts->p25tg == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
j = 0;
|
||||
if (strcmp (mfid, "10010000") == 0)
|
||||
{
|
||||
for (i = 4; i < 16; i++)
|
||||
{
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tg[state->tgcount][j] = tgid[i];
|
||||
}
|
||||
tmpstr[j] = tgid[i];
|
||||
j++;
|
||||
}
|
||||
tmpstr[12] = '0';
|
||||
tmpstr[13] = '0';
|
||||
tmpstr[14] = '0';
|
||||
tmpstr[15] = '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tg[state->tgcount][j] = tgid[i];
|
||||
}
|
||||
tmpstr[j] = tgid[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
tmpstr[16] = 0;
|
||||
talkgroup = strtol (tmpstr, NULL, 2);
|
||||
state->lasttg = talkgroup;
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tgcount = state->tgcount + 1;
|
||||
}
|
||||
if (opts->p25tg == 1)
|
||||
{
|
||||
fprintf (stderr, "tg: %li\n", talkgroup);
|
||||
}
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " HDU FEC ERR \n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -324,15 +324,53 @@ processLDU1 (dsd_opts* opts, dsd_state* state)
|
||||
lcinfo[54] = hex_data[ 0][4] + '0';
|
||||
lcinfo[55] = hex_data[ 0][5] + '0';
|
||||
|
||||
unsigned long long int lcinfohex = 0;
|
||||
if (state->carrier == 1 && state->errs == 0) //only update when carrier is present, otherwise, garbage values may be collected
|
||||
{
|
||||
state->payload_mfid = strtol (mfid, NULL, 2);
|
||||
int j;
|
||||
uint8_t LCW_bytes[9];
|
||||
uint8_t LCW_bits[72];
|
||||
memset (LCW_bytes, 0, sizeof(LCW_bytes));
|
||||
memset (LCW_bits, 0, sizeof(LCW_bits));
|
||||
|
||||
//state->payload_mfid = ConvertBitIntoBytes(&mfid[0], 7);
|
||||
lcinfohex = ConvertBitIntoBytes(&lcinfo[0], 55);
|
||||
//fprintf (stderr, " LDU1 LCINFO %16llX \n", lcinfohex);
|
||||
//load array above into byte form first
|
||||
LCW_bytes[0] = (uint8_t) ConvertBitIntoBytes(&lcformat[0], 8);
|
||||
LCW_bytes[1] = (uint8_t) ConvertBitIntoBytes(&mfid[0], 8);
|
||||
for (i = 0; i < 7; i++) LCW_bytes[i+2] = (uint8_t) ConvertBitIntoBytes(&lcinfo[i*8], 8);
|
||||
|
||||
//load as bit array next (easier to process data in bit form)
|
||||
for(i = 0, j = 0; i < 9; i++, j+=8)
|
||||
{
|
||||
LCW_bits[j + 0] = (LCW_bytes[i] >> 7) & 0x01;
|
||||
LCW_bits[j + 1] = (LCW_bytes[i] >> 6) & 0x01;
|
||||
LCW_bits[j + 2] = (LCW_bytes[i] >> 5) & 0x01;
|
||||
LCW_bits[j + 3] = (LCW_bytes[i] >> 4) & 0x01;
|
||||
LCW_bits[j + 4] = (LCW_bytes[i] >> 3) & 0x01;
|
||||
LCW_bits[j + 5] = (LCW_bytes[i] >> 2) & 0x01;
|
||||
LCW_bits[j + 6] = (LCW_bytes[i] >> 1) & 0x01;
|
||||
LCW_bits[j + 7] = (LCW_bytes[i] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
//send to new P25 LCW function
|
||||
if (irrecoverable_errors == 0)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
p25_lcw (opts, state, LCW_bits, 0);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s",KRED);
|
||||
fprintf (stderr, " LCW FEC ERR ");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, " P25 LCW Payload ");
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", LCW_bytes[i]);
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,37 +357,37 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
|
||||
kid[14] = hex_data[ 0][4] + '0';
|
||||
kid[15] = hex_data[ 0][5] + '0';
|
||||
|
||||
algidhex = strtol (algid, NULL, 2);
|
||||
kidhex = strtol (kid, NULL, 2);
|
||||
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
||||
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
||||
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
||||
|
||||
//if (state->carrier == 1 && state->errs == 0) //only update when carrier is present, and no errors??
|
||||
if (state->carrier == 1)
|
||||
{
|
||||
|
||||
algidhex = strtol (algid, NULL, 2);
|
||||
kidhex = strtol (kid, NULL, 2);
|
||||
|
||||
state->payload_algid = algidhex;
|
||||
state->payload_keyid = kidhex;
|
||||
|
||||
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
||||
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
||||
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
||||
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
||||
state->payload_miP = (mihex1 << 32) | (mihex2);
|
||||
|
||||
}
|
||||
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
if (irrecoverable_errors == 0)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX", algidhex, kidhex, mihex1, mihex2, mihex3);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
state->payload_algid = algidhex;
|
||||
state->payload_keyid = kidhex;
|
||||
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
||||
state->payload_miP = (mihex1 << 32) | (mihex2);
|
||||
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " ENC");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " ENC \n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " LDU2 FEC ERR \n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
463
src/p25p1_mdpu.c
Normal file
463
src/p25p1_mdpu.c
Normal file
@@ -0,0 +1,463 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* p25p1_mpdu.c
|
||||
* P25p1 Multi Block PDU and Multi Block Trunking
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2023-03 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
static uint32_t crc32mbf(uint8_t buf[], int len)
|
||||
{
|
||||
uint32_t g = 0x04c11db7;
|
||||
uint64_t crc = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
crc <<= 1;
|
||||
int b = ( buf [i / 8] >> (7 - (i % 8)) ) & 1;
|
||||
if (((crc >> 32) ^ b) & 1) crc ^= g;
|
||||
}
|
||||
crc = (crc & 0xffffffff) ^ 0xffffffff;
|
||||
return crc;
|
||||
}
|
||||
|
||||
void processMPDU(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int tsbkbit[196]; //tsbk bit array, 196 trellis encoded bits
|
||||
int tsbk_dibit[98];
|
||||
|
||||
memset (tsbkbit, 0, sizeof(tsbkbit));
|
||||
memset (tsbk_dibit, 0, sizeof(tsbk_dibit));
|
||||
|
||||
int dibit = 0;
|
||||
|
||||
uint8_t tsbk_byte[12]; //12 byte return from bd_bridge (block_deinterleave)
|
||||
memset (tsbk_byte, 0, sizeof(tsbk_byte));
|
||||
|
||||
unsigned long long int PDU[24];
|
||||
memset (PDU, 0, sizeof(PDU));
|
||||
|
||||
int tsbk_decoded_bits[190]; //decoded bits from tsbk_bytes for sending to crc16_lb_bridge
|
||||
memset (tsbk_decoded_bits, 0, sizeof(tsbk_decoded_bits));
|
||||
|
||||
uint8_t mpdu_decoded_bits[288];
|
||||
memset (mpdu_decoded_bits, 0, sizeof(mpdu_decoded_bits));
|
||||
|
||||
uint8_t mpdu_crc_bits[196];
|
||||
memset (mpdu_crc_bits, 0, sizeof(mpdu_crc_bits));
|
||||
|
||||
uint8_t mpdu_crc_bytes[24];
|
||||
memset (mpdu_crc_bytes, 0, sizeof(mpdu_crc_bytes));
|
||||
|
||||
int flushing_bits[196]; //for flushing the trellis state machine
|
||||
memset (flushing_bits, 0, sizeof(flushing_bits));
|
||||
|
||||
int i, j, k, b, x, y;
|
||||
int ec[3]; //error value returned from (block_deinterleave)
|
||||
int err[2]; //error value returned from ccrc16 or crc32
|
||||
memset (ec, -2, sizeof(ec));
|
||||
memset (err, -2, sizeof(err));
|
||||
|
||||
int skipdibit = 14; //initial status dibit will occur at 14, then add 36 each time it occurs
|
||||
int protectbit = 0;
|
||||
int MFID = 0xFF; //Manufacturer ID
|
||||
|
||||
uint8_t mpdu_byte[36];
|
||||
memset (mpdu_byte, 0, sizeof(mpdu_byte));
|
||||
|
||||
uint8_t io = 0;
|
||||
uint8_t fmt = 0;
|
||||
uint8_t sap = 0;
|
||||
uint8_t blks = 0;
|
||||
uint8_t opcode = 0;
|
||||
|
||||
//CRC32
|
||||
uint32_t CRCComputed = 0;
|
||||
uint32_t CRCExtracted = 0;
|
||||
|
||||
//collect three reps of 101 dibits (98 valid dibits with status dibits interlaced)
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
k = 0;
|
||||
for (i = 0; i < 101; i++)
|
||||
{
|
||||
|
||||
dibit = getDibit(opts, state);
|
||||
|
||||
if (i+(j*101) != skipdibit) //
|
||||
{
|
||||
tsbk_dibit[k] = dibit;
|
||||
tsbkbit[k*2] = (dibit >> 1) & 1;
|
||||
tsbkbit[k*2+1] = (dibit & 1);
|
||||
k++;
|
||||
}
|
||||
|
||||
if (i+(j*101) == skipdibit)
|
||||
{
|
||||
skipdibit += 36; //was set to 35, this was the bug
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//flushing the trellis state machine
|
||||
if (j == 0)
|
||||
{
|
||||
bd_bridge(flushing_bits, tsbk_byte);
|
||||
//reset tsbk_byte afterwards
|
||||
memset (tsbk_byte, 0, sizeof(tsbk_byte));
|
||||
}
|
||||
|
||||
//send tsbkbit to block_deinterleave and return tsbk_byte
|
||||
ec[j] = bd_bridge(tsbkbit, tsbk_byte);
|
||||
|
||||
//too many bit manipulations!
|
||||
k = 0;
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
for (x = 0; x < 8; x++)
|
||||
{
|
||||
tsbk_decoded_bits[k] = ((tsbk_byte[i] << x) & 0x80) >> 7;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
//CRC16 on the header
|
||||
if (j == 0) err[0] = crc16_lb_bridge(tsbk_decoded_bits, 80);
|
||||
|
||||
//load into bit array for storage (easier decoding for future PDUs)
|
||||
for (i = 0; i < 96; i++) mpdu_decoded_bits[i+(j*96)] = (uint8_t)tsbk_decoded_bits[i];
|
||||
|
||||
//shuffle corrected bits back into tsbk_byte
|
||||
k = 0;
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
int byte = 0;
|
||||
for (x = 0; x < 8; x++)
|
||||
{
|
||||
byte = byte << 1;
|
||||
byte = byte | tsbk_decoded_bits[k];
|
||||
k++;
|
||||
}
|
||||
tsbk_byte[i] = byte;
|
||||
mpdu_byte[i+(j*12)] = byte; //add to completed MBF format bytes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
io = (mpdu_byte[0] >> 5) & 0x1;
|
||||
fmt = mpdu_byte[0] & 0x1F;
|
||||
sap = mpdu_byte[1] & 0x3F;
|
||||
blks = mpdu_byte[6] & 0x7F;
|
||||
MFID = mpdu_byte[2];
|
||||
|
||||
//group list mode so we can look and see if we need to block tuning any groups, etc
|
||||
char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
fprintf (stderr, "%s",KGRN);
|
||||
fprintf (stderr, " P25 MBF FMT: %02X SAP: %02X", fmt, sap);
|
||||
fprintf (stderr, " Blocks: %02X MFID: %02X", blks, MFID);
|
||||
|
||||
//trunking blocks
|
||||
if ((sap == 0x3D) && ((fmt == 0x17) || (fmt == 0x15)))
|
||||
{
|
||||
if (fmt == 0x15) fprintf (stderr, " UNC");
|
||||
else fprintf (stderr, " ALT");
|
||||
fprintf (stderr, " MBT"); //find way to convert to vPDU or TSBK format?
|
||||
if (fmt == 0x17) opcode = mpdu_byte[7] & 0x3F; //alt MBT
|
||||
else opcode = mpdu_byte[12] & 0x3F; //unconf MBT
|
||||
fprintf (stderr, " - OP: %02X", opcode);
|
||||
}
|
||||
|
||||
//CRC32 is now working!
|
||||
for (i = 0; i < 196; i++) mpdu_crc_bits[i] = mpdu_decoded_bits[i+96];
|
||||
for (i = 0; i < 24; i++) mpdu_crc_bytes[i] = mpdu_byte[i+12];
|
||||
if (blks == 1) //blocks to follow is 1
|
||||
{
|
||||
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&mpdu_crc_bits[96-32], 32);
|
||||
CRCComputed = crc32mbf(mpdu_crc_bytes, 96-32);
|
||||
if (CRCComputed == CRCExtracted) err[1] = 0;
|
||||
}
|
||||
if (blks == 2) //blocks to follow is 2
|
||||
{
|
||||
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&mpdu_crc_bits[192-32], 32);
|
||||
CRCComputed = crc32mbf(mpdu_crc_bytes, 192-32);
|
||||
if (CRCComputed == CRCExtracted) err[1] = 0;
|
||||
}
|
||||
|
||||
//just going to do a couple for now -- the extended format will not be vPDU compatible :(
|
||||
if (err[0] == 0 && err[1] == 0 && io == 1 && fmt == 0x17) //ALT Format
|
||||
{
|
||||
//NET_STS_BCST -- TIA-102.AABC-D 6.2.11.2
|
||||
if (opcode == 0x3B)
|
||||
{
|
||||
int lra = mpdu_byte[3];
|
||||
int sysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
|
||||
int res_a = mpdu_byte[8];
|
||||
int res_b = mpdu_byte[9];
|
||||
long int wacn = (mpdu_byte[12] << 12) | (mpdu_byte[13] << 4) | (mpdu_byte[14] >> 4);
|
||||
int channelt = (mpdu_byte[15] << 8) | mpdu_byte[16];
|
||||
int channelr = (mpdu_byte[17] << 8) | mpdu_byte[18];
|
||||
int ssc = mpdu_byte[19];
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
fprintf (stderr, "\n Network Status Broadcast MBT - Extended \n");
|
||||
fprintf (stderr, " LRA [%02X] WACN [%05lX] SYSID [%03X] NAC [%03llX]\n", lra, wacn, sysid, state->p2_cc);
|
||||
fprintf (stderr, " CHAN-T [%04X] CHAN-R [%04X]", channelt, channelr);
|
||||
long int ct_freq = process_channel_to_freq(opts, state, channelt);
|
||||
long int cr_freq = process_channel_to_freq(opts, state, channelr);
|
||||
|
||||
state->p25_cc_freq = ct_freq;
|
||||
state->p25_cc_is_tdma = 0; //flag off for CC tuning purposes when system is qpsk
|
||||
|
||||
//place the cc freq into the list at index 0 if 0 is empty, or not the same,
|
||||
//so we can hunt for rotating CCs without user LCN list
|
||||
if (state->trunk_lcn_freq[0] == 0 || state->trunk_lcn_freq[0] != state->p25_cc_freq)
|
||||
{
|
||||
state->trunk_lcn_freq[0] = state->p25_cc_freq;
|
||||
}
|
||||
|
||||
//only set IF these values aren't already hard set by the user
|
||||
if (state->p2_hardset == 0)
|
||||
{
|
||||
state->p2_wacn = wacn;
|
||||
state->p2_sysid = sysid;
|
||||
}
|
||||
}
|
||||
//RFSS Status Broadcast - Extended 6.2.15.2
|
||||
else if (opcode == 0x3A)
|
||||
{
|
||||
int lra = mpdu_byte[3];
|
||||
int lsysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
|
||||
int rfssid = mpdu_byte[12];
|
||||
int siteid = mpdu_byte[13];
|
||||
int channelt = (mpdu_byte[14] << 8) | mpdu_byte[15];
|
||||
int channelr = (mpdu_byte[16] << 8) | mpdu_byte[17];
|
||||
int sysclass = mpdu_byte[18];
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
fprintf (stderr, "\n RFSS Status Broadcast MBF - Extended \n");
|
||||
fprintf (stderr, " LRA [%02X] SYSID [%03X] RFSS ID [%02X] SITE ID [%02X]\n CHAN-T [%04X] CHAN-R [%02X] SSC [%02X] ", lra, lsysid, rfssid, siteid, channelt, channelr, sysclass);
|
||||
process_channel_to_freq (opts, state, channelt);
|
||||
process_channel_to_freq (opts, state, channelr);
|
||||
|
||||
state->p2_siteid = siteid;
|
||||
state->p2_rfssid = rfssid;
|
||||
}
|
||||
|
||||
//Adjacent Status Broadcast (ADJ_STS_BCST) Extended 6.2.2.2
|
||||
else if (opcode == 0x3C)
|
||||
{
|
||||
int lra = mpdu_byte[3];
|
||||
int cfva = mpdu_byte[4] >> 4;
|
||||
int lsysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
|
||||
int rfssid = mpdu_byte[8];
|
||||
int siteid = mpdu_byte[9];
|
||||
int channelt = (mpdu_byte[12] << 8) | mpdu_byte[13];
|
||||
int channelr = (mpdu_byte[14] << 8) | mpdu_byte[15];
|
||||
int sysclass = mpdu_byte[16];
|
||||
long int wacn = (mpdu_byte[17] << 12) | (mpdu_byte[18] << 4) | (mpdu_byte[19] >> 4);
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
fprintf (stderr, "\n Adjacent Status Broadcast - Extended\n");
|
||||
fprintf (stderr, " LRA [%02X] CFVA [%X] RFSS[%03d] SYSID [%03X] SITE [%03X]\n CHAN-T [%04X] CHAN-R [%04X] SSC [%02X] WACN [%05lX]\n ", lra, cfva, rfssid, lsysid, siteid, channelt, channelr, sysclass, wacn);
|
||||
if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||
if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||
if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||
else fprintf (stderr, " Last Known");
|
||||
if (cfva & 0x1) fprintf (stderr, " Valid RFSS Connection Active");
|
||||
process_channel_to_freq (opts, state, channelt);
|
||||
process_channel_to_freq (opts, state, channelr);
|
||||
|
||||
}
|
||||
|
||||
//Group Voice Channel Grant - Extended
|
||||
else if (opcode == 0x0)
|
||||
{
|
||||
int svc = mpdu_byte[8]; //look up what these do!
|
||||
int channelt = (mpdu_byte[14] << 8) | mpdu_byte[15];
|
||||
int channelr = (mpdu_byte[16] << 8) | mpdu_byte[17];
|
||||
long int source = (mpdu_byte[3] << 16) |(mpdu_byte[4] << 8) | mpdu_byte[5];
|
||||
int group = (mpdu_byte[18] << 8) | mpdu_byte[19];
|
||||
long int freq1 = 0;
|
||||
long int freq2 = 0;
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
fprintf (stderr, "\n Group Voice Channel Grant Update - Extended");
|
||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc, channelt, channelr, group, group);
|
||||
freq1 = process_channel_to_freq (opts, state, channelt);
|
||||
freq2 = process_channel_to_freq (opts, state, channelr);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[0], "Active Ch: %04X TG: %d", channelt, group);
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == group)
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||
|
||||
//tune if tuning available
|
||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
||||
{
|
||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[channelt >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
SetFreq(opts->rigctl_sockfd, freq1);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
}
|
||||
//rtl_udp
|
||||
else if (opts->audio_in_type == 3)
|
||||
{
|
||||
rtl_udp_tune (opts, state, freq1);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Unit to Unit Voice Channel Grant - Extended
|
||||
else if (opcode == 0x6)
|
||||
{
|
||||
//I'm not doing EVERY element of this, just enough for tuning!
|
||||
int svc = mpdu_byte[8]; //look up what these do!
|
||||
int channelt = (mpdu_byte[22] << 8) | mpdu_byte[23];
|
||||
int channelr = (mpdu_byte[24] << 8) | mpdu_byte[25]; //optional!
|
||||
//using source and target address, not source and target id (is this correct?)
|
||||
long int source = (mpdu_byte[3] << 16) |(mpdu_byte[4] << 8) | mpdu_byte[5];
|
||||
long int target = (mpdu_byte[19] << 16) |(mpdu_byte[20] << 8) | mpdu_byte[21];
|
||||
long int freq1 = 0;
|
||||
long int freq2 = 0;
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
fprintf (stderr, "\n Unit to Unit Voice Channel Grant Update - Extended");
|
||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Source [%ld][%04lX] Target [%ld][%04lX]", svc, channelt, channelr, source, source, target, target);
|
||||
freq1 = process_channel_to_freq (opts, state, channelt);
|
||||
freq2 = process_channel_to_freq (opts, state, channelr); //optional!
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[0], "Active Ch: %04X TGT: %ld;", channelt, target);
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == target)
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Skip tuning private calls if group calls are disabled
|
||||
if (opts->trunk_tune_private_calls == 0) goto SKIPCALL;
|
||||
|
||||
//tune if tuning available
|
||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
|
||||
{
|
||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[channelt >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
SetFreq(opts->rigctl_sockfd, freq1);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
}
|
||||
//rtl_udp
|
||||
else if (opts->audio_in_type == 3)
|
||||
{
|
||||
rtl_udp_tune (opts, state, freq1);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//convert mpdu_byte to vPDU format and get indication of what the message was
|
||||
PDU[0] = 0x0C; //P25p1 MB Duid 0x0C
|
||||
PDU[1] = opcode;
|
||||
PDU[2] = MFID;
|
||||
PDU[1] = PDU[1] ^ 0xC0; //flip bits to make it compatible with MAC_PDUs explicit format
|
||||
|
||||
//without any extra bytes filled in, we can atleast see what the MBT is supposed to be
|
||||
//Hopefully, no 0 value things will cause issues -- call grants, iden_up, etc!
|
||||
// fprintf (stderr, "%s",KYEL);
|
||||
// process_MAC_VPDU(opts, state, 0, PDU);
|
||||
// fprintf (stderr, "%s",KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
SKIPCALL: ; //do nothing
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, "\n P25 MBF Payload \n ");
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", mpdu_byte[i]);
|
||||
if (i == 11 || i == 23) fprintf (stderr, "\n ");
|
||||
|
||||
}
|
||||
fprintf (stderr, " CRC EXT %08X CMP %08X", CRCExtracted, CRCComputed);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
fprintf (stderr, "\n ");
|
||||
|
||||
//Header
|
||||
if (err[0] != 0)
|
||||
{
|
||||
fprintf (stderr, "%s",KRED);
|
||||
fprintf (stderr, " (HDR CRC16 ERR)");
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
}
|
||||
//Completed MBF
|
||||
if (err[1] != 0)
|
||||
{
|
||||
fprintf (stderr, "%s",KRED);
|
||||
fprintf (stderr, " (MBF CRC32 ERR)");
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
@@ -33,4 +33,8 @@ processTDU (dsd_opts* opts, dsd_state* state)
|
||||
status = getDibit (opts, state) + '0';
|
||||
// TODO: do something useful with the status bits...
|
||||
}
|
||||
|
||||
//reset some strings -- since its a tdu, blank out any call strings, only want during actual call
|
||||
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||
}
|
||||
|
||||
@@ -390,11 +390,60 @@ processTDULC (dsd_opts* opts, dsd_state* state)
|
||||
lcinfo[53] = dodeca_data[0][ 9] + '0';
|
||||
lcinfo[54] = dodeca_data[0][10] + '0';
|
||||
lcinfo[55] = dodeca_data[0][11] + '0';
|
||||
/*
|
||||
if (state->carrier == 1) //only update when carrier is present, otherwise, garbage values may be collected
|
||||
|
||||
int j;
|
||||
uint8_t LCW_bytes[9];
|
||||
uint8_t LCW_bits[72];
|
||||
memset (LCW_bytes, 0, sizeof(LCW_bytes));
|
||||
memset (LCW_bits, 0, sizeof(LCW_bits));
|
||||
|
||||
//load array above into byte form first
|
||||
LCW_bytes[0] = (uint8_t) ConvertBitIntoBytes(&lcformat[0], 8);
|
||||
LCW_bytes[1] = (uint8_t) ConvertBitIntoBytes(&mfid[0], 8);
|
||||
for (i = 0; i < 7; i++) LCW_bytes[i+2] = (uint8_t) ConvertBitIntoBytes(&lcinfo[i*8], 8);
|
||||
|
||||
//load as bit array next (easier to process data in bit form)
|
||||
for(i = 0, j = 0; i < 9; i++, j+=8)
|
||||
{
|
||||
state->payload_mfid = strtol (mfid, NULL, 2); //wonder if this is accurate info or not
|
||||
LCW_bits[j + 0] = (LCW_bytes[i] >> 7) & 0x01;
|
||||
LCW_bits[j + 1] = (LCW_bytes[i] >> 6) & 0x01;
|
||||
LCW_bits[j + 2] = (LCW_bytes[i] >> 5) & 0x01;
|
||||
LCW_bits[j + 3] = (LCW_bytes[i] >> 4) & 0x01;
|
||||
LCW_bits[j + 4] = (LCW_bytes[i] >> 3) & 0x01;
|
||||
LCW_bits[j + 5] = (LCW_bytes[i] >> 2) & 0x01;
|
||||
LCW_bits[j + 6] = (LCW_bytes[i] >> 1) & 0x01;
|
||||
LCW_bits[j + 7] = (LCW_bytes[i] >> 0) & 0x01;
|
||||
}
|
||||
*/
|
||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
||||
|
||||
//send to new P25 LCW function
|
||||
if (irrecoverable_errors == 0)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
p25_lcw (opts, state, LCW_bits, 0);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s",KRED);
|
||||
fprintf (stderr, " LCW FEC ERR ");
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, " P25 LCW Payload ");
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", LCW_bytes[i]);
|
||||
}
|
||||
|
||||
|
||||
fprintf (stderr, "%s\n", KNRM);
|
||||
}
|
||||
|
||||
//reset some strings -- since its a tdu, blank out any call strings, only want during actual call
|
||||
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||
|
||||
}
|
||||
|
||||
@@ -7,16 +7,30 @@
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int tsbkbit[196] = {0}; //tsbk bit array, 196 trellis encoded bits
|
||||
int tsbk_dibit[98] = {0};
|
||||
int tsbkbit[196]; //tsbk bit array, 196 trellis encoded bits
|
||||
int tsbk_dibit[98];
|
||||
|
||||
memset (tsbkbit, 0, sizeof(tsbkbit));
|
||||
memset (tsbk_dibit, 0, sizeof(tsbk_dibit));
|
||||
|
||||
int dibit = 0;
|
||||
uint8_t tsbk_byte[12] = {0}; //12 byte return from bd_bridge (block_deinterleave)
|
||||
unsigned long long int PDU[24] = {0}; //24 byte PDU to send to the tsbk_byte vPDU handler, should be same formats (mostly)
|
||||
int tsbk_decoded_bits[190] = {0}; //decoded bits from tsbk_bytes for sending to crc16_lb_bridge
|
||||
|
||||
uint8_t tsbk_byte[12]; //12 byte return from bd_bridge (block_deinterleave)
|
||||
memset (tsbk_byte, 0, sizeof(tsbk_byte));
|
||||
|
||||
unsigned long long int PDU[24]; //24 byte PDU to send to the tsbk_byte vPDU handler, should be same formats (mostly)
|
||||
memset (PDU, 0, sizeof(PDU));
|
||||
|
||||
int tsbk_decoded_bits[190]; //decoded bits from tsbk_bytes for sending to crc16_lb_bridge
|
||||
memset (tsbk_decoded_bits, 0, sizeof(tsbk_decoded_bits));
|
||||
|
||||
int flushing_bits[196]; //for flushing the trellis state machine
|
||||
memset (flushing_bits, 0, sizeof(flushing_bits));
|
||||
|
||||
int i, j, k, b, x, y;
|
||||
int ec = -2; //error value returned from (block_deinterleave)
|
||||
int err = -2; //error value returned from crc16_lb_bridge
|
||||
@@ -54,6 +68,14 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
|
||||
}
|
||||
|
||||
//flushing the trellis state machine
|
||||
if (j == 0)
|
||||
{
|
||||
bd_bridge(flushing_bits, tsbk_byte);
|
||||
//reset tsbk_byte afterwards
|
||||
memset (tsbk_byte, 0, sizeof(tsbk_byte));
|
||||
}
|
||||
|
||||
//send tsbkbit to block_deinterleave and return tsbk_byte
|
||||
ec = bd_bridge(tsbkbit, tsbk_byte);
|
||||
|
||||
@@ -84,7 +106,7 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
tsbk_byte[i] = byte;
|
||||
}
|
||||
|
||||
//convert tsbk_byte to PDU and send to vPDU handler
|
||||
//convert tsbk_byte to vPDU and send to vPDU handler
|
||||
//...may or may not be entirely compatible,
|
||||
MFID = tsbk_byte[1];
|
||||
PDU[0] = 0x07; //P25p1 TSBK Duid 0x07
|
||||
@@ -106,7 +128,10 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
protectbit = (tsbk_byte[0] >> 6) & 0x1;
|
||||
|
||||
//Don't run NET_STS out of this, or will set wrong NAC/CC //&& PDU[1] != 0x49 && PDU[1] != 0x45
|
||||
//0x49 is telephone grant, 0x46 Unit to Unit Channel Answer Request (seems bogus)
|
||||
//0x49 is telephone grant, 0x46 Unit to Unit Channel Answer Request (seems bogus) (mfid 90)
|
||||
// if (MFID < 0x2 && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
||||
//running with A4 and 90 causing some false positives when sending to vPDU, issuing bad call grants to nowhere!
|
||||
// if ( (MFID < 0x2 || MFID == 0xA4 || MFID == 0x90) && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
||||
if (MFID < 0x2 && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
||||
{
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
@@ -115,9 +140,9 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
//set our WACN and SYSID here now that we have valid ec and crc/checksum
|
||||
if (protectbit == 0 && err == 0 && ec == 0 && (tsbk_byte[0] & 0x3F) == 0x3B)
|
||||
else if (protectbit == 0 && err == 0 && ec == 0 && (tsbk_byte[0] & 0x3F) == 0x3B)
|
||||
{
|
||||
long int wacn = (tsbk_byte[3] << 12) | (tsbk_byte[4] << 4) | (tsbk_byte[5] >> 4);;
|
||||
long int wacn = (tsbk_byte[3] << 12) | (tsbk_byte[4] << 4) | (tsbk_byte[5] >> 4);
|
||||
int sysid = ((tsbk_byte[5] & 0xF) << 8) | tsbk_byte[6];
|
||||
int channel = (tsbk_byte[7] << 8) | tsbk_byte[8];
|
||||
fprintf (stderr, "%s",KYEL);
|
||||
@@ -143,16 +168,43 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, "\n P25 PDU Payload ");
|
||||
fprintf (stderr, "\n P25 PDU Payload #%d ", j);
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", tsbk_byte[i]);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, "\n P25 PDU Payload #%d ", j+1);
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", tsbk_byte[i]);
|
||||
}
|
||||
fprintf (stderr, "\n MFID %02X Protected: %d Last Block: %d", MFID, protectbit, (tsbk_byte[0] >> 7) );
|
||||
|
||||
if (ec != 0)
|
||||
{
|
||||
fprintf (stderr, "%s",KRED);
|
||||
fprintf (stderr, " (FEC ERR)");
|
||||
}
|
||||
else if (err != 0)
|
||||
{
|
||||
fprintf (stderr, "%s",KRED);
|
||||
fprintf (stderr, " (CRC ERR)");
|
||||
}
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
//reset for next rep
|
||||
ec = -2;
|
||||
err = -2;
|
||||
@@ -165,5 +217,7 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
j = 4; //set j to break the loop early
|
||||
}
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
@@ -711,12 +711,23 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else fprintf (stderr, "VCH S ");
|
||||
|
||||
//check to see when last voice activity occurred in order to allow tuning on phase 2
|
||||
//mac_signal or mac_idle when no more voice activity on current channel
|
||||
//this is primarily a fix for TDMA control channels that carry voice (Duke P25)
|
||||
//but may also allow for chain tuning without returning to the control channel <--may be problematic since we can assign a p25_cc_freq from the pdu
|
||||
if (duid_decoded == 13 && opts->p25_is_tuned == 1 && ((time(NULL) - state->last_vc_sync_time) > opts->trunk_hangtime) ) //version for MAC_SIGNAL only, no idle
|
||||
{
|
||||
opts->p25_is_tuned = 0;
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||
}
|
||||
|
||||
if (duid_decoded == 0)
|
||||
{
|
||||
fprintf (stderr, " 4V %d", state->fourv_counter[state->currentslot]+1);
|
||||
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0 &&
|
||||
state->p2_wacn != 0xFFFFF && state->p2_cc != 0xFFF && state->p2_sysid != 0xFFF)
|
||||
{
|
||||
state->last_vc_sync_time = time(NULL);
|
||||
process_4V (opts, state);
|
||||
}
|
||||
}
|
||||
@@ -726,6 +737,7 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state)
|
||||
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0 &&
|
||||
state->p2_wacn != 0xFFFFF && state->p2_cc != 0xFFF && state->p2_sysid != 0xFFF)
|
||||
{
|
||||
state->last_vc_sync_time = time(NULL);
|
||||
process_2V (opts, state);
|
||||
}
|
||||
}
|
||||
@@ -823,22 +835,21 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state)
|
||||
void processP2 (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
state->dmr_stereo = 1;
|
||||
//state->currentslot = 1;
|
||||
|
||||
p2_dibit_buffer (opts, state);
|
||||
|
||||
//look at our ISCH values and determine location in superframe before running frame scramble
|
||||
for (framing_counter = 0; framing_counter < 4; framing_counter++) //12
|
||||
for (framing_counter = 0; framing_counter < 4; framing_counter++)
|
||||
{
|
||||
process_ISCH (opts, state); //run ISCH in here so we know when to start descramble offset
|
||||
//run ISCH in here so we know when to start descramble offset
|
||||
process_ISCH (opts, state);
|
||||
}
|
||||
|
||||
//set initial current slot depending on offset value
|
||||
if (state->p2_scramble_offset % 2)
|
||||
{
|
||||
state->currentslot = 0;
|
||||
state->currentslot = 1;
|
||||
}
|
||||
else state->currentslot = 1;
|
||||
else state->currentslot = 0;
|
||||
|
||||
//frame_scramble runs lfsr and creates an array of unscrambled bits to pull from
|
||||
process_Frame_Scramble (opts, state);
|
||||
|
||||
327
src/p25p2_vpdu.c
327
src/p25p2_vpdu.c
@@ -84,7 +84,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
|
||||
//MFID90 Voice Grants, A3, A4, and A5
|
||||
//MFID90 Voice Grants, A3, A4, and A5 <--I bet A4 here was triggering a phantom call when TSBK sent PDUs here
|
||||
//MFID90 Group Regroup Channel Grant - Implicit
|
||||
if (MAC[1+len_a] == 0xA3 && MAC[2+len_a] == 0x90)
|
||||
{
|
||||
@@ -96,6 +96,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
fprintf (stderr, "\n CHAN [%04X] Group [%d][%04X]", channel, sgroup, sgroup);
|
||||
freq = process_channel_to_freq (opts, state, channel);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Active Ch: %04X SG: %d;", channel, sgroup);
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == sgroup)
|
||||
@@ -114,16 +117,27 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
// //testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[channel >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[channel >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[channel >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
@@ -164,6 +178,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
fprintf (stderr, "\n CHAN [%04X] Group [%d][%04X]", channel, sgroup, sgroup);
|
||||
freq = process_channel_to_freq (opts, state, channel);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Active Ch: %04X SG: %d", channel, sgroup);
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == sgroup)
|
||||
@@ -183,14 +200,24 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[channel >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[channel >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[channel >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->use_rigctl == 1)
|
||||
@@ -239,6 +266,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Active Ch: %04X SG: %d; Ch: %04X SG: %d;", channel1, group1, channel2, group2);
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||
|
||||
@@ -284,15 +314,26 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
if (opts->mod_qpsk == 1)
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[tunable_chan >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
@@ -343,6 +384,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
fprintf (stderr, "\n SVC [%02X] CHAN [%04X] Group [%d] Source [%d]", svc, channel, group, source);
|
||||
freq = process_channel_to_freq (opts, state, channel);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Active Ch: %04X TG: %d;", channel, group);
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == group)
|
||||
@@ -362,15 +406,26 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[channel >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[channel >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[channel >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
@@ -403,7 +458,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
}
|
||||
|
||||
//Unit-to-Unit Voice Service Channel Grant (UU_V_CH_GRANT), or Grant Update (same format)
|
||||
if (MAC[1+len_a] == 0x44 || MAC[1+len_a] == 0x46)
|
||||
if (MAC[1+len_a] == 0x44 || MAC[1+len_a] == 0x46) //double check these opcodes
|
||||
{
|
||||
int channel = (MAC[2+len_a] << 8) | MAC[3+len_a];
|
||||
int target = (MAC[4+len_a] << 16) | (MAC[5+len_a] << 8) | MAC[6+len_a];
|
||||
@@ -415,6 +470,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
fprintf (stderr, "\n CHAN [%04X] Source [%d] Target [%d]", channel, source, target);
|
||||
freq = process_channel_to_freq (opts, state, channel);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Active Ch: %04X TGT: %d;", channel, target);
|
||||
|
||||
//Skip tuning private calls if private calls is disabled
|
||||
if (opts->trunk_tune_private_calls == 0) goto SKIPCALL;
|
||||
|
||||
@@ -435,15 +493,26 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[channel >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[channel >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[channel >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
@@ -497,6 +566,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
freq1t = process_channel_to_freq (opts, state, channelt2);
|
||||
freq1r = process_channel_to_freq (opts, state, channelr2);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Active Ch: %04X TG: %d; Ch: %04X TG: %d;", channelt1, group1, channelt2, group2);
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||
|
||||
@@ -539,14 +611,24 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[tunable_chan >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->use_rigctl == 1)
|
||||
@@ -581,7 +663,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
}
|
||||
|
||||
//Group Voice Channel Grant Update Multiple - Implicit
|
||||
if (MAC[1+len_a] == 0x05) //wonder if this is MAC_SIGNAL only? Too long for a TSBK
|
||||
if (MAC[1+len_a] == 0x05)
|
||||
{
|
||||
int so1 = MAC[2+len_a];
|
||||
int channel1 = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
||||
@@ -604,6 +686,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
fprintf (stderr, "\n Channel 3 [%04X] Group 3 [%d][%04X]", channel3, group3, group3);
|
||||
freq3 = process_channel_to_freq (opts, state, channel3);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Active Ch: %04X TG: %d; Ch: %04X TG: %d; Ch: %04X TG: %d;", channel1, group1, channel2, group2, channel3, group3);
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||
|
||||
@@ -651,15 +736,26 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
if (opts->mod_qpsk == 1)
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[tunable_chan >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
@@ -710,6 +806,9 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
||||
|
||||
//add active channel to string for ncurses display
|
||||
sprintf (state->dmr_lrrp_gps[slot], "Active Ch: %04X TG: %d; Ch: %04X TG: %d;", channel1, group1, channel2, group2);
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
|
||||
|
||||
@@ -753,15 +852,26 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
if (opts->mod_qpsk == 1)
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[tunable_chan >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[tunable_chan >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
@@ -810,11 +920,13 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc, channelt, channelr, group, group);
|
||||
freq1 = process_channel_to_freq (opts, state, channelt);
|
||||
freq2 = process_channel_to_freq (opts, state, channelr);
|
||||
if (slot == 0)
|
||||
{
|
||||
state->lasttg = group;
|
||||
}
|
||||
else state->lasttgR = group;
|
||||
|
||||
//don't set the tg here, one multiple grant updates, will mess up the TG value on current call
|
||||
// if (slot == 0)
|
||||
// {
|
||||
// state->lasttg = group;
|
||||
// }
|
||||
// else state->lasttgR = group;
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
@@ -835,15 +947,26 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
|
||||
{
|
||||
//testing switch to P2 channel symbol rate with qpsk enabled, we need to know if we are going to a TDMA channel or an FDMA channel
|
||||
// if (opts->mod_qpsk == 1)
|
||||
// {
|
||||
// int spacing = state->p25_chan_spac[channelt >> 12];
|
||||
// if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
// {
|
||||
// state->samplesPerSymbol = 8;
|
||||
// state->symbolCenter = 3;
|
||||
// }
|
||||
// }
|
||||
|
||||
//may be this way independent of whether or not this is QPSK (does C4FM run at 6000 sps?)
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
int spacing = state->p25_chan_spac[channelt >> 12];
|
||||
if (spacing == 0x64) //tdma should always be 0x64, and fdma should always be 0x32
|
||||
if (state->p25_chan_tdma[channelt >> 12] == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 8;
|
||||
state->symbolCenter = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rigctl
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
@@ -940,7 +1063,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
}
|
||||
|
||||
//Unit-to-Unit Answer Request (UU_ANS_REQ)
|
||||
if (MAC[1+len_a] == 0x45)
|
||||
if (MAC[1+len_a] == 0x45 && MAC[2+len_a] != 0x90)
|
||||
{
|
||||
int svc = MAC[2+len_a];
|
||||
int answer = MAC[3+len_a];
|
||||
@@ -952,17 +1075,17 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
|
||||
}
|
||||
|
||||
//Telephone Interconnect Voice Channel Grant (TELE_INT_CH_GRANT)
|
||||
if (MAC[1+len_a] == 0x48)
|
||||
{
|
||||
fprintf (stderr, "\n Telephone Interconnect Voice Channel Grant ");
|
||||
}
|
||||
//Telephone Interconnect Voice Channel Grant (TELE_INT_CH_GRANT) //look at PDU for this, see if this is tunable
|
||||
// if (MAC[1+len_a] == 0x48 && MAC[2+len_a] != 0x90)
|
||||
// {
|
||||
// fprintf (stderr, "\n Telephone Interconnect Voice Channel Grant ");
|
||||
// }
|
||||
|
||||
//Telephone Interconnect Voice Channel Grant Update (TELE_INT_CH_GRANT_UPDT)
|
||||
if (MAC[1+len_a] == 0x49)
|
||||
{
|
||||
fprintf (stderr, "\n Telephone Interconnect Voice Channel Grant Update ");
|
||||
}
|
||||
// //Telephone Interconnect Voice Channel Grant Update (TELE_INT_CH_GRANT_UPDT)
|
||||
// if (MAC[1+len_a] == 0x49 && MAC[2+len_a] != 0x90)
|
||||
// {
|
||||
// fprintf (stderr, "\n Telephone Interconnect Voice Channel Grant Update ");
|
||||
// }
|
||||
|
||||
//Synchronization Broadcast (SYNC_BCST)
|
||||
if (MAC[1+len_a] == 0x70)
|
||||
@@ -986,12 +1109,12 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
else offhour = ( (ltoff & 0b11111 ) / 2);
|
||||
|
||||
int seconds = slots / 135; //very rough estimation, but may be close enough for grins
|
||||
if (seconds > 60) seconds = 60; //sanity check for rounding error
|
||||
if (seconds > 59) seconds = 59; //sanity check for rounding error
|
||||
fprintf (stderr, " Date: 20%02d.%02d.%02d Time: %02d:%02d:%02d UTC\n",
|
||||
year, month, day, hour, min, seconds);
|
||||
fprintf (stderr, " Local Time Offset: %.01f Hours;", offhour);
|
||||
//if ist bit is set, then time on system may be considered invalid (i.e., no external time sync)
|
||||
if (ist % 1)
|
||||
if (ist == 1)
|
||||
{
|
||||
fprintf (stderr, " Invalid System Time ");
|
||||
}
|
||||
@@ -999,18 +1122,31 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
|
||||
}
|
||||
|
||||
//identifier update VHF/UHF
|
||||
if (MAC[1+len_a] == 0x74)
|
||||
//identifier update VHF/UHF
|
||||
if (MAC[1+len_a] == 0x74)
|
||||
{
|
||||
state->p25_chan_iden = MAC[2+len_a] >> 4;
|
||||
int iden = state->p25_chan_iden;
|
||||
|
||||
state->p25_chan_type[iden] = 1;
|
||||
int bw_vu = (MAC[2+len_a] & 0xF);
|
||||
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
||||
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
||||
state->p25_base_freq[iden] = (MAC[6+len_a] << 24) | (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | (MAC[9+len_a] << 0);
|
||||
|
||||
//this is causing more issues -- need a different way to check on this
|
||||
// if (state->p25_chan_spac[iden] == 0x64) //tdma
|
||||
// {
|
||||
// state->p25_chan_type[iden] = 4; //
|
||||
// state->p25_chan_tdma[iden] = 1; //
|
||||
// }
|
||||
// else //fdma
|
||||
// {
|
||||
// state->p25_chan_type[iden] = 1; //
|
||||
// state->p25_chan_tdma[iden] = 0; //
|
||||
// }
|
||||
|
||||
state->p25_chan_type[iden] = 1; //set as old values for now
|
||||
state->p25_chan_tdma[iden] = 0; //set as old values for now
|
||||
|
||||
fprintf (stderr, "\n Identifier Update UHF/VHF\n");
|
||||
fprintf (stderr, " Channel Identifier [%01X] BW [%01X] Transmit Offset [%04X]\n Channel Spacing [%03X] Base Frequency [%08lX] [%09ld]",
|
||||
state->p25_chan_iden, bw_vu, state->p25_trans_off[iden], state->p25_chan_spac[iden], state->p25_base_freq[iden], state->p25_base_freq[iden] * 5);
|
||||
@@ -1023,6 +1159,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
int iden = state->p25_chan_iden;
|
||||
|
||||
state->p25_chan_type[iden] = 1;
|
||||
state->p25_chan_tdma[iden] = 0;
|
||||
int bw = ((MAC[2+len_a] & 0xF) << 5) | ((MAC[3+len_a] & 0xF8) >> 2);
|
||||
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
||||
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
||||
@@ -1038,6 +1175,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
{
|
||||
state->p25_chan_iden = MAC[2+len_a] >> 4;
|
||||
int iden = state->p25_chan_iden;
|
||||
state->p25_chan_tdma[iden] = 1;
|
||||
state->p25_chan_type[iden] = MAC[2+len_a] & 0xF;
|
||||
state->p25_trans_off[iden] = (MAC[3+len_a] << 6) | (MAC[4+len_a] >> 2);
|
||||
state->p25_chan_spac[iden] = ((MAC[4+len_a] & 0x3) << 8) | MAC[5+len_a];
|
||||
@@ -1053,6 +1191,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
{
|
||||
state->p25_chan_iden = MAC[3+len_a] >> 4;
|
||||
int iden = state->p25_chan_iden;
|
||||
state->p25_chan_tdma[iden] = 1;
|
||||
state->p25_chan_type[iden] = MAC[3+len_a] & 0xF;
|
||||
state->p25_trans_off[iden] = (MAC[4+len_a] << 6) | (MAC[5+len_a] >> 2);
|
||||
state->p25_chan_spac[iden] = ((MAC[5+len_a] & 0x3) << 8) | MAC[6+len_a];
|
||||
@@ -1223,7 +1362,25 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
int src = (MAC[5+len_a] << 16) | (MAC[6+len_a] << 8) | MAC[7+len_a];
|
||||
|
||||
fprintf (stderr, "\n VCH %d - TG %d SRC %d ", slot, gr, src);
|
||||
fprintf (stderr, "Group Voice");
|
||||
|
||||
if (svc & 0x80) fprintf (stderr, " Emergency");
|
||||
if (svc & 0x40) fprintf (stderr, " Encrypted");
|
||||
|
||||
if (opts->payload == 1) //hide behind payload due to len
|
||||
{
|
||||
if (svc & 0x20) fprintf (stderr, " Duplex");
|
||||
if (svc & 0x10) fprintf (stderr, " Packet");
|
||||
else fprintf (stderr, " Circuit");
|
||||
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
|
||||
}
|
||||
|
||||
fprintf (stderr, " Group Voice");
|
||||
|
||||
sprintf (state->call_string[slot], " Group ");
|
||||
if (svc & 0x80) strcat (state->call_string[slot], " Emergency ");
|
||||
else if (svc & 0x40) strcat (state->call_string[slot], " Encrypted ");
|
||||
else strcat (state->call_string[slot], " ");
|
||||
|
||||
if (slot == 0)
|
||||
{
|
||||
@@ -1244,7 +1401,25 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
int src = (MAC[6+len_a] << 16) | (MAC[7+len_a] << 8) | MAC[8+len_a];
|
||||
|
||||
fprintf (stderr, "\n VCH %d - TG %d SRC %d ", slot, gr, src);
|
||||
fprintf (stderr, "Unit to Unit Voice");
|
||||
|
||||
if (svc & 0x80) fprintf (stderr, " Emergency");
|
||||
if (svc & 0x40) fprintf (stderr, " Encrypted");
|
||||
|
||||
if (opts->payload == 1) //hide behind payload due to len
|
||||
{
|
||||
if (svc & 0x20) fprintf (stderr, " Duplex");
|
||||
if (svc & 0x10) fprintf (stderr, " Packet");
|
||||
else fprintf (stderr, " Circuit");
|
||||
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
|
||||
}
|
||||
|
||||
fprintf (stderr, " Unit to Unit Voice");
|
||||
|
||||
sprintf (state->call_string[slot], " Private ");
|
||||
if (svc & 0x80) strcat (state->call_string[slot], " Emergency ");
|
||||
else if (svc & 0x40) strcat (state->call_string[slot], " Encrypted ");
|
||||
else strcat (state->call_string[slot], " ");
|
||||
|
||||
if (slot == 0)
|
||||
{
|
||||
@@ -1316,17 +1491,21 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (MAC[1+len_a] == 0x7C)
|
||||
{
|
||||
int lra = MAC[2+len_a];
|
||||
int cfva = MAC[3+len_a] >> 4;
|
||||
int lsysid = ((MAC[3+len_a] & 0xF) << 8) | MAC[4+len_a];
|
||||
int rfssid = MAC[5+len_a];
|
||||
int siteid = MAC[6+len_a];
|
||||
int channelt = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
||||
int sysclass = MAC[9+len_a];
|
||||
if (1 == 1) //state->p2_is_lcch == 1
|
||||
{
|
||||
fprintf (stderr, "\n Adjacent Status Broadcast - Abbreviated\n");
|
||||
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] SITE [%03X] CHAN-T [%04X] SSC [%02X]", lra, rfssid, lsysid, siteid, channelt, sysclass);
|
||||
process_channel_to_freq (opts, state, channelt);
|
||||
}
|
||||
fprintf (stderr, "\n Adjacent Status Broadcast - Abbreviated\n");
|
||||
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] SITE [%03X] CHAN-T [%04X] SSC [%02X]\n ", lra, rfssid, lsysid, siteid, channelt, sysclass);
|
||||
if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||
if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||
if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||
else fprintf (stderr, " Last Known");
|
||||
if (cfva & 0x1) fprintf (stderr, " Valid RFSS Connection Active");
|
||||
process_channel_to_freq (opts, state, channelt);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1334,19 +1513,23 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
if (MAC[1+len_a] == 0xFC)
|
||||
{
|
||||
int lra = MAC[2+len_a];
|
||||
int cfva = MAC[3+len_a] >> 4;
|
||||
int lsysid = ((MAC[3+len_a] & 0xF) << 8) | MAC[4+len_a];
|
||||
int rfssid = MAC[5+len_a];
|
||||
int siteid = MAC[6+len_a];
|
||||
int channelt = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
||||
int channelr = (MAC[9+len_a] << 8) | MAC[10+len_a];
|
||||
int sysclass = MAC[9+len_a];
|
||||
if (1 == 1) //state->p2_is_lcch == 1
|
||||
{
|
||||
fprintf (stderr, "\n Adjacent Status Broadcast - Extended\n");
|
||||
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] SITE [%03X] CHAN-T [%04X] CHAN-R [%04X] SSC [%02X]", lra, rfssid, lsysid, siteid, channelt, channelr, sysclass);
|
||||
process_channel_to_freq (opts, state, channelt);
|
||||
process_channel_to_freq (opts, state, channelr);
|
||||
}
|
||||
int sysclass = MAC[11+len_a]; //need to re-check this
|
||||
fprintf (stderr, "\n Adjacent Status Broadcast - Extended\n");
|
||||
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] SITE [%03X] CHAN-T [%04X] CHAN-R [%04X] SSC [%02X]\n ", lra, rfssid, lsysid, siteid, channelt, channelr, sysclass);
|
||||
if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||
if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||
if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||
else fprintf (stderr, " Last Known");
|
||||
if (cfva & 0x1) fprintf (stderr, " Valid RFSS Connection Active");
|
||||
process_channel_to_freq (opts, state, channelt);
|
||||
process_channel_to_freq (opts, state, channelr);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
||||
//Figure out which PDU we are looking at, see above info on 8.4.1
|
||||
//reorganize bits into bytes and process accordingly
|
||||
|
||||
//new slot variable with flipped assignment for SACCH
|
||||
uint8_t slot = (state->currentslot ^ 1) & 1;
|
||||
|
||||
unsigned long long int SMAC[24] = {0}; //22.5 bytes for SACCH MAC PDUs
|
||||
int byte = 0;
|
||||
int k = 0;
|
||||
@@ -206,6 +209,9 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
||||
//close any open MBEout files
|
||||
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||
|
||||
//blank the call string here -- slot variable is already flipped accordingly for sacch
|
||||
sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||
|
||||
}
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
@@ -236,6 +242,10 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
process_MAC_VPDU(opts, state, 1, SMAC);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//blank the call string here -- slot variable is already flipped accordingly for sacch
|
||||
sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||
|
||||
}
|
||||
if (opcode == 0x4 && err == 0)
|
||||
{
|
||||
@@ -280,6 +290,9 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
||||
//Figure out which PDU we are looking at, see above info on 8.4.1
|
||||
//reorganize bits into bytes and process accordingly
|
||||
|
||||
//new slot variable
|
||||
uint8_t slot = state->currentslot;
|
||||
|
||||
unsigned long long int FMAC[24] = {0}; //19.5 bytes for FACCH MAC PDUs, add padding to end
|
||||
int byte = 0;
|
||||
int k = 0;
|
||||
@@ -433,6 +446,9 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
||||
|
||||
//close any open MBEout files
|
||||
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||
|
||||
//blank the call string here
|
||||
sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||
|
||||
}
|
||||
if (state->currentslot == 1)
|
||||
@@ -484,6 +500,10 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
process_MAC_VPDU(opts, state, 0, FMAC);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//blank the call string here
|
||||
sprintf (state->call_string[slot], "%s", " "); //21 spaces
|
||||
|
||||
}
|
||||
if (opcode == 0x4 && err == 0)
|
||||
{
|
||||
|
||||
@@ -630,7 +630,7 @@ processX2TDMAvoice (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
if ((eeei == 0) && (aiei == 0))
|
||||
{
|
||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
||||
// processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
||||
}
|
||||
if (opts->p25enc == 1)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user