mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 09:17:20 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0635ab7121 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,6 +2,3 @@
|
|||||||
*.so*
|
*.so*
|
||||||
*.dylib
|
*.dylib
|
||||||
build
|
build
|
||||||
*.sh
|
|
||||||
*.csv
|
|
||||||
examples
|
|
||||||
|
|||||||
@@ -27,7 +27,3 @@ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|||||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
PERFORMANCE OF THIS SOFTWARE.
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
# Digital Speech Decoder - Florida Man Edition
|
|
||||||
|
|
||||||
This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"). This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu and hrh17 for the many hours of wav samples submitted by them.
|
|
||||||
|
|||||||
246
README.md
246
README.md
@@ -1,26 +1,248 @@
|
|||||||
|
# Notice - This branch is now considered 'legacy' and the master branch will be considered the 'main' branch going forward.
|
||||||
|
|
||||||
# Digital Speech Decoder - Florida Man Edition
|
# Digital Speech Decoder - Florida Man Edition
|
||||||
|
|
||||||
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and
|
This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"). This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu and hrh17 for the many hours of wav samples submitted by them.
|
||||||
[Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25") along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, etc, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users, so please understand that this version may not compile or run correctly in other environments.
|
|
||||||
|
|
||||||
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.
|
## 2022.10.01 Update ##
|
||||||
|
|
||||||
Click image below for video of current DMR/P25 trunking capabilities.
|
Very Experimental (Mostly Just Sampling Speed and using built in QPSK demodulator) for Phase 2 CQPSK 6000 sps input over disc tap inputs (SDR++, GQRX, WAV files). Promising results, but some errors in decoding as expected. I've had some pretty good luck using SDR++ to listen to and decode Duke Energy TDMA Control Channel through the virtual sink. Experimental CQPSK can be toggled in Ncurses Menu and also via command line switch `-f2 -m2` but keep in mind this will only work with Phase 2 RF channels using CQPSK, and not a combined decoding.
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=hGIijBbbUxg "DSD-FME Trunking Demo")
|
Also, added some new iden_up PDUs and other odd and end P25 PDU's (still far from complete). Ncurses Terminal and Console output will now show relevant frequencies for CC and Voice channels if iden_up commands are present.
|
||||||
|
|
||||||

|
DSD-FME, and The Florida Man Labs will be taking a hiatus period from straigth development of this project. Currently, I feel we are at a bit of a stand still as far as what can be done with the project without new demodulation, dibit buffer, audio, and input methods. DSD-FME will continue to receive maintenance updates for simple bug fixes and also any major bug issues that cause crashing, etc., but I am reluctant to continue developing with the current underlying issues mentioned above. I am going to go into a period of hiatus with this project and do lots of experimentation, learning, and testing to see what viable options are out there or need to be created from scratch. I feel like I've learned a lot, achieved a lot, made tons of mistakes, learned from a few of them, but ultimately improved on the DSD project. DSD-FME will continue to receive replies and support in any opened issues but will currently only extend to help in setting up and using the software, on top of regular minor maintenance and any crash related issues.
|
||||||
|
|
||||||

|
## 2022.09.28 Mini Update ##
|
||||||
|
Back by popular demand, I've re-implemented DMR Stereo as a standalone decoder. DMR Stereo is also still available in XDMA decoding for P25 1, 2, and DMR BS/MS Simplex as well.
|
||||||
|
|
||||||
## Information
|
DMR Stereo `dsd-fme -fs`
|
||||||
|
XDMA `dsd-fme -ft`
|
||||||
|
|
||||||
See the example folder for information on cloning, installing, example usage, and trunking features.
|
It can also be selected in the NCurses Terminal Menu. Sadly I had to remove the X2-TDMA decoder option in the menu, I'm sure nobody will miss it.
|
||||||
|
|
||||||
## Notice
|
## 2022.09.17 Update ##
|
||||||
|
P25 Phase 2 Audio decoding has been implemented into DSD-FME. Currently, Phase 2 will only work from OP25 symbol capture bin files, or from wav files/SDR applications/RTL input that are sourced from FSK4 sources. CQPSK (LSM/H-D8PSK) will not work from audio sources. With the addition of Phases 2 audio, a new default decoder class has been implemented, XDMA, which is P25 Phase 1, Phase 2, DMR BS and MS (DMR Stereo). Furthermore, very limited Phase 1 TSBK support and Phase 2 FACCH/SACCH/LCCH has been worked in just to get Network Status Broadcasts, which can fill in the required P2 parameters for WACN, SYSID, and CC for Phase 2 frame de-scrambling, and active voice call information.
|
||||||
|
|
||||||
The pulseaudio branch will hearby be for legacy use as the promotion of this version to main branch will obsolete that branch. Any pre-compiled windows versions found in the release link will be considered legacy as well, and do not feature trunking support and have older handling for some other modes. Users are free to use those, but keep in mind the use case syntax may and will be different between versions. Furthermore, any wiki articles made for windows users may contain outdated information for building the pulseaudio branch, please adjust any information found in those accordingly.
|
With the implementation of the XDMA decoder class (default decoder), the CLI switches for DMR Stereo has been repurposed. -T option will now open a per call wav file saving when used with NCurses Terminal, otherwise it will write wav files to slot 1 and slot 2 wav files.
|
||||||
|
|
||||||
|
Anybody using `dsd-fme -fr -T` ...... should now just use `dsd-fme -ft` instead!
|
||||||
|
|
||||||
|
To see the up to date CLI options, please look at the help options with the -h CLI option.
|
||||||
|
|
||||||
|
## 2022.08.12 Update ##
|
||||||
|
A new menu system has been introduced in the NCURSES Terminal. Also includes support for LRRP to text file type of choice and reading OP25 symbol capture bin files.
|
||||||
|
|
||||||
|
[](https://www.youtube.com/watch?v=TqTfAfaPJ4s "DSD-FME Update 2022.08.12")
|
||||||
|
|
||||||
|
To get started with the new menu system, simply launch with:
|
||||||
|
|
||||||
|
`dsd-fme -N 2> log.txt`
|
||||||
|
|
||||||
|
## 2022.06.20 Update - Current Users Please Read!! ##
|
||||||
|
The executable for this project has been changed from `dsd` to `dsd-fme` so after pulling or cloning the lastest version, make sure to call the software with `dsd-fme`.
|
||||||
|
|
||||||
|
## 2022.05.05 Update ##
|
||||||
|
DMR Stereo method added for listening to voice audio in both TDMA channels/slots simultaneously.
|
||||||
|
|
||||||
|
### Example Usage and Note!
|
||||||
|
`dsd-fme` or `./dsd-fme` may need to be used in your usage, depending on if you are running it from the build folder, or if you have run make install. You will need to tweak the examples to your particular usage. Users can always check `dsd-fme -h` for help and all command line switches to use.
|
||||||
|
|
||||||
|
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, P25 (1 and 2) and X2-TDMA decoding. To use other decoding methods which cannot be auto detected, please use the following command line switches. Make sure to route audio into and out of DSD-FME using pavucontrol and virtual sinks as needed.
|
||||||
|
|
||||||
|
```
|
||||||
|
-ft XDMA decoder class (P25 1 and 2, DMR BS/MS, X2-TDMA)
|
||||||
|
-fs DMR Stereo BS/MS Simplex
|
||||||
|
-fa Legacy Auto
|
||||||
|
-fi NXDN48
|
||||||
|
-fn NXDN96
|
||||||
|
-fp ProVoice
|
||||||
|
-fm dPMR, also may need to use -xd if inverted dPMR.
|
||||||
|
-fr DMR, also may need to use -xr if inverted DMR.
|
||||||
|
-f1 P25P1
|
||||||
|
-fx X2-TDMA
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Usage - New XDMA Decoder, Ncurses Terminal, Pulse Input/Output, and Log Console to file
|
||||||
|
`dsd-fme -ft -N 2> voice.log`
|
||||||
|
|
||||||
|
and in a second terminal tab, same folder, run
|
||||||
|
|
||||||
|
`tail -n 40 -f voice.log`
|
||||||
|
|
||||||
|
Then you can open up your pavucontrol "Pulse Audio Volume Control" or "Volume Control" application and route input into DSD-FME from any application and DSD-FME output to the left and right speakers respectively. (unlock the channel in the application stream and adjust left and right independently)
|
||||||
|
|
||||||
|
## Example Usage - RTL2832 Dongle Input
|
||||||
|
`dsd-fme -fi -i rtl -c 154.9875M -P -2 -D 1 -G 36 -L 70 -U 6021 -Y 12`
|
||||||
|
|
||||||
|
```
|
||||||
|
-i rtl to use rtl_fm (default is -i pulse for pulse audio)
|
||||||
|
|
||||||
|
-o pulse to set output to pulse audio (excluded, is default anyways)
|
||||||
|
|
||||||
|
-c Set frequency
|
||||||
|
|
||||||
|
-P set PPM error (default = 0)
|
||||||
|
|
||||||
|
-D set device index number (default = 0; first device detected)
|
||||||
|
|
||||||
|
-G set device gain (0-49) (default = 0 Auto Gain)
|
||||||
|
|
||||||
|
-L set rtl squelch to 70 (default = 0; off)
|
||||||
|
|
||||||
|
-U set UDP port for rtl_fm remote control (default = 6020)
|
||||||
|
|
||||||
|
-Y 12 set rtl VFO bandwidth in kHz, (default = 12)(6, 8, 12, 16, 24, 48)
|
||||||
|
|
||||||
|
```
|
||||||
|
## Example STDIN UDP from GQRX or SDR++, output to Pulse Audio, and append decoded audio to wav file
|
||||||
|
Be sure to first start UDP output sink in GQRX or SDR++ and set VFO appropriately
|
||||||
|
|
||||||
|
`socat stdio udp-listen:7355 | dsd-fme -fi -i - -w nxdn.wav`
|
||||||
|
|
||||||
|
## Example Usage - Extra Information for Academic Study and Logging
|
||||||
|
|
||||||
|
`dsd-fme -Z -N 2>> voice.log`
|
||||||
|
|
||||||
|
and in a second terminal tab, same folder, run
|
||||||
|
|
||||||
|
`tail -n 40 -f voice.log`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
The Current list of objectives include:
|
||||||
|
|
||||||
|
1. New demodulators and input method(s) (considering liquid-dsp).
|
||||||
|
|
||||||
|
2. Graphical User Interface (may need to migrate to new project).
|
||||||
|
|
||||||
|
3. Lots of experimentation.
|
||||||
|
|
||||||
|
## How to clone, check out, and build this branch
|
||||||
|
|
||||||
|
### Ubuntu 22.04/20.04/LM20/Debian Bullseye or Newer:
|
||||||
|
|
||||||
|
Using the included download-and-install.sh should make for a simple and painless clone, build, and install on newer Debian/Ubuntu/Mint/Pi systems. Simply acquire or copy the script, and run it. Update: Ubuntu 22.04 and RPi Bullseye 64-bit has been tested working with the installer script and functions appropriately.
|
||||||
|
|
||||||
|
If you need all dependencies build and installed first (only on Debian/Ubuntu/Mint/Pi), run:
|
||||||
|
|
||||||
|
```
|
||||||
|
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/pulseaudio/download-and-install.sh
|
||||||
|
chmod +x download-and-install.sh
|
||||||
|
./download-and-install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have dependencies already installed (i.e. need a fresh clean install on a system with DSD-FME already or using system other than Debian/Ubuntu, etc), please run this instead:
|
||||||
|
|
||||||
|
```
|
||||||
|
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/pulseaudio/download-and-install-nodeps.sh
|
||||||
|
chmod +x download-and-install-nodeps.sh
|
||||||
|
./download-and-install-nodeps.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ubuntu 18.04/LM19/Buster Note:
|
||||||
|
The above install.sh should now function on older system types. You can elect to manually follow the steps down below. Do NOT Manually build and install ITPP 4.3.1 on older systems, it is currently not wanting to build on Ubuntu 18.04 and Linux Mint 19. Install it from the repository instead.
|
||||||
|
|
||||||
|
## Manual Install
|
||||||
|
|
||||||
|
First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different.
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev
|
||||||
|
```
|
||||||
|
## Headless
|
||||||
|
|
||||||
|
If running headless, swap out pavucontrol for pulsemixer, and also install pulseaudio as well. Attempting to install pavucontrol in a headless environment may attempt to install a minimal desktop environment. Note: Default behavior of pulseaudio in a headless environment may be to be muted, so check by opening pulsemixer and unmuting and routing audio appropriately.
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev pulsemixer pulseaudio
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build and Install ITPP - ONLY IF NOT IN REPO!!
|
||||||
|
|
||||||
|
```
|
||||||
|
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
|
||||||
|
tar xjf itpp*
|
||||||
|
#if you can't cd into this folder, double check folder name first
|
||||||
|
cd itpp-4.3.1
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
cd ..
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice") This version of MBELib is 1.3.1 and prints to STDERR, using the stock 1.3.0 MBElib may cause problems with print alignments if paired with this version of DSD-FME, or cause issues with printing over the screen in future builds.
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/lwvmobile/mbelib
|
||||||
|
cd mbelib
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
cd ..
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
Finish by running these steps to clone and build DSD-FME w/ pulseaudio support.
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
|
cd dsd-fme
|
||||||
|
git branch -a
|
||||||
|
git checkout remotes/origin/pulseaudio
|
||||||
|
git checkout -b pulseaudio
|
||||||
|
git branch -a #double check to see if you are on pulseaudio branch
|
||||||
|
sudo cp tone8.wav /usr/share/
|
||||||
|
sudo cp tone24.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
##only run make install if you don't have another version already installed##
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
|
||||||
|
```
|
||||||
|
Optional 'Virtual Sinks' for routing audio from SDR++ or GQRX, Media Players, etc. into DSD-FME
|
||||||
|
|
||||||
|
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired.
|
||||||
|
|
||||||
|
```
|
||||||
|
pacmd load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
||||||
|
pacmd load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
|
||||||
|
```
|
||||||
|
|
||||||
|
Already have this branch, and just want to pull the latest build? You can run the rebuild.sh file in the dsd-fme folder, or manually do the pull with the commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
##Open your clone folder##
|
||||||
|
git pull https://github.com/lwvmobile/dsd-fme pulseaudio
|
||||||
|
##cd into your build folder##
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
If the call alert wav files aren't playing, then make sure to run the following in the dsd-fme folder to copy the wav files to the /usr/share/ folder and give them adequate permission to be accessed.
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo cp tone8.wav /usr/share/
|
||||||
|
sudo cp tone24.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Copyright (C) 2010 DSD Author
|
Copyright (C) 2010 DSD Author
|
||||||
@@ -37,5 +259,3 @@ GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|||||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
PERFORMANCE OF THIS SOFTWARE.
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,18 @@ read -p "Press enter to continue"
|
|||||||
|
|
||||||
git clone https://github.com/lwvmobile/dsd-fme
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
cd dsd-fme
|
cd dsd-fme
|
||||||
|
git branch -a
|
||||||
|
git checkout remotes/origin/pulseaudio
|
||||||
|
git checkout -b pulseaudio
|
||||||
|
git branch -a #double check to see if you are on pulseaudio branch
|
||||||
sudo cp tone8.wav /usr/share/
|
sudo cp tone8.wav /usr/share/
|
||||||
sudo cp tone24.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/tone8.wav
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
sudo chmod 777 /usr/share/tone48.wav
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
make -j `nproc`
|
make -j `nproc`
|
||||||
|
##only run make install if you don't have another version already installed##
|
||||||
sudo make install
|
sudo make install
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
|
|||||||
@@ -45,16 +45,19 @@ cd ..
|
|||||||
|
|
||||||
git clone https://github.com/lwvmobile/dsd-fme
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
cd dsd-fme
|
cd dsd-fme
|
||||||
|
git branch -a
|
||||||
|
git checkout remotes/origin/pulseaudio
|
||||||
|
git checkout -b pulseaudio
|
||||||
|
git branch -a #double check to see if you are on pulseaudio branch
|
||||||
sudo cp tone8.wav /usr/share/
|
sudo cp tone8.wav /usr/share/
|
||||||
sudo cp tone24.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/tone8.wav
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
sudo chmod 777 /usr/share/tone48.wav
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
make -j `nproc`
|
make -j `nproc`
|
||||||
|
##only run make install if you don't have another version already installed##
|
||||||
sudo make install
|
sudo make install
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
|
|
||||||
|
|||||||
BIN
dsd-fme.png
BIN
dsd-fme.png
Binary file not shown.
|
Before Width: | Height: | Size: 706 KiB After Width: | Height: | Size: 160 KiB |
BIN
dsd-fme2.png
BIN
dsd-fme2.png
Binary file not shown.
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 153 KiB |
BIN
dsd-fme3.png
BIN
dsd-fme3.png
Binary file not shown.
|
Before Width: | Height: | Size: 692 KiB |
@@ -1,155 +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 using channel 0 as the control channel will hardset that frequency to the control channel. If you need to map out your channels for TIII, you can observe the console output and look for channel numbers. For conveniece I have included the DSDPlus channel numbering (as best as I can figure it) into the console print so it will make it easier for users from DSDPlus to map frequencies into the channel_map.csv file. Make sure your channel numbers are the Cd (channel decimal) values from the log, and not the C+ (dsdplus) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area.
|
|
||||||
|
|
||||||
```
|
|
||||||
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 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/cqpsk 10/4 (everything but phase 2 signal)
|
|
||||||
M - toggle c4fm/cqpsk 8/3 (phase 2 tdma control channel)
|
|
||||||
t - toggle trunking (needs either rtl input, or rigctl setup at CLI)
|
|
||||||
R - start capturing symbol capture bin (date/time name file)
|
|
||||||
r - 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
|
|
||||||
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
|
|
||||||
|
|
||||||
```
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
## 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 ..
|
|
||||||
```
|
|
||||||
|
|
||||||
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,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
|
|
||||||
449
include/dsd.h
449
include/dsd.h
@@ -32,7 +32,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
//#define __USE_XOPEN //compiler warning on this, need to rewrite dsd_file so it doesn't use strptime
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -62,13 +62,38 @@
|
|||||||
|
|
||||||
static volatile int exitflag;
|
static volatile int exitflag;
|
||||||
|
|
||||||
//group csv import struct
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
unsigned long int groupNumber;
|
uint8_t RFChannelType;
|
||||||
char groupMode[8]; //char *?
|
uint8_t FunctionnalChannelType;
|
||||||
char groupName[50];
|
uint8_t Option;
|
||||||
} groupinfo;
|
uint8_t Direction;
|
||||||
|
uint8_t CompleteLich; /* 7 bits of LICH without parity */
|
||||||
|
uint8_t PreviousCompleteLich; /* 7 bits of previous LICH without parity */
|
||||||
|
} NxdnLich_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t StructureField;
|
||||||
|
uint8_t RAN;
|
||||||
|
uint8_t Data[18];
|
||||||
|
uint8_t CrcIsGood;
|
||||||
|
} NxdnSacchRawPart_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t Data[80];
|
||||||
|
uint8_t CrcIsGood;
|
||||||
|
} NxdnFacch1RawPart_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t Data[184];
|
||||||
|
uint8_t CrcIsGood;
|
||||||
|
} NxdnFacch2RawPart_t;
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -110,11 +135,11 @@ typedef struct
|
|||||||
uint8_t NextIVComputed[8];
|
uint8_t NextIVComputed[8];
|
||||||
} NxdnElementsContent_t;
|
} NxdnElementsContent_t;
|
||||||
|
|
||||||
//Reed Solomon (12,9) constant
|
//Read Solomon (12,9) constant
|
||||||
#define RS_12_9_DATASIZE 9
|
#define RS_12_9_DATASIZE 9
|
||||||
#define RS_12_9_CHECKSUMSIZE 3
|
#define RS_12_9_CHECKSUMSIZE 3
|
||||||
|
|
||||||
//Reed Solomon (12,9) struct
|
//Read Solomon (12,9) struct
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t data[RS_12_9_DATASIZE+RS_12_9_CHECKSUMSIZE];
|
uint8_t data[RS_12_9_DATASIZE+RS_12_9_CHECKSUMSIZE];
|
||||||
@@ -138,6 +163,48 @@ typedef struct
|
|||||||
uint8_t bytes[3];
|
uint8_t bytes[3];
|
||||||
} rs_12_9_checksum_t;
|
} rs_12_9_checksum_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char VoiceFrame[3][72]; /* 3 x 72 bit of voice per time slot frame */
|
||||||
|
char Sync[48]; /* 48 bit of data or SYNC per time slot frame */
|
||||||
|
}TimeSlotRawVoiceFrame_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char DeInterleavedVoiceSample[3][4][24]; /* 3 x (4 x 24) deinterleaved bit of voice per time slot frame */
|
||||||
|
}TimeSlotDeinterleavedVoiceFrame_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int errs1[3]; /* 3 x errors #1 computed when demodulate the AMBE voice bit of the frame */
|
||||||
|
int errs2[3]; /* 3 x errors #2 computed when demodulate the AMBE voice bit of the frame */
|
||||||
|
char AmbeBit[3][49]; /* 3 x 49 bit of AMBE voice of the frame */
|
||||||
|
}TimeSlotAmbeVoiceFrame_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int ProtectFlag;
|
||||||
|
unsigned int Reserved;
|
||||||
|
unsigned int FullLinkControlOpcode;
|
||||||
|
unsigned int FeatureSetID;
|
||||||
|
unsigned int ServiceOptions;
|
||||||
|
unsigned int GroupAddress; /* Destination ID or TG (in Air Interface format) */
|
||||||
|
unsigned int SourceAddress; /* Source ID (in Air Interface format) */
|
||||||
|
unsigned int DataValidity; /* 0 = All Full LC data are incorrect ; 1 = Full LC data are correct (CRC checked OK) */
|
||||||
|
unsigned int LeftOvers; //what is the 8 bits for that isn't used
|
||||||
|
}FullLinkControlPDU_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
TimeSlotRawVoiceFrame_t TimeSlotRawVoiceFrame[6]; /* A voice superframe contains 6 timeslot voice frame */
|
||||||
|
TimeSlotDeinterleavedVoiceFrame_t TimeSlotDeinterleavedVoiceFrame[6];
|
||||||
|
TimeSlotAmbeVoiceFrame_t TimeSlotAmbeVoiceFrame[6];
|
||||||
|
FullLinkControlPDU_t FullLC;
|
||||||
|
} TimeSlotVoiceSuperFrame_t;
|
||||||
|
|
||||||
|
|
||||||
//dPMR
|
//dPMR
|
||||||
/* Could only be 2 or 4 */
|
/* Could only be 2 or 4 */
|
||||||
#define NB_OF_DPMR_VOICE_FRAME_TO_DECODE 2
|
#define NB_OF_DPMR_VOICE_FRAME_TO_DECODE 2
|
||||||
@@ -180,14 +247,14 @@ typedef struct
|
|||||||
int p25status;
|
int p25status;
|
||||||
int p25tg;
|
int p25tg;
|
||||||
int scoperate;
|
int scoperate;
|
||||||
char audio_in_dev[2048]; //increase size for super long directory/file names
|
char audio_in_dev[1024];
|
||||||
int audio_in_fd;
|
int audio_in_fd;
|
||||||
SNDFILE *audio_in_file;
|
SNDFILE *audio_in_file;
|
||||||
SF_INFO *audio_in_file_info;
|
SF_INFO *audio_in_file_info;
|
||||||
|
|
||||||
uint32_t rtlsdr_center_freq;
|
uint32_t rtlsdr_center_freq;
|
||||||
int rtlsdr_ppm_error;
|
int rtlsdr_ppm_error; //was int, changed to float
|
||||||
int audio_in_type;
|
int audio_in_type; // 0 for device, 1 for file, 3 for rtlsdr
|
||||||
char audio_out_dev[1024];
|
char audio_out_dev[1024];
|
||||||
int audio_out_fd;
|
int audio_out_fd;
|
||||||
SNDFILE *audio_out_file;
|
SNDFILE *audio_out_file;
|
||||||
@@ -279,7 +346,6 @@ typedef struct
|
|||||||
int inverted_dpmr;
|
int inverted_dpmr;
|
||||||
int frame_dpmr;
|
int frame_dpmr;
|
||||||
|
|
||||||
short int dmr_mono;
|
|
||||||
short int dmr_stereo;
|
short int dmr_stereo;
|
||||||
short int lrrp_file_output;
|
short int lrrp_file_output;
|
||||||
|
|
||||||
@@ -293,58 +359,6 @@ typedef struct
|
|||||||
FILE *symbolfile;
|
FILE *symbolfile;
|
||||||
int call_alert;
|
int call_alert;
|
||||||
|
|
||||||
//rigctl opt
|
|
||||||
int rigctl_sockfd;
|
|
||||||
int use_rigctl;
|
|
||||||
int rigctlportno;
|
|
||||||
char rigctlhostname[1024];
|
|
||||||
|
|
||||||
//udp socket for GQRX, SDR++, etc
|
|
||||||
int udp_sockfd;
|
|
||||||
int udp_portno;
|
|
||||||
char * udp_hostname;
|
|
||||||
SNDFILE *udp_file_in;
|
|
||||||
|
|
||||||
//tcp socket for SDR++, etc
|
|
||||||
int tcp_sockfd;
|
|
||||||
int tcp_portno;
|
|
||||||
char tcp_hostname[1024];
|
|
||||||
SNDFILE *tcp_file_in;
|
|
||||||
|
|
||||||
//wav file sample rate, interpolator and decimator
|
|
||||||
int wav_sample_rate;
|
|
||||||
int wav_interpolator;
|
|
||||||
int wav_decimator;
|
|
||||||
|
|
||||||
int p25_trunk; //experimental P25 trunking with RIGCTL (or RTLFM)
|
|
||||||
int p25_is_tuned; //set to 1 if currently on VC, set back to 0 if on CC
|
|
||||||
int trunk_hangtime; //hangtime in seconds before tuning back to CC
|
|
||||||
|
|
||||||
//csv import filenames
|
|
||||||
char group_in_file[1024];
|
|
||||||
char lcn_in_file[1024];
|
|
||||||
char chan_in_file[1024];
|
|
||||||
//end import filenames
|
|
||||||
|
|
||||||
//reverse mute
|
|
||||||
uint8_t reverse_mute;
|
|
||||||
|
|
||||||
//setmod bandwidth
|
|
||||||
int setmod_bw;
|
|
||||||
|
|
||||||
//DMR Location Area - DMRLA B***S***
|
|
||||||
uint8_t dmr_dmrla_is_set; //flag to tell us dmrla is set by the user
|
|
||||||
uint8_t dmr_dmrla_n; //n value for dmrla
|
|
||||||
|
|
||||||
//Trunking - Use Group List as Allow List
|
|
||||||
uint8_t trunk_use_allow_list;
|
|
||||||
|
|
||||||
//Trunking - Tune Private Calls
|
|
||||||
uint8_t trunk_tune_private_calls;
|
|
||||||
|
|
||||||
//Trunking - Tune Data Calls...why?
|
|
||||||
uint8_t trunk_tune_data_calls;
|
|
||||||
|
|
||||||
} dsd_opts;
|
} dsd_opts;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -370,6 +384,7 @@ typedef struct
|
|||||||
int audio_out_idx2;
|
int audio_out_idx2;
|
||||||
int audio_out_idxR;
|
int audio_out_idxR;
|
||||||
int audio_out_idx2R;
|
int audio_out_idx2R;
|
||||||
|
//int wav_out_bytes;
|
||||||
int center;
|
int center;
|
||||||
int jitter;
|
int jitter;
|
||||||
int synctype;
|
int synctype;
|
||||||
@@ -416,6 +431,7 @@ typedef struct
|
|||||||
int numtdulc;
|
int numtdulc;
|
||||||
int firstframe;
|
int firstframe;
|
||||||
char slot0light[8];
|
char slot0light[8];
|
||||||
|
//char slot1light[8];
|
||||||
float aout_gain;
|
float aout_gain;
|
||||||
float aout_gainR;
|
float aout_gainR;
|
||||||
float aout_max_buf[200];
|
float aout_max_buf[200];
|
||||||
@@ -464,10 +480,10 @@ typedef struct
|
|||||||
// Last dibit read
|
// Last dibit read
|
||||||
int last_dibit;
|
int last_dibit;
|
||||||
|
|
||||||
// Heuristics state data for +P25 signals
|
// Heuristics state data for +P5 signals
|
||||||
P25Heuristics p25_heuristics;
|
P25Heuristics p25_heuristics;
|
||||||
|
|
||||||
// Heuristics state data for -P25 signals
|
// Heuristics state data for -P5 signals
|
||||||
P25Heuristics inv_p25_heuristics;
|
P25Heuristics inv_p25_heuristics;
|
||||||
|
|
||||||
//input sample buffer for monitoring Input
|
//input sample buffer for monitoring Input
|
||||||
@@ -481,10 +497,17 @@ typedef struct
|
|||||||
unsigned int nxdn_cipher_type;
|
unsigned int nxdn_cipher_type;
|
||||||
unsigned int nxdn_key;
|
unsigned int nxdn_key;
|
||||||
char nxdn_call_type[1024];
|
char nxdn_call_type[1024];
|
||||||
|
char dmr_callsign[2][6][99]; //plenty of room in case of overflow;
|
||||||
|
char dmr_lrrp[2][6][9999]; //memory hog
|
||||||
unsigned long long int dmr_lrrp_source[2];
|
unsigned long long int dmr_lrrp_source[2];
|
||||||
|
|
||||||
|
NxdnSacchRawPart_t NxdnSacchRawPart[4];
|
||||||
|
NxdnFacch1RawPart_t NxdnFacch1RawPart[2];
|
||||||
|
NxdnFacch2RawPart_t NxdnFacch2RawPart;
|
||||||
NxdnElementsContent_t NxdnElementsContent;
|
NxdnElementsContent_t NxdnElementsContent;
|
||||||
|
NxdnLich_t NxdnLich;
|
||||||
|
|
||||||
|
int printNXDNAmbeVoiceSampleHex;
|
||||||
|
|
||||||
char ambe_ciphered[49];
|
char ambe_ciphered[49];
|
||||||
char ambe_deciphered[49];
|
char ambe_deciphered[49];
|
||||||
@@ -505,8 +528,12 @@ typedef struct
|
|||||||
char slot1light[8];
|
char slot1light[8];
|
||||||
char slot2light[8];
|
char slot2light[8];
|
||||||
int directmode;
|
int directmode;
|
||||||
|
TimeSlotVoiceSuperFrame_t TS1SuperFrame;
|
||||||
|
TimeSlotVoiceSuperFrame_t TS2SuperFrame;
|
||||||
|
|
||||||
char dmr_branding[99];
|
char dmr_branding[25];
|
||||||
|
uint8_t dmr_12_rate_sf[2][288]; //was 84, expanded to 288 to prevent possible crash if more comes in than expected
|
||||||
|
uint8_t dmr_34_rate_sf[2][288]; //was 96, expanded to 288 to prevent possible crash if more comes in than expected
|
||||||
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
|
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
|
||||||
uint8_t data_header_blocks[2]; //collect number of blocks to follow from data header per slot
|
uint8_t data_header_blocks[2]; //collect number of blocks to follow from data header per slot
|
||||||
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
|
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
|
||||||
@@ -515,27 +542,27 @@ typedef struct
|
|||||||
uint8_t data_block_counter[2]; //counter for number of data blocks collected
|
uint8_t data_block_counter[2]; //counter for number of data blocks collected
|
||||||
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
|
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
|
||||||
|
|
||||||
//new stuff below here
|
|
||||||
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
|
|
||||||
uint8_t dmr_pdu_sf[2][288]; //unified pdu 'superframe' //[slot][byte]
|
|
||||||
uint8_t data_block_crc_valid[2][25]; //flag each individual block as good crc on confirmed data
|
|
||||||
char dmr_embedded_signalling[2][6][48]; //embedded signalling 2 slots by 6 vc by 48 bits (replacing TS1SuperFrame.TimeSlotRawVoiceFrame.Sync structure)
|
|
||||||
|
|
||||||
char dmr_cach_fragment[4][17]; //unsure of size, will need to check/verify
|
|
||||||
int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet.
|
|
||||||
|
|
||||||
//dmr talker alias new/fixed stuff
|
|
||||||
uint8_t dmr_alias_format[2]; //per slot
|
|
||||||
uint8_t dmr_alias_len[2]; //per slot
|
|
||||||
char dmr_alias_block_segment[2][4][7][16]; //2 slots, by 4 blocks, by up to 7 alias bytes that are up to 16-bit chars
|
|
||||||
char dmr_embedded_gps[2][200]; //2 slots by 99 char string for string embedded gps
|
|
||||||
char dmr_lrrp_gps[2][200]; //2 slots by 99 char string for string lrrp gps
|
|
||||||
char dmr_site_parms[200]; //string for site/net info depending on type of DMR system (TIII or Con+)
|
|
||||||
char call_string[2][200]; //string for call information
|
|
||||||
|
|
||||||
|
|
||||||
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
|
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
|
||||||
|
|
||||||
|
//These flags are used to known the DMR frame
|
||||||
|
//printing format (hex or binary)
|
||||||
|
int printDMRRawVoiceFrameHex;
|
||||||
|
int printDMRRawVoiceFrameBin;
|
||||||
|
int printDMRRawDataFrameHex;
|
||||||
|
int printDMRRawDataFrameBin;
|
||||||
|
int printDMRAmbeVoiceSampleHex;
|
||||||
|
int printDMRAmbeVoiceSampleBin;
|
||||||
|
|
||||||
|
//These flags are used to known the dPMR frame
|
||||||
|
//orinting format (hex or binary)
|
||||||
|
int printdPMRRawVoiceFrameHex;
|
||||||
|
int printdPMRRawVoiceFrameBin;
|
||||||
|
int printdPMRRawDataFrameHex;
|
||||||
|
int printdPMRRawDataFrameBin;
|
||||||
|
int printdPMRAmbeVoiceSampleHex;
|
||||||
|
int printdPMRAmbeVoiceSampleBin;
|
||||||
|
|
||||||
unsigned char * dpmr_caller_id;
|
unsigned char * dpmr_caller_id;
|
||||||
unsigned char * dpmr_target_id;
|
unsigned char * dpmr_target_id;
|
||||||
int dpmr_color_code;
|
int dpmr_color_code;
|
||||||
@@ -545,8 +572,6 @@ typedef struct
|
|||||||
short int dmr_ms_mode;
|
short int dmr_ms_mode;
|
||||||
unsigned int dmrburstL;
|
unsigned int dmrburstL;
|
||||||
unsigned int dmrburstR;
|
unsigned int dmrburstR;
|
||||||
int dropL;
|
|
||||||
int dropR;
|
|
||||||
unsigned long long int R;
|
unsigned long long int R;
|
||||||
unsigned long long int RR;
|
unsigned long long int RR;
|
||||||
unsigned long long int H;
|
unsigned long long int H;
|
||||||
@@ -576,11 +601,9 @@ typedef struct
|
|||||||
int p25_chan_spac[16];
|
int p25_chan_spac[16];
|
||||||
long int p25_base_freq[16];
|
long int p25_base_freq[16];
|
||||||
|
|
||||||
//p25 frequency storage for trunking and display in ncurses
|
//p25 frequency storage for display in ncurses
|
||||||
long int p25_cc_freq; //cc freq from net_stat
|
long int p25_cc_freq; //cc freq from net_stat
|
||||||
long int p25_vc_freq[2]; //vc freq from voice grant updates, etc
|
long int p25_vc_freq[2]; //vc freq from voice grant updates, etc
|
||||||
int p25_cc_is_tdma; //flag to tell us that the P25 control channel is TDMA so we can change symbol rate when required
|
|
||||||
|
|
||||||
|
|
||||||
//experimental symbol file capture read throttle
|
//experimental symbol file capture read throttle
|
||||||
int symbol_throttle; //throttle speed
|
int symbol_throttle; //throttle speed
|
||||||
@@ -589,59 +612,16 @@ typedef struct
|
|||||||
//dstar header for ncurses
|
//dstar header for ncurses
|
||||||
unsigned char dstarradioheader[41];
|
unsigned char dstarradioheader[41];
|
||||||
|
|
||||||
//dmr trunking stuff
|
#ifdef TRACE_DSD
|
||||||
int dmr_rest_channel;
|
char debug_prefix;
|
||||||
int dmr_mfid; //just when 'fid' is used as a manufacturer ID and not a feature set id
|
char debug_prefix_2;
|
||||||
int dmr_vc_lcn;
|
unsigned int debug_sample_index;
|
||||||
int dmr_vc_lsn;
|
unsigned int debug_sample_left_edge;
|
||||||
int dmr_tuned_lcn;
|
unsigned int debug_sample_right_edge;
|
||||||
|
FILE* debug_label_file;
|
||||||
//edacs
|
FILE* debug_label_dibit_file;
|
||||||
int ea_mode;
|
FILE* debug_label_imbe_file;
|
||||||
int esk_mode;
|
#endif
|
||||||
unsigned short esk_mask;
|
|
||||||
unsigned long long int edacs_site_id;
|
|
||||||
int edacs_lcn_count; //running tally of lcn's observed on edacs system
|
|
||||||
int edacs_cc_lcn; //current lcn for the edacs control channel
|
|
||||||
int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to)
|
|
||||||
int edacs_tuned_lcn; //the vc we are currently tuned to...above variable is for updating all in the matrix
|
|
||||||
|
|
||||||
//trunking group and lcn freq list
|
|
||||||
long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully
|
|
||||||
long int trunk_chan_map[0xFFFF]; //NXDN - 10 bit; P25 - 16 bit; DMR up to 12 bit (standard TIII)
|
|
||||||
groupinfo group_array[0x3FF]; //max supported by Cygwin is 3FFF, I hope nobody actually tries to import this many groups
|
|
||||||
unsigned int group_tally; //tally number of groups imported from CSV file for referencing later
|
|
||||||
int lcn_freq_count;
|
|
||||||
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
|
|
||||||
time_t last_cc_sync_time; //use this to start hunting for CC after signal lost
|
|
||||||
time_t last_vc_sync_time; //flag for voice activity bursts, tune back on con+ after more than x seconds no voice
|
|
||||||
int is_con_plus; //con_plus flag for knowing its safe to skip payload channel after x seconds of no voice sync
|
|
||||||
|
|
||||||
//new nxdn stuff
|
|
||||||
int nxdn_part_of_frame;
|
|
||||||
int nxdn_ran;
|
|
||||||
int nxdn_sf;
|
|
||||||
bool nxdn_sacch_non_superframe; //flag to indicate whether or not a sacch is a part of a superframe, or an individual piece
|
|
||||||
uint8_t nxdn_sacch_frame_segment[4][18]; //part of frame by 18 bits
|
|
||||||
uint8_t nxdn_sacch_frame_segcrc[4];
|
|
||||||
uint8_t nxdn_alias_block_number;
|
|
||||||
char nxdn_alias_block_segment[4][4][8];
|
|
||||||
//site/srv/cch info
|
|
||||||
char nxdn_location_category[14];
|
|
||||||
uint32_t nxdn_location_sys_code;
|
|
||||||
uint16_t nxdn_location_site_code;
|
|
||||||
|
|
||||||
//dmr late entry mi
|
|
||||||
uint64_t late_entry_mi_fragment[2][7][3];
|
|
||||||
|
|
||||||
//char dmr_branding[25]; //may need higher value?
|
|
||||||
char dmr_branding_sub[25];
|
|
||||||
|
|
||||||
//Remus DMR End Call Alert Beep
|
|
||||||
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
|
|
||||||
|
|
||||||
//Upsampled Audio Smoothing
|
|
||||||
uint8_t audio_smoothing;
|
|
||||||
|
|
||||||
} dsd_state;
|
} dsd_state;
|
||||||
|
|
||||||
@@ -682,12 +662,8 @@ typedef struct
|
|||||||
#define DMR_MS_DATA_SYNC "311131133313133331131113"
|
#define DMR_MS_DATA_SYNC "311131133313133331131113"
|
||||||
#define DMR_MS_VOICE_SYNC "133313311131311113313331"
|
#define DMR_MS_VOICE_SYNC "133313311131311113313331"
|
||||||
|
|
||||||
//Part 1-A CAI 4.4.4 (FSW only - Late Entry - Marginal Signal)
|
//Testing NXDN FSW again
|
||||||
#define NXDN_FSW "3131331131"
|
#define NXDN_FSW "3131331131"
|
||||||
#define INV_NXDN_FSW "1313113313"
|
|
||||||
//Part 1-A CAI 4.4.3 Preamble Last 9 plus FSW (start of RDCH)
|
|
||||||
#define NXDN_PANDFSW "3131133313131331131" //19 symbols
|
|
||||||
#define INV_NXDN_PANDFSW "1313311131313113313" //19 symbols
|
|
||||||
|
|
||||||
#define DMR_RC_DATA_SYNC "131331111133133133311313"
|
#define DMR_RC_DATA_SYNC "131331111133133133311313"
|
||||||
|
|
||||||
@@ -701,9 +677,6 @@ typedef struct
|
|||||||
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
||||||
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
||||||
|
|
||||||
#define EDACS_SYNC "313131313131313131313111333133133131313131313131"
|
|
||||||
#define INV_EDACS_SYNC "131313131313131313131333111311311313131313131313"
|
|
||||||
|
|
||||||
#define DPMR_FRAME_SYNC_1 "111333331133131131111313"
|
#define DPMR_FRAME_SYNC_1 "111333331133131131111313"
|
||||||
#define DPMR_FRAME_SYNC_2 "113333131331"
|
#define DPMR_FRAME_SYNC_2 "113333131331"
|
||||||
#define DPMR_FRAME_SYNC_3 "133131333311"
|
#define DPMR_FRAME_SYNC_3 "133131333311"
|
||||||
@@ -718,7 +691,13 @@ typedef struct
|
|||||||
/*
|
/*
|
||||||
* function prototypes
|
* function prototypes
|
||||||
*/
|
*/
|
||||||
|
void processDMRdata (dsd_opts * opts, dsd_state * state);
|
||||||
|
void processDMRvoice (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmrBS (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmrMS (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmrMSData (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
|
||||||
void processdPMRvoice (dsd_opts * opts, dsd_state * state);
|
void processdPMRvoice (dsd_opts * opts, dsd_state * state);
|
||||||
void processAudio (dsd_opts * opts, dsd_state * state);
|
void processAudio (dsd_opts * opts, dsd_state * state);
|
||||||
void processAudioR (dsd_opts * opts, dsd_state * state);
|
void processAudioR (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -769,7 +748,7 @@ void initState (dsd_state * state);
|
|||||||
void usage ();
|
void usage ();
|
||||||
void liveScanner (dsd_opts * opts, dsd_state * state);
|
void liveScanner (dsd_opts * opts, dsd_state * state);
|
||||||
void cleanupAndExit (dsd_opts * opts, dsd_state * state);
|
void cleanupAndExit (dsd_opts * opts, dsd_state * state);
|
||||||
// void sigfun (int sig); //not necesary
|
void sigfun (int sig);
|
||||||
int main (int argc, char **argv);
|
int main (int argc, char **argv);
|
||||||
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv);
|
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv);
|
||||||
void processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
|
void processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
|
||||||
@@ -778,7 +757,8 @@ void resumeScan (dsd_opts * opts, dsd_state * state);
|
|||||||
int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
|
int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
|
||||||
void upsample (dsd_state * state, float invalue);
|
void upsample (dsd_state * state, float invalue);
|
||||||
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
||||||
|
void processNXDNVoice (dsd_opts * opts, dsd_state * state);
|
||||||
|
void processNXDNData (dsd_opts * opts, dsd_state * state);
|
||||||
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
||||||
void processHDU (dsd_opts * opts, dsd_state * state);
|
void processHDU (dsd_opts * opts, dsd_state * state);
|
||||||
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -794,41 +774,45 @@ 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);
|
||||||
short dmr_filter(short sample);
|
short dmr_filter(short sample);
|
||||||
short nxdn_filter(short sample);
|
short nxdn_filter(short sample);
|
||||||
short dpmr_filter(short sample);
|
|
||||||
|
|
||||||
int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr);
|
int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr);
|
||||||
/* Global functions */
|
/* Global functions */ //also borrowed
|
||||||
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
|
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
|
||||||
|
uint32_t ConvertAsciiToByte(uint8_t AsciiMsbByte, uint8_t AsciiLsbByte, uint8_t * OutputByte);
|
||||||
|
void Convert49BitSampleInto7Bytes(char * InputBit, char * OutputByte);
|
||||||
|
void Convert7BytesInto49BitSample(char * InputByte, char * OutputBit);
|
||||||
|
|
||||||
void ncursesOpen (dsd_opts * opts, dsd_state * state);
|
void ncursesOpen (dsd_opts * opts, dsd_state * state);
|
||||||
void ncursesPrinter (dsd_opts * opts, dsd_state * state);
|
void ncursesPrinter (dsd_opts * opts, dsd_state * state);
|
||||||
void ncursesClose ();
|
void ncursesClose ();
|
||||||
|
|
||||||
//new NXDN Functions start here!
|
/* NXDN frame decoding functions */
|
||||||
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
void ProcessNXDNFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
||||||
void nxdn_descramble (uint8_t dibits[], int len);
|
void ProcessNxdnRCCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
||||||
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
|
void ProcessNxdnRTCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
||||||
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
|
void ProcessNxdnRDCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
||||||
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
|
void ProcessNxdnRTCH_C_Frame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
||||||
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348]);
|
void ProcessNXDNIdleData (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
||||||
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
|
void ProcessNXDNFacch1Data (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
||||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
|
void ProcessNXDNUdchData (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
|
||||||
|
//end borrow
|
||||||
|
|
||||||
//OP25 NXDN CRC functions
|
/* NXDN functions */
|
||||||
static inline int load_i(const uint8_t val[], int len);
|
|
||||||
static uint8_t crc6(const uint8_t buf[], int len);
|
|
||||||
static uint16_t crc12f(const uint8_t buf[], int len);
|
|
||||||
static uint16_t crc15(const uint8_t buf[], int len);
|
|
||||||
static uint16_t crc16cac(const uint8_t buf[], int len);
|
|
||||||
|
|
||||||
/* NXDN Convolution functions */
|
|
||||||
void CNXDNConvolution_start(void);
|
void CNXDNConvolution_start(void);
|
||||||
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
|
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
|
||||||
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
|
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
|
||||||
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
|
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
|
||||||
|
uint8_t NXDN_decode_LICH(uint8_t InputLich[8],
|
||||||
//keeping these
|
uint8_t * RFChannelType,
|
||||||
|
uint8_t * FunctionnalChannelType,
|
||||||
|
uint8_t * Option,
|
||||||
|
uint8_t * Direction,
|
||||||
|
uint8_t * CompleteLichBinaryFormat,
|
||||||
|
uint8_t Inverted);
|
||||||
|
uint8_t NXDN_SACCH_raw_part_decode(uint8_t * Input, uint8_t * Output);
|
||||||
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
|
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
|
||||||
|
uint8_t NXDN_FACCH1_decode(uint8_t * Input, uint8_t * Output);
|
||||||
|
uint8_t NXDN_UDCH_decode(uint8_t * Input, uint8_t * Output);
|
||||||
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||||
uint8_t CrcCorrect, uint8_t * ElementsContent);
|
uint8_t CrcCorrect, uint8_t * ElementsContent);
|
||||||
void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
@@ -836,15 +820,13 @@ void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|||||||
char * NXDN_Call_Type_To_Str(uint8_t CallType);
|
char * NXDN_Call_Type_To_Str(uint8_t CallType);
|
||||||
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
|
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
|
||||||
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
|
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
|
||||||
//added these
|
uint16_t CRC15BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
|
||||||
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
uint16_t CRC12BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
|
||||||
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
uint8_t CRC6BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
|
||||||
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 dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
||||||
|
void printdPMRAmbeVoiceSample(dsd_opts * opts, dsd_state * state);
|
||||||
|
void printdPMRRawVoiceFrame (dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
//dPMR functions
|
//dPMR functions
|
||||||
void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble);
|
void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble);
|
||||||
@@ -854,62 +836,41 @@ uint8_t CRC8BitdPMR(uint8_t * BufferIn, uint32_t BitLength);
|
|||||||
void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8]);
|
void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8]);
|
||||||
int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
|
int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
|
||||||
|
|
||||||
|
void ProcessDMR (dsd_opts * opts, dsd_state * state);
|
||||||
|
void DMRDataFrameProcess(dsd_opts * opts, dsd_state * state);
|
||||||
|
void DMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
||||||
//BPTC (Block Product Turbo Code) functions
|
//BPTC (Block Product Turbo Code) functions
|
||||||
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
|
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
|
||||||
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
|
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
|
||||||
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
|
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
|
||||||
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32]);
|
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32]);
|
||||||
|
|
||||||
//Reed Solomon (12,9) functions
|
//Read Solomon (12,9) functions
|
||||||
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
|
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
|
||||||
uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
|
uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
|
||||||
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
|
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
|
||||||
rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword);
|
rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword);
|
||||||
|
|
||||||
//DMR CRC Functions
|
//SYNC DMR data extraction functions
|
||||||
|
void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
|
void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
|
void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state);
|
||||||
uint16_t ComputeCrcCCITT(uint8_t * DMRData);
|
uint16_t ComputeCrcCCITT(uint8_t * DMRData);
|
||||||
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len);
|
|
||||||
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
|
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
|
||||||
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
|
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
|
||||||
|
uint8_t * DmrAlgIdToStr(uint8_t AlgID);
|
||||||
|
uint8_t * DmrAlgPrivacyModeToStr(uint32_t PrivacyMode);
|
||||||
|
|
||||||
//new simplified dmr functions
|
void ProcessDmrPIHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
|
void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
void dmr_data_sync (dsd_opts * opts, dsd_state * state);
|
void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
void Process1Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors, uint8_t type);
|
void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
|
void ProcessMBCData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
|
void ProcessMBChData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
|
void ProcessReservedData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
//Embedded Alias and GPS
|
void ProcessUnifiedData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
|
||||||
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
|
||||||
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
|
||||||
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
|
||||||
|
|
||||||
//"DMR STEREO"
|
|
||||||
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrBS (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrMS (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrMSData (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
|
|
||||||
|
|
||||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified)
|
|
||||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], 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[]);
|
|
||||||
|
|
||||||
//dmr alg stuff
|
|
||||||
void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state);
|
|
||||||
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, char ambe_fr[4][24], char ambe_fr2[4][24], char ambe_fr3[4][24]);
|
|
||||||
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state);
|
|
||||||
|
|
||||||
//DMR FEC/CRC from Boatbod - OP25
|
|
||||||
bool Hamming17123(uint8_t* d);
|
|
||||||
uint8_t crc8(uint8_t bits[], unsigned int len);
|
|
||||||
bool crc8_ok(uint8_t bits[], unsigned int len);
|
|
||||||
|
|
||||||
//LFSR code courtesy of https://github.com/mattames/LFSR/
|
//LFSR code courtesy of https://github.com/mattames/LFSR/
|
||||||
void LFSR(dsd_state * state);
|
void LFSR(dsd_state * state);
|
||||||
@@ -965,40 +926,6 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
//P25 Channel to Frequency
|
//P25 Channel to Frequency
|
||||||
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel);
|
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel);
|
||||||
|
|
||||||
//NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums
|
|
||||||
long int nxdn_channel_to_frequency (dsd_opts * opts, dsd_state * state, uint16_t channel);
|
|
||||||
|
|
||||||
//rigctl functions and TCP/UDP functions
|
|
||||||
void error(char *msg);
|
|
||||||
int Connect (char *hostname, int portno);
|
|
||||||
bool Send(int sockfd, char *buf);
|
|
||||||
bool Recv(int sockfd, char *buf);
|
|
||||||
|
|
||||||
//rtl_fm udp tuning function
|
|
||||||
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency);
|
|
||||||
|
|
||||||
long int GetCurrentFreq(int sockfd);
|
|
||||||
bool SetFreq(int sockfd, long int freq);
|
|
||||||
bool SetModulation(int sockfd, int bandwidth);
|
|
||||||
//commands below unique to GQRX only, not usable on SDR++
|
|
||||||
bool GetSignalLevel(int sockfd, double *dBFS);
|
|
||||||
bool GetSquelchLevel(int sockfd, double *dBFS);
|
|
||||||
bool SetSquelchLevel(int sockfd, double dBFS);
|
|
||||||
bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp);
|
|
||||||
//end gqrx-scanner
|
|
||||||
|
|
||||||
//UDP socket connection
|
|
||||||
int UDPBind (char *hostname, int portno);
|
|
||||||
|
|
||||||
//EDACS
|
|
||||||
void edacs(dsd_opts * opts, dsd_state * state);
|
|
||||||
unsigned long long int edacs_bch (unsigned long long int message);
|
|
||||||
|
|
||||||
//csv imports
|
|
||||||
int csvGroupImport(dsd_opts * opts, dsd_state * state);
|
|
||||||
int csvLCNImport(dsd_opts * opts, dsd_state * state);
|
|
||||||
int csvChanImport(dsd_opts * opts, dsd_state * state);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -1009,9 +936,10 @@ void cleanup_rtlsdr_stream();
|
|||||||
void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
|
void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
|
||||||
void rtlsdr_sighandler();
|
void rtlsdr_sighandler();
|
||||||
#endif
|
#endif
|
||||||
//DMR TRELLIS
|
//TRELLIS
|
||||||
void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* payload);
|
void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* payload);
|
||||||
unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits);
|
unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits);
|
||||||
|
//unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* state, unsigned char* tribits);
|
||||||
bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload);
|
bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload);
|
||||||
void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits);
|
void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits);
|
||||||
void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points);
|
void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points);
|
||||||
@@ -1025,6 +953,7 @@ int ez_rs28_facch (int payload[156], int parity[114]); //ezpwd bridge for FME
|
|||||||
int ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
|
int ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
|
||||||
unsigned long long int isch_lookup (unsigned long long int isch); //isch map lookup
|
unsigned long long int isch_lookup (unsigned long long int isch); //isch map lookup
|
||||||
int bd_bridge (int payload[196], uint8_t decoded[12]); //bridge to Michael Ossmann Block De-interleaver and 1/2 rate trellis decoder
|
int bd_bridge (int payload[196], uint8_t decoded[12]); //bridge to Michael Ossmann Block De-interleaver and 1/2 rate trellis decoder
|
||||||
|
// uint16_t crc16(uint8_t buf[], int len);
|
||||||
int crc16_lb_bridge (int payload[190], int len);
|
int crc16_lb_bridge (int payload[190], int len);
|
||||||
int crc12_xb_bridge (int payload[190], int len);
|
int crc12_xb_bridge (int payload[190], int len);
|
||||||
|
|
||||||
|
|||||||
62
include/nxdn96_const.h
Normal file
62
include/nxdn96_const.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 DSD Author
|
||||||
|
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NXDN AMBE interleave schedule
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MAIN
|
||||||
|
extern const int nW[36];
|
||||||
|
extern const int nX[36];
|
||||||
|
extern const int nY[36];
|
||||||
|
extern const int nZ[36];
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 2,
|
||||||
|
0, 2, 0, 2, 0, 2,
|
||||||
|
0, 2, 0, 2, 0, 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
||||||
|
20, 7, 19, 6, 18, 5,
|
||||||
|
17, 4, 16, 3, 15, 2,
|
||||||
|
14, 1, 13, 0, 12, 10,
|
||||||
|
11, 9, 10, 8, 9, 7,
|
||||||
|
8, 6, 7, 5, 6, 4
|
||||||
|
};
|
||||||
|
|
||||||
|
const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
||||||
|
0, 2, 0, 3, 0, 3,
|
||||||
|
1, 3, 1, 3, 1, 3,
|
||||||
|
1, 3, 1, 3, 1, 3,
|
||||||
|
1, 3, 1, 3, 1, 3,
|
||||||
|
1, 3, 1, 3, 1, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
||||||
|
2, 0, 1, 13, 0, 12,
|
||||||
|
22, 11, 21, 10, 20, 9,
|
||||||
|
19, 8, 18, 7, 17, 6,
|
||||||
|
16, 5, 15, 4, 14, 3,
|
||||||
|
13, 2, 12, 1, 11, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,82 +1,257 @@
|
|||||||
// Portions Below from Osmocom OP25
|
/*
|
||||||
// NXDN Encoder (C) Copyright 2019 Max H. Parke KA1RBI
|
* Copyright (C) 2010 DSD Author
|
||||||
// thx gr-ysf fr_vch_decoder_bb_impl.cc * Copyright 2015 Mathias Weyland *
|
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||||
// OP25 is free software; you can redistribute it and/or modify it
|
*
|
||||||
// under the terms of the GNU General Public License as published by
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
// the Free Software Foundation; either version 3, or (at your option)
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
// any later version.
|
* copyright notice and this permission notice appear in all copies.
|
||||||
//
|
*
|
||||||
// OP25 is distributed in the hope that it will be useful, but WITHOUT
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
// License for more details.
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
//
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
// You should have received a copy of the GNU General Public License
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// along with OP25; see the file COPYING. If not, write to the Free
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
|
*/
|
||||||
// 02110-1301, USA.
|
|
||||||
|
|
||||||
static const uint8_t PERM_12_5[] = {
|
#ifndef _MAIN
|
||||||
0,12,24,36,48,
|
extern const int nW[36];
|
||||||
1,13,25,37,49,
|
extern const int nX[36];
|
||||||
2,14,26,38,50,
|
extern const int nY[36];
|
||||||
3,15,27,39,51,
|
extern const int nZ[36];
|
||||||
4,16,28,40,52,
|
extern const char nxdnpr[145];
|
||||||
5,17,29,41,53,
|
extern const char nxdnpr2[145]; //why is this 145 but referenced later as 182 or 384
|
||||||
6,18,30,42,54,
|
//borrow from LEH
|
||||||
7,19,31,43,55,
|
extern const unsigned char nsW[72];
|
||||||
8,20,32,44,56,
|
extern const int DSDNXDN_SACCH_m_Interleave[60];
|
||||||
9,21,33,45,57,
|
extern const int DSDNXDN_SACCH_m_PunctureList[12];
|
||||||
10,22,34,46,58,
|
extern const int DSDNXDN_FACCH1_m_Interleave[144];
|
||||||
11,23,35,47,59
|
extern const int DSDNXDN_FACCH1_m_PunctureList[48];
|
||||||
|
extern const int DSDNXDN_UDCH_m_Interleave[348];
|
||||||
|
extern const int DSDNXDN_UDCH_m_PunctureList[58];
|
||||||
|
//
|
||||||
|
//enum
|
||||||
|
//{
|
||||||
|
#define NXDN_VCALL 0b000001 /* VCALL = VCALL_REQ = VCALL_RESP */
|
||||||
|
#define NXDN_VCALL_IV 0b000011
|
||||||
|
#define NXDN_DCALL_HDR 0b001001 /* Header Format - NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
|
||||||
|
#define NXDN_DCALL_USR 0b001011 /* User Data Format */
|
||||||
|
#define NXDN_DCALL_ACK 0b001100
|
||||||
|
#define NXDN_REL_EX 0b000111
|
||||||
|
#define NXDN_HEAD_DLY 0b001111
|
||||||
|
#define NXDN_SDCALL_REQ_HDR 0b111000 /* Header Format */
|
||||||
|
#define NXDN_SDCALL_REQ_USR 0b111001 /* User Data Format */
|
||||||
|
#define NXDN_SDCALL_RESP 0b111011
|
||||||
|
#define NXDN_SDCALL_IV 0b111010
|
||||||
|
#define NXDN_STAT_INQ_REQ 0b110000
|
||||||
|
#define NXDN_STAT_INQ_RESP 0b110001
|
||||||
|
#define NXDN_STAT_REQ 0b110010
|
||||||
|
#define NXDN_STAT_RESP 0b110011
|
||||||
|
#define NXDN_REM_CON_REQ 0b110100
|
||||||
|
#define NXDN_REM_CON_RESP 0b110101
|
||||||
|
#define NXDN_REM_CON_E_REQ 0b110110
|
||||||
|
#define NXDN_REM_CON_E_RESP 0b110111
|
||||||
|
//NXDN_VCALL_REQ = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
|
||||||
|
//NXDN_VCALL_RESP = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
|
||||||
|
#define NXDN_VCALL_REC_REQ = 0b000010 /* NXDN_VCALL_REC_REQ = NXDN_VCALL_REC_RESP */
|
||||||
|
//NXDN_VCALL_REC_RESP = 0b000010,
|
||||||
|
#define NXDN_VCALL_CONN_REQ = 0b000011 /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
|
||||||
|
//NXDN_VCALL_CONN_RESP = 0b000011, /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
|
||||||
|
#define NXDN_VCALL_ASSGN_DUP = 0b000101
|
||||||
|
//NXDN_DCALL_REQ = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
|
||||||
|
//NXDN_DCALL_RESP = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
|
||||||
|
#define NXDN_DCALL_REC_REQ = 0b001010 /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
|
||||||
|
//NXDN_DCALL_REC_RESP = 0b001010, /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
|
||||||
|
#define NXDN_DCALL_ASSGN = 0b001110
|
||||||
|
#define NXDN_DCALL_ASSGN_DUP = 0b001101
|
||||||
|
#define NXDN_IDLE 0b010000
|
||||||
|
#define NXDN_DISC_REQ = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
|
||||||
|
#define NXDN_DISC = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* pseudorandom bit sequence
|
||||||
|
*/
|
||||||
|
const char nxdnpr[145] = { 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1 };
|
||||||
|
//const unsigned char nxdnpr2[384] =
|
||||||
|
const unsigned char nxdnpr2[182] = //generated from default in manual with the x^9+x^4+1 poly?
|
||||||
|
{
|
||||||
|
0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0,
|
||||||
|
1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0,
|
||||||
|
0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1,
|
||||||
|
0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1,
|
||||||
|
1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
|
||||||
|
0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
|
||||||
|
1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1,
|
||||||
|
0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
|
||||||
|
1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1,
|
||||||
|
1, 1, 0, 0, 0, 1//, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
|
||||||
|
// 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0,
|
||||||
|
// 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
|
||||||
|
// 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||||
|
// 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1,
|
||||||
|
// 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0,
|
||||||
|
// 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1,
|
||||||
|
// 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1,
|
||||||
|
// 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1,
|
||||||
|
// 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0,
|
||||||
|
// 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||||
|
// 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
// 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
* NXDN AMBE interleave schedule
|
||||||
|
*/
|
||||||
|
const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 2,
|
||||||
|
0, 2, 0, 2, 0, 2,
|
||||||
|
0, 2, 0, 2, 0, 2
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t PERM_16_9[] = {
|
const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
||||||
0, 16, 32, 48, 64, 80, 96, 112, 128,
|
20, 7, 19, 6, 18, 5,
|
||||||
1, 17, 33, 49, 65, 81, 97, 113, 129,
|
17, 4, 16, 3, 15, 2,
|
||||||
2, 18, 34, 50, 66, 82, 98, 114, 130,
|
14, 1, 13, 0, 12, 10,
|
||||||
3, 19, 35, 51, 67, 83, 99, 115, 131,
|
11, 9, 10, 8, 9, 7,
|
||||||
4, 20, 36, 52, 68, 84, 100, 116, 132,
|
8, 6, 7, 5, 6, 4
|
||||||
5, 21, 37, 53, 69, 85, 101, 117, 133,
|
|
||||||
6, 22, 38, 54, 70, 86, 102, 118, 134,
|
|
||||||
7, 23, 39, 55, 71, 87, 103, 119, 135,
|
|
||||||
8, 24, 40, 56, 72, 88, 104, 120, 136,
|
|
||||||
9, 25, 41, 57, 73, 89, 105, 121, 137,
|
|
||||||
10, 26, 42, 58, 74, 90, 106, 122, 138,
|
|
||||||
11, 27, 43, 59, 75, 91, 107, 123, 139,
|
|
||||||
12, 28, 44, 60, 76, 92, 108, 124, 140,
|
|
||||||
13, 29, 45, 61, 77, 93, 109, 125, 141,
|
|
||||||
14, 30, 46, 62, 78, 94, 110, 126, 142,
|
|
||||||
15, 31, 47, 63, 79, 95, 111, 127, 143
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint16_t PERM_12_25[] = {
|
const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
||||||
0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228,240,252,264,276,288,
|
0, 2, 0, 3, 0, 3,
|
||||||
1,13,25,37,49,61,73,85,97,109,121,133,145,157,169,181,193,205,217,229,241,253,265,277,289,
|
1, 3, 1, 3, 1, 3,
|
||||||
2,14,26,38,50,62,74,86,98,110,122,134,146,158,170,182,194,206,218,230,242,254,266,278,290,
|
1, 3, 1, 3, 1, 3,
|
||||||
3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183,195,207,219,231,243,255,267,279,291,
|
1, 3, 1, 3, 1, 3,
|
||||||
4,16,28,40,52,64,76,88,100,112,124,136,148,160,172,184,196,208,220,232,244,256,268,280,292,
|
1, 3, 1, 3, 1, 3
|
||||||
5,17,29,41,53,65,77,89,101,113,125,137,149,161,173,185,197,209,221,233,245,257,269,281,293,
|
|
||||||
6,18,30,42,54,66,78,90,102,114,126,138,150,162,174,186,198,210,222,234,246,258,270,282,294,
|
|
||||||
7,19,31,43,55,67,79,91,103,115,127,139,151,163,175,187,199,211,223,235,247,259,271,283,295,
|
|
||||||
8,20,32,44,56,68,80,92,104,116,128,140,152,164,176,188,200,212,224,236,248,260,272,284,296,
|
|
||||||
9,21,33,45,57,69,81,93,105,117,129,141,153,165,177,189,201,213,225,237,249,261,273,285,297,
|
|
||||||
10,22,34,46,58,70,82,94,106,118,130,142,154,166,178,190,202,214,226,238,250,262,274,286,298,
|
|
||||||
11,23,35,47,59,71,83,95,107,119,131,143,155,167,179,191,203,215,227,239,251,263,275,287,299
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint16_t PERM_12_29[] = {
|
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
||||||
0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228,240,252,264,276,288,300,312,324,336,
|
2, 0, 1, 13, 0, 12,
|
||||||
1,13,25,37,49,61,73,85,97,109,121,133,145,157,169,181,193,205,217,229,241,253,265,277,289,301,313,325,337,
|
22, 11, 21, 10, 20, 9,
|
||||||
2,14,26,38,50,62,74,86,98,110,122,134,146,158,170,182,194,206,218,230,242,254,266,278,290,302,314,326,338,
|
19, 8, 18, 7, 17, 6,
|
||||||
3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183,195,207,219,231,243,255,267,279,291,303,315,327,339,
|
16, 5, 15, 4, 14, 3,
|
||||||
4,16,28,40,52,64,76,88,100,112,124,136,148,160,172,184,196,208,220,232,244,256,268,280,292,304,316,328,340,
|
13, 2, 12, 1, 11, 0
|
||||||
5,17,29,41,53,65,77,89,101,113,125,137,149,161,173,185,197,209,221,233,245,257,269,281,293,305,317,329,341,
|
|
||||||
6,18,30,42,54,66,78,90,102,114,126,138,150,162,174,186,198,210,222,234,246,258,270,282,294,306,318,330,342,
|
|
||||||
7,19,31,43,55,67,79,91,103,115,127,139,151,163,175,187,199,211,223,235,247,259,271,283,295,307,319,331,343,
|
|
||||||
8,20,32,44,56,68,80,92,104,116,128,140,152,164,176,188,200,212,224,236,248,260,272,284,296,308,320,332,344,
|
|
||||||
9,21,33,45,57,69,81,93,105,117,129,141,153,165,177,189,201,213,225,237,249,261,273,285,297,309,321,333,345,
|
|
||||||
10,22,34,46,58,70,82,94,106,118,130,142,154,166,178,190,202,214,226,238,250,262,274,286,298,310,322,334,346,
|
|
||||||
11,23,35,47,59,71,83,95,107,119,131,143,155,167,179,191,203,215,227,239,251,263,275,287,299,311,323,335,347
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NXDN SACCH interleave
|
||||||
|
*/
|
||||||
|
const unsigned char nsW[72] = {
|
||||||
|
0, 12, 24, 36, 48, 60,
|
||||||
|
1, 13, 25, 37, 49, 61,
|
||||||
|
2, 14, 26, 38, 50, 62,
|
||||||
|
3, 15, 27, 39, 51, 63,
|
||||||
|
4, 16, 28, 40, 52, 64,
|
||||||
|
5, 17, 29, 41, 53, 65,
|
||||||
|
6, 18, 30, 42, 54, 66,
|
||||||
|
7, 19, 31, 43, 55, 67,
|
||||||
|
8, 20, 32, 44, 56, 68,
|
||||||
|
9, 21, 33, 45, 57, 69,
|
||||||
|
10,22, 34, 46, 58, 70,
|
||||||
|
11,23, 35, 47, 59, 71
|
||||||
|
};
|
||||||
|
|
||||||
|
const int DSDNXDN_SACCH_m_Interleave[60] =
|
||||||
|
{
|
||||||
|
0, 12, 24, 36, 48,
|
||||||
|
1, 13, 25, 37, 49,
|
||||||
|
2, 14, 26, 38, 50,
|
||||||
|
3, 15, 27, 39, 51,
|
||||||
|
4, 16, 28, 40, 52,
|
||||||
|
5, 17, 29, 41, 53,
|
||||||
|
6, 18, 30, 42, 54,
|
||||||
|
7, 19, 31, 43, 55,
|
||||||
|
8, 20, 32, 44, 56,
|
||||||
|
9, 21, 33, 45, 57,
|
||||||
|
10, 22, 34, 46, 58,
|
||||||
|
11, 23, 35, 47, 59,
|
||||||
|
};
|
||||||
|
|
||||||
|
const int DSDNXDN_SACCH_m_PunctureList[12] = { 5, 11, 17, 23, 29, 35, 41, 47, 53, 59, 65, 71 };
|
||||||
|
|
||||||
|
const unsigned int DSDNXDN_FACCH1_m_Interleave[144] =
|
||||||
|
{
|
||||||
|
0, 16, 32, 48, 64, 80, 96, 112, 128,
|
||||||
|
1, 17, 33, 49, 65, 81, 97, 113, 129,
|
||||||
|
2, 18, 34, 50, 66, 82, 98, 114, 130,
|
||||||
|
3, 19, 35, 51, 67, 83, 99, 115, 131,
|
||||||
|
4, 20, 36, 52, 68, 84, 100, 116, 132,
|
||||||
|
5, 21, 37, 53, 69, 85, 101, 117, 133,
|
||||||
|
6, 22, 38, 54, 70, 86, 102, 118, 134,
|
||||||
|
7, 23, 39, 55, 71, 87, 103, 119, 135,
|
||||||
|
8, 24, 40, 56, 72, 88, 104, 120, 136,
|
||||||
|
9, 25, 41, 57, 73, 89, 105, 121, 137,
|
||||||
|
10, 26, 42, 58, 74, 90, 106, 122, 138,
|
||||||
|
11, 27, 43, 59, 75, 91, 107, 123, 139,
|
||||||
|
12, 28, 44, 60, 76, 92, 108, 124, 140,
|
||||||
|
13, 29, 45, 61, 77, 93, 109, 125, 141,
|
||||||
|
14, 30, 46, 62, 78, 94, 110, 126, 142,
|
||||||
|
15, 31, 47, 63, 79, 95, 111, 127, 143,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned int DSDNXDN_FACCH1_m_PunctureList[48] =
|
||||||
|
{
|
||||||
|
1U, 5U, 9U, 13U, 17U, 21U, 25U, 29U, 33U, 37U,
|
||||||
|
41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, 73U, 77U,
|
||||||
|
81U, 85U, 89U, 93U, 97U, 101U, 105U, 109U, 113U, 117U,
|
||||||
|
121U, 125U, 129U, 133U, 137U, 141U, 145U, 149U, 153U, 157U,
|
||||||
|
161U, 165U, 169U, 173U, 177U, 181U, 185U, 189U
|
||||||
|
};
|
||||||
|
|
||||||
|
const int DSDNXDN_UDCH_m_Interleave[348] = {
|
||||||
|
0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180, 192, 204, 216, 228, 240, 252, 264, 276, 288, 300, 312, 324, 336,
|
||||||
|
1, 13, 25, 37, 49, 61, 73, 85, 97, 109, 121, 133, 145, 157, 169, 181, 193, 205, 217, 229, 241, 253, 265, 277, 289, 301, 313, 325, 337,
|
||||||
|
2, 14, 26, 38, 50, 62, 74, 86, 98, 110, 122, 134, 146, 158, 170, 182, 194, 206, 218, 230, 242, 254, 266, 278, 290, 302, 314, 326, 338,
|
||||||
|
3, 15, 27, 39, 51, 63, 75, 87, 99, 111, 123, 135, 147, 159, 171, 183, 195, 207, 219, 231, 243, 255, 267, 279, 291, 303, 315, 327, 339,
|
||||||
|
4, 16, 28, 40, 52, 64, 76, 88, 100, 112, 124, 136, 148, 160, 172, 184, 196, 208, 220, 232, 244, 256, 268, 280, 292, 304, 316, 328, 340,
|
||||||
|
5, 17, 29, 41, 53, 65, 77, 89, 101, 113, 125, 137, 149, 161, 173, 185, 197, 209, 221, 233, 245, 257, 269, 281, 293, 305, 317, 329, 341,
|
||||||
|
6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 126, 138, 150, 162, 174, 186, 198, 210, 222, 234, 246, 258, 270, 282, 294, 306, 318, 330, 342,
|
||||||
|
7, 19, 31, 43, 55, 67, 79, 91, 103, 115, 127, 139, 151, 163, 175, 187, 199, 211, 223, 235, 247, 259, 271, 283, 295, 307, 319, 331, 343,
|
||||||
|
8, 20, 32, 44, 56, 68, 80, 92, 104, 116, 128, 140, 152, 164, 176, 188, 200, 212, 224, 236, 248, 260, 272, 284, 296, 308, 320, 332, 344,
|
||||||
|
9, 21, 33, 45, 57, 69, 81, 93, 105, 117, 129, 141, 153, 165, 177, 189, 201, 213, 225, 237, 249, 261, 273, 285, 297, 309, 321, 333, 345,
|
||||||
|
10, 22, 34, 46, 58, 70, 82, 94, 106, 118, 130, 142, 154, 166, 178, 190, 202, 214, 226, 238, 250, 262, 274, 286, 298, 310, 322, 334, 346,
|
||||||
|
11, 23, 35, 47, 59, 71, 83, 95, 107, 119, 131, 143, 155, 167, 179, 191, 203, 215, 227, 239, 251, 263, 275, 287, 299, 311, 323, 335, 347,
|
||||||
|
};
|
||||||
|
|
||||||
|
const int DSDNXDN_UDCH_m_PunctureList[58] = {
|
||||||
|
3, 11,
|
||||||
|
17, 25,
|
||||||
|
31, 39,
|
||||||
|
45, 53,
|
||||||
|
59, 67,
|
||||||
|
73, 81,
|
||||||
|
87, 95,
|
||||||
|
101, 109,
|
||||||
|
115, 123,
|
||||||
|
129, 137,
|
||||||
|
143, 151,
|
||||||
|
157, 165,
|
||||||
|
171, 179,
|
||||||
|
185, 193,
|
||||||
|
199, 207,
|
||||||
|
213, 221,
|
||||||
|
227, 235,
|
||||||
|
241, 249,
|
||||||
|
255, 263,
|
||||||
|
269, 277,
|
||||||
|
283, 291,
|
||||||
|
297, 305,
|
||||||
|
311, 319,
|
||||||
|
325, 333,
|
||||||
|
339, 347,
|
||||||
|
353, 361,
|
||||||
|
367, 375,
|
||||||
|
381, 389,
|
||||||
|
395, 403,
|
||||||
|
};
|
||||||
|
|
||||||
|
//#endif /* _MAIN */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
sudo cp tone8.wav /usr/share/
|
sudo cp tone8.wav /usr/share/
|
||||||
sudo cp tone24.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/tone8.wav
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
sudo chmod 777 /usr/share/tone48.wav
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
|
|||||||
@@ -5,10 +5,15 @@ echo Automatic Git Pull and Rebuild
|
|||||||
echo
|
echo
|
||||||
sleep 1
|
sleep 1
|
||||||
##Open your clone folder##
|
##Open your clone folder##
|
||||||
git pull
|
git pull https://github.com/lwvmobile/dsd-fme pulseaudio
|
||||||
sleep 2
|
sleep 2
|
||||||
|
sudo cp tone8.wav /usr/share/
|
||||||
|
sudo cp tone24.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
##cd into your build folder##
|
##cd into your build folder##
|
||||||
cd build
|
cd build
|
||||||
|
##cmake usually isn't necesary, but could be if I update the cmakelist.txt
|
||||||
cmake ..
|
cmake ..
|
||||||
make -j `nproc`
|
make -j `nproc`
|
||||||
sudo make install
|
sudo make install
|
||||||
|
|||||||
462
src/dmr_block.c
462
src/dmr_block.c
@@ -1,462 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_block.c
|
|
||||||
* DMR Data Header and Data Block Assembly/Handling
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: ONLY RUN ON GOOD CRC?
|
|
||||||
|
|
||||||
//collect relevant data header info, figure out format, and grab appropriate info from it
|
|
||||||
if (state->data_p_head[slot] == 0)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
if ( (state->data_header_format[slot] & 0xF) == 0x2)
|
|
||||||
{
|
|
||||||
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 ( (state->data_header_format[slot] & 0xF) == 0x3)
|
|
||||||
{
|
|
||||||
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 ( (state->data_header_format[slot] & 0xF) == 0xD) //DD_HEAD
|
|
||||||
{
|
|
||||||
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) ); //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
|
|
||||||
{
|
|
||||||
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] );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 ", KMAG);
|
|
||||||
|
|
||||||
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++)
|
|
||||||
{
|
|
||||||
state->dmr_pdu_sf[slot][i+(blocks*12)] = dheader[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//only set this flag after everything else is processed
|
|
||||||
if (state->data_header_sap[slot] == 0x9 && state->data_p_head[slot] == 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
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//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];
|
|
||||||
|
|
||||||
//crc specific variables
|
|
||||||
uint8_t len = 24; //len of completed message needing crc32 number of blocks*block_len - 12?
|
|
||||||
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[18*8*8]; //8 blocks at 18 bytes at 8 bits //just a large value to have plenty storage space
|
|
||||||
|
|
||||||
//MBC Header and Block CRC
|
|
||||||
uint8_t mbc_crc_good[2]; //header and blocks crc pass/fail local storage
|
|
||||||
memset (mbc_crc_good, 0, sizeof (mbc_crc_good)); //init on 0 - bad crc
|
|
||||||
|
|
||||||
if (type == 1) blocks = state->data_header_blocks[slot] - 1;
|
|
||||||
if (type == 2) blocks = state->data_block_counter[slot];
|
|
||||||
|
|
||||||
//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 (block_len == 0) block_len = 18;
|
|
||||||
if (block_len > 24) block_len = 24;
|
|
||||||
|
|
||||||
//TODO: add CRC/FEC validation to each data header and each confirmed data block as well - type 1
|
|
||||||
if (type == 1)
|
|
||||||
{
|
|
||||||
//type 1 data block, shuffle method
|
|
||||||
for (j = 0; j < blocks; j++) //6
|
|
||||||
{
|
|
||||||
for (i = 0; i < block_len; i++) //block_len
|
|
||||||
{
|
|
||||||
state->dmr_pdu_sf[slot][i+block_len*j] = state->dmr_pdu_sf[slot][i+block_len*(j+1)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < block_len; i++)
|
|
||||||
{
|
|
||||||
state->dmr_pdu_sf[slot][i+(blocks*block_len)] = block_bytes[i];
|
|
||||||
}
|
|
||||||
//time to send the completed 'superframe' to the DMR PDU message handler
|
|
||||||
if (state->data_block_counter[slot] == state->data_header_blocks[slot])
|
|
||||||
{
|
|
||||||
//TODO: CRC32 on completed messages
|
|
||||||
dmr_pdu (opts, state, block_len, state->dmr_pdu_sf[slot]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//type 2 - MBC header and MBC 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;
|
|
||||||
|
|
||||||
//Type 2 data block, additive method
|
|
||||||
for (i = 0; i < block_len; i++)
|
|
||||||
{
|
|
||||||
state->dmr_pdu_sf[slot][i+(blockcounter*block_len)] = block_bytes[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
memset (dmr_pdu_sf_bits, 0, sizeof(dmr_pdu_sf_bits));
|
|
||||||
|
|
||||||
lb = block_bytes[0] >> 7; //last block flag
|
|
||||||
pf = (block_bytes[0] >> 6) & 1;
|
|
||||||
|
|
||||||
if (lb == 1) //last block arrived, time to send to cspdu decoder
|
|
||||||
{
|
|
||||||
for(i = 0, j = 0; i < 12*4; i++, j+=8) //
|
|
||||||
{
|
|
||||||
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
|
||||||
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
|
||||||
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
|
||||||
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
|
||||||
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
|
||||||
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
|
||||||
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
|
||||||
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
//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
|
|
||||||
|
|
||||||
mbc_crc_good[0] = state->data_block_crc_valid[slot][0];
|
|
||||||
|
|
||||||
CRCExtracted = 0;
|
|
||||||
//extract crc from last block, apply to completed 'superframe' minus header
|
|
||||||
for(i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
CRCExtracted = CRCExtracted << 1;
|
|
||||||
CRCExtracted = CRCExtracted | (uint32_t)(dmr_pdu_sf_bits[i + 96*(1+blocks) - 16] & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t mbc_block_bits[12*8*3];
|
|
||||||
memset (mbc_block_bits, 0, sizeof(mbc_block_bits));
|
|
||||||
//shift continuation blocks and last block into seperate array for crc check
|
|
||||||
for (i = 0; i < 12*8*3; i++)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
IrrecoverableErrors = 1;
|
|
||||||
|
|
||||||
//set good on good header and good blocks
|
|
||||||
if (mbc_crc_good[0] == 1 && mbc_crc_good[1] == 1)
|
|
||||||
{
|
|
||||||
CRCCorrect = 1;
|
|
||||||
IrrecoverableErrors = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 Multi Block PDU Superframe - Slot [%d]\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 ", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Full Super Frame MBC - Debug Output
|
|
||||||
if (opts->payload == 1 && type == 2 && lb == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf (stderr, "\n MBC Multi Block PDU Superframe - Slot [%d]\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++)
|
|
||||||
{
|
|
||||||
state->dmr_pdu_sf[slot][i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->data_block_crc_valid[slot][0] = 0; //Zero Out MBC Header Block CRC Valid
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//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
|
|
||||||
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_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));
|
|
||||||
}
|
|
||||||
727
src/dmr_bs.c
727
src/dmr_bs.c
@@ -1,11 +1,3 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_bs.c
|
|
||||||
* DMR BS Voice Handling and Data Gathering Routines - "DMR STEREO"
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
#include "dmr_const.h"
|
#include "dmr_const.h"
|
||||||
|
|
||||||
@@ -15,37 +7,32 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
int i, j, k, l, dibit;
|
int i, j, k, l, dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
|
char ambe_fr[4][24] = {0};
|
||||||
char ambe_fr[4][24];
|
char ambe_fr2[4][24] = {0};
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr3[4][24] = {0};
|
||||||
char ambe_fr3[4][24];
|
|
||||||
|
|
||||||
//redundancy check (carrier signal loss event)
|
const int *w, *x, *y, *z;
|
||||||
|
char sync[25] = {0};
|
||||||
|
char syncdata[48] = {0};
|
||||||
|
//char cachdata[13] = {0};
|
||||||
|
int mutecurrentslot;
|
||||||
|
int msMode;
|
||||||
|
char cc[4];
|
||||||
|
unsigned char EmbeddedSignalling[16] = {0};
|
||||||
|
int EmbeddedSignallingOk;
|
||||||
|
unsigned int internalcolorcode;
|
||||||
|
int internalslot;
|
||||||
char redundancyA[36];
|
char redundancyA[36];
|
||||||
char redundancyB[36];
|
char redundancyB[36];
|
||||||
|
unsigned short int vc1;
|
||||||
|
unsigned short int vc2;
|
||||||
|
int cach_okay = 0;
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
//cach fec
|
||||||
char m1[4][24];
|
char cachdata[25] = {0}; //increased from 24 to 25 to see if this fixes ubuntu 32 bug
|
||||||
char m2[4][24];
|
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
|
||||||
char m3[4][24];
|
|
||||||
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
char sync[25];
|
|
||||||
char syncdata[48];
|
|
||||||
char EmbeddedSignalling[16];
|
|
||||||
|
|
||||||
uint8_t emb_ok = 0;
|
|
||||||
uint8_t tact_okay = 0;
|
|
||||||
uint8_t cach_err = 0;
|
|
||||||
|
|
||||||
uint8_t internalslot;
|
|
||||||
uint8_t vc1;
|
|
||||||
uint8_t vc2;
|
|
||||||
|
|
||||||
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
|
|
||||||
uint8_t dummy_bits[196];
|
|
||||||
memset (dummy_bits, 0, sizeof(dummy_bits));
|
|
||||||
|
|
||||||
//add time to mirror printFrameSync
|
//add time to mirror printFrameSync
|
||||||
time_t now;
|
time_t now;
|
||||||
char * getTime(void) //get pretty hh:mm:ss timestamp
|
char * getTime(void) //get pretty hh:mm:ss timestamp
|
||||||
@@ -76,21 +63,10 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
short int loop = 1;
|
short int loop = 1;
|
||||||
short int skipcount = 0;
|
short int skipcount = 0;
|
||||||
|
|
||||||
//cach
|
|
||||||
char cachdata[25];
|
|
||||||
int cachInterleave[24] =
|
|
||||||
{0, 7, 8, 9, 1, 10,
|
|
||||||
11, 12, 2, 13, 14,
|
|
||||||
15, 3, 16, 4, 17, 18,
|
|
||||||
19, 5, 20, 21, 22, 6, 23
|
|
||||||
};
|
|
||||||
|
|
||||||
//cach tact bits
|
|
||||||
uint8_t tact_bits[7];
|
|
||||||
|
|
||||||
//Run Loop while the getting is good
|
//Run Loop while the getting is good
|
||||||
while (loop == 1) {
|
while (loop == 1) {
|
||||||
|
|
||||||
|
// CACH
|
||||||
internalslot = -1; //reset here so we know if this value is being set properly
|
internalslot = -1; //reset here so we know if this value is being set properly
|
||||||
for(i = 0; i < 12; i++)
|
for(i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
@@ -101,23 +77,30 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
//decode and correct cach and compare
|
||||||
|
cach_okay = 69;
|
||||||
|
if ( Hamming_7_4_decode (cachdata) ) //see if we need to de-interleave this...probably do.
|
||||||
{
|
{
|
||||||
tact_bits[i] = cachdata[i];
|
cach_okay = 1;
|
||||||
}
|
}
|
||||||
|
if (cach_okay == 1)
|
||||||
//disabled for dmr_cach testing
|
|
||||||
tact_okay = 0;
|
|
||||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
|
||||||
if (tact_okay != 1)
|
|
||||||
{
|
{
|
||||||
|
//fprintf (stderr, "CACH Okay ");
|
||||||
|
}
|
||||||
|
if (cach_okay != 1)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
|
||||||
if (opts->aggressive_framesync == 1)
|
if (opts->aggressive_framesync == 1)
|
||||||
{
|
{
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internalslot = state->currentslot = tact_bits[1];
|
state->currentslot = cachdata[1];
|
||||||
|
internalslot = cachdata[1];
|
||||||
|
|
||||||
|
//this cach fec seems to work quite well, really helps on shaky signals
|
||||||
|
|
||||||
//Setup for first AMBE Frame
|
//Setup for first AMBE Frame
|
||||||
//Interleave Schedule
|
//Interleave Schedule
|
||||||
@@ -132,7 +115,6 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
state->dmr_stereo_payload[i+12] = dibit;
|
state->dmr_stereo_payload[i+12] = dibit;
|
||||||
redundancyA[i] = dibit;
|
redundancyA[i] = dibit;
|
||||||
|
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -152,9 +134,11 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
|
// end redundancy test, set B to A
|
||||||
//end redundancy test, set B to A
|
for(i = 0; i < 36; i++)
|
||||||
memcpy (redundancyB, redundancyA, sizeof (redundancyA));
|
{
|
||||||
|
redundancyB[i] = redundancyA[i];
|
||||||
|
}
|
||||||
|
|
||||||
//Setup for Second AMBE Frame
|
//Setup for Second AMBE Frame
|
||||||
//Interleave Schedule
|
//Interleave Schedule
|
||||||
@@ -178,39 +162,90 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//signaling data or sync
|
//short int l = 0; //do I still use this?
|
||||||
|
// signaling data or sync
|
||||||
|
long long int late_entry_pi[24] = {0};
|
||||||
|
short int m = 0;
|
||||||
for(i = 0; i < 24; i++)
|
for(i = 0; i < 24; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+66] = dibit;
|
state->dmr_stereo_payload[i+66] = dibit;
|
||||||
sync[i] = (dibit | 1) + 48; //how does this work again?
|
|
||||||
|
|
||||||
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
sync[i] = (dibit | 1) + 48;
|
||||||
|
|
||||||
//embedded link control
|
if (i < 4 || i > 19)
|
||||||
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
|
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
// fprintf(stderr, "\n EMB = ");
|
||||||
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
|
EmbeddedSignalling[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
//fprintf (stderr, "%02b", dibit);
|
||||||
|
EmbeddedSignalling[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Used for ProcessVoiceBurstSync
|
||||||
|
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
|
||||||
|
{
|
||||||
|
/* Time slot 1 superframe buffer filling => SYNC data */
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2+1] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
|
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
/* Time slot 2 superframe buffer filling => SYNC data */
|
||||||
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2+1] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sync[24] = 0;
|
sync[24] = 0;
|
||||||
|
|
||||||
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
|
EmbeddedSignallingOk = -1;
|
||||||
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
|
if(QR_16_7_6_decode(EmbeddedSignalling))
|
||||||
|
{
|
||||||
|
EmbeddedSignallingOk = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
internalcolorcode = 69; //set so we know if this value is being set properly
|
||||||
|
|
||||||
|
if( EmbeddedSignallingOk == 1 ) //don't set on 1?
|
||||||
|
{
|
||||||
|
//state->color_code = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||||
|
internalcolorcode = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||||
|
state->color_code_ok = EmbeddedSignallingOk;
|
||||||
|
|
||||||
|
//Power Indicator, not the other PI (header)
|
||||||
|
state->PI = (unsigned int)EmbeddedSignalling[4];
|
||||||
|
state->PI_ok = EmbeddedSignallingOk;
|
||||||
|
|
||||||
|
//Link Control Start Stop Indicator
|
||||||
|
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
||||||
|
state->LCSS_ok = EmbeddedSignallingOk;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, " %b", EmbeddedSignalling[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//else skipcount++;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, " %b", EmbeddedSignalling[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
//dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i+90] = dibit;
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
@@ -233,6 +268,10 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
//dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i+108] = dibit;
|
state->dmr_stereo_payload[i+108] = dibit;
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||||
@@ -247,148 +286,305 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//reset vc counters to 1 if new voice sync frame on each slot
|
//reset vc counters to 1 if new voice sync frame on each slot
|
||||||
if ( strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
|
if ( strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
|
||||||
{
|
{
|
||||||
if (internalslot == 0) vc1 = 1;
|
if (internalslot == 0)
|
||||||
if (internalslot == 1) vc2 = 1;
|
{
|
||||||
|
vc1 = 1;
|
||||||
|
state->DMRvcL = 0; //reset here if internal sync forces reset
|
||||||
|
}
|
||||||
|
if (internalslot == 1)
|
||||||
|
{
|
||||||
|
vc2 = 1;
|
||||||
|
state->DMRvcR = 0; //reset here if internal sync forces reset
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if( strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
|
||||||
|
// {
|
||||||
|
// fprintf (stderr,"%s ", getTime());
|
||||||
|
// fprintf (stderr, " Sync: DMR TDMA Direct Mode TS 1 \n");
|
||||||
|
// goto SKIP;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if( strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
||||||
|
// {
|
||||||
|
// fprintf (stderr,"%s ", getTime());
|
||||||
|
// fprintf (stderr, " Sync: DMR TDMA Direct Mode TS 2 \n");
|
||||||
|
// goto SKIP;
|
||||||
|
// }
|
||||||
|
|
||||||
//check for sync pattern here after collected the rest of the payload, decide what to do with it
|
//check for sync pattern here after collected the rest of the payload, decide what to do with it
|
||||||
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
|
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
|
||||||
{
|
{
|
||||||
|
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", getTime());
|
||||||
if (internalslot == 0)
|
if (internalslot == 0)
|
||||||
{
|
{
|
||||||
|
sprintf(state->slot1light, "[slot1]");
|
||||||
|
sprintf(state->slot2light, " slot2 ");
|
||||||
|
|
||||||
if (opts->inverted_dmr == 0)
|
if (opts->inverted_dmr == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Sync: +DMR ");
|
fprintf (stderr,"Sync: +DMR ");
|
||||||
}
|
}
|
||||||
else fprintf (stderr,"Sync: -DMR ");
|
else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
//constantly reset the vc counter to 1 each data frame in anticipation of new voice frame
|
||||||
vc1 = 1;
|
vc1 = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
if (internalslot == 1)
|
if (internalslot == 1)
|
||||||
{
|
{
|
||||||
|
sprintf(state->slot2light, "[slot2]");
|
||||||
|
sprintf(state->slot1light, " slot1 ");
|
||||||
|
|
||||||
if (opts->inverted_dmr == 0)
|
if (opts->inverted_dmr == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Sync: +DMR ");
|
fprintf (stderr,"Sync: +DMR ");
|
||||||
}
|
}
|
||||||
else fprintf (stderr,"Sync: -DMR ");
|
else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
//constantly reset the vc counter to 1 each data frame in anticipation of new voice frame
|
||||||
vc2 = 1;
|
vc2 = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
dmr_data_sync (opts, state);
|
processDMRdata (opts, state);
|
||||||
|
skipcount++; //after 2 data frames, drop back to getFrameSync and process subsequent data with processDMRdata
|
||||||
|
goto SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
//find way to mitigate or correct coming back here after leaving in inverted signal
|
||||||
|
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 && opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
skipcount++;
|
skipcount++;
|
||||||
goto SKIP;
|
goto SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
//only play voice on no data sync
|
//extra check to see if we have missed a voice/data frame sync at this point and then exit if required
|
||||||
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0) //we already have a tact ecc check, so we won't get here without that, see if there is any other eccs we can run just to make sure
|
if (1 == 1) //opts->aggressive_framesync
|
||||||
{
|
{
|
||||||
|
//extremely aggressive
|
||||||
//check the embedded signalling, if bad at this point, we probably aren't quite in sync
|
if ( vc1 > 7 || vc2 > 7 )
|
||||||
if(QR_16_7_6_decode(EmbeddedSignalling)) emb_ok = 1;
|
|
||||||
else emb_ok = 0;
|
|
||||||
|
|
||||||
//disable the goto END; if this causes more problems than fixing on late entry dual voices i.e., lots of forced resyncs
|
|
||||||
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
|
|
||||||
else if (emb_ok == 1)
|
|
||||||
{
|
{
|
||||||
uint8_t cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
goto END;
|
||||||
uint8_t power = EmbeddedSignalling[4];
|
|
||||||
uint8_t lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//only play voice on no data sync
|
||||||
|
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (EmbeddedSignallingOk == 0)
|
||||||
|
{
|
||||||
|
//goto END;
|
||||||
|
}
|
||||||
skipcount = 0; //reset skip count if processing voice frames
|
skipcount = 0; //reset skip count if processing voice frames
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", getTime());
|
||||||
|
|
||||||
//simplifying things
|
|
||||||
char polarity[3];
|
|
||||||
char light[18];
|
|
||||||
uint8_t vc;
|
|
||||||
if (internalslot == 0)
|
if (internalslot == 0)
|
||||||
{
|
{
|
||||||
state->dmrburstL = 16;
|
state->dmrburstL = 16; //use 16 for Voice?
|
||||||
vc = vc1;
|
|
||||||
sprintf (light, "%s", " [SLOT1] slot2 ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->dmrburstR = 16;
|
|
||||||
vc = vc2;
|
|
||||||
sprintf (light, "%s", " slot1 [SLOT2] ");
|
|
||||||
}
|
|
||||||
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
|
||||||
else sprintf (polarity, "%s", "-");
|
|
||||||
|
|
||||||
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
if (internalslot == 0 && vc1 == 6)
|
fprintf (stderr,"Sync: +DMR [SLOT1] slot2 | | DMRSTEREO | VC%d ",vc1);
|
||||||
{
|
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
|
||||||
//process embedded link control
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "%s", KYEL);
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: -DMR [SLOT1] slot2 | | DMRSTEREO | VC%d ",vc1);
|
||||||
|
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (internalslot == 1 && vc2 == 6)
|
if (internalslot == 1)
|
||||||
{
|
{
|
||||||
//process embedded link control
|
state->dmrburstR = 16; //use 16 for Voice?
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR slot1 [SLOT2] | | DMRSTEREO | VC%d ",vc2);
|
||||||
|
if (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
if (state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: -DMR slot1 [SLOT2] | | DMRSTEREO | VC%d ",vc2);
|
||||||
|
if (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
if (state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues)
|
||||||
|
{
|
||||||
|
//process voice burst
|
||||||
|
ProcessVoiceBurstSync(opts, state);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
if (internalslot == 1 && vc2 == 6) //presumably when full (and no sync issues)
|
||||||
|
{
|
||||||
//copy ambe_fr frames first, running process mbe will correct them,
|
//process voice burst
|
||||||
//but this also leads to issues extracting good le mi values when
|
ProcessVoiceBurstSync(opts, state);
|
||||||
//we go to do correction on them there too
|
fprintf (stderr, "\n");
|
||||||
memcpy (m1, ambe_fr, sizeof(m1));
|
}
|
||||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
|
||||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
if (internalslot == 0 && vc1 == 6)
|
||||||
fprintf (stderr, "\n");
|
|
||||||
|
|
||||||
// run alg refresh after vc6 ambe processing
|
|
||||||
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
|
|
||||||
if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state);
|
|
||||||
|
|
||||||
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
|
|
||||||
|
|
||||||
//increment the vc counters
|
|
||||||
if (internalslot == 0) vc1++;
|
|
||||||
if (internalslot == 1) vc2++;
|
|
||||||
|
|
||||||
//update cc amd vc sync time for trunking purposes (particularly Con+)
|
|
||||||
if (opts->p25_is_tuned == 1)
|
|
||||||
{
|
{
|
||||||
state->last_vc_sync_time = time(NULL);
|
state->DMRvcL = 0;
|
||||||
state->last_cc_sync_time = time(NULL);
|
if (state->payload_algid >= 0x21)
|
||||||
}
|
{
|
||||||
|
|
||||||
//reset err checks
|
if (state->payload_mi != 0 && state->payload_algid >= 0x21)
|
||||||
cach_err = 1;
|
{
|
||||||
tact_okay = 0;
|
LFSR(state);
|
||||||
emb_ok = 0;
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Extra safeguards to break loop
|
if (internalslot == 1 && vc2 == 6)
|
||||||
// if ( (vc1 > 7 && vc2 > 7) ) goto END;
|
{
|
||||||
if ( (vc1 > 14 || vc2 > 14) ) goto END;
|
state->DMRvcR = 0;
|
||||||
|
if (state->payload_algidR >= 0x21)
|
||||||
|
{
|
||||||
|
if (state->payload_miR != 0 && state->payload_algidR >= 0x21)
|
||||||
|
{
|
||||||
|
LFSR(state);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (internalslot == 0)
|
||||||
|
{
|
||||||
|
vc1++;
|
||||||
|
}
|
||||||
|
if (internalslot == 1)
|
||||||
|
{
|
||||||
|
vc2++;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SKIP:
|
// recalibrate center/umid/lmid, not sure this helps or does anything particularly in this case
|
||||||
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with dmr_data_sync
|
state->center = ((state->max) + (state->min)) / 2;
|
||||||
|
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
|
||||||
|
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
|
||||||
|
|
||||||
|
//Escape conditions to break the loop
|
||||||
|
//7 seems optimal, allows for an extra frame each in case of late entry on dual voices
|
||||||
|
if ( (vc1 > 7 && vc2 > 7) )
|
||||||
|
{
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
//14 probably allows a few more in case of a bit of jitter but eventually breaks loop (1 second?)
|
||||||
|
if ( (vc1 > 14 || vc2 > 14) )
|
||||||
|
{
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
//earlier skip conditions teleport us here to process further skip conditions before restarting while loop
|
||||||
|
SKIP:
|
||||||
|
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with processDMRdata
|
||||||
{
|
{
|
||||||
//set tests to all good so we don't get a bogus/redundant voice error
|
|
||||||
cach_err = 0;
|
|
||||||
tact_okay = 1;
|
|
||||||
emb_ok = 1;
|
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,41 +595,26 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // while loop
|
} // while loop
|
||||||
|
//Teleport here on END condition, set or reset variables and return to look for new frame sync
|
||||||
END:
|
END:
|
||||||
state->dmr_stereo = 0;
|
state->dmr_stereo = 0;
|
||||||
state->errs = 0;
|
|
||||||
state->errs2 = 0;
|
|
||||||
state->errs2R = 0;
|
state->errs2R = 0;
|
||||||
state->errs2 = 0;
|
state->errs2 = 0;
|
||||||
|
|
||||||
//if we have a tact or emb err, then produce sync pattern/err message
|
//if we have a cach err, then produce sync pattern/err message
|
||||||
if (tact_okay != 1 || emb_ok != 1)
|
if (cach_okay != 1)
|
||||||
{
|
{
|
||||||
|
fprintf (stderr,"%s ", getTime());
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"Sync: DMR ");
|
||||||
fprintf (stderr,"Sync: DMR ");
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "| ** VOICE CACH ERR ** ");
|
||||||
fprintf (stderr, "| VOICE CACH/EMB ERR");
|
fprintf (stderr, "%s", KNRM);
|
||||||
fprintf (stderr, "%s", KNRM);
|
//run LFSR if either slot had an active MI in it.
|
||||||
fprintf (stderr, "\n");
|
state->currentslot = 0;
|
||||||
//run LFSR if either slot had an active MI in it.
|
if (state->payload_algid >= 0x21) LFSR(state);
|
||||||
if (state->payload_algid >= 0x21)
|
state->currentslot = 1;
|
||||||
{
|
if (state->payload_algidR >= 0x21) LFSR(state);
|
||||||
state->currentslot = 0;
|
fprintf (stderr, "\n");
|
||||||
LFSR(state);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
if (state->payload_algidR >= 0x21)
|
|
||||||
{
|
|
||||||
state->currentslot = 1;
|
|
||||||
LFSR(state);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
//failsafe to reset all data header and blocks when bad tact or emb
|
|
||||||
dmr_reset_blocks (opts, state);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -443,29 +624,22 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
int i, j, k, l, dibit;
|
int i, j, k, l, dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24] = {0};
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr2[4][24] = {0};
|
||||||
char ambe_fr3[4][24];
|
char ambe_fr3[4][24] = {0};
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
|
||||||
char m1[4][24];
|
|
||||||
char m2[4][24];
|
|
||||||
char m3[4][24];
|
|
||||||
|
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
char sync[25];
|
char sync[25];
|
||||||
uint8_t tact_okay = 0;
|
char syncdata[48];
|
||||||
uint8_t cach_err = 0;
|
int mutecurrentslot;
|
||||||
uint8_t sync_okay = 1;
|
int msMode;
|
||||||
|
char cc[4];
|
||||||
|
unsigned char EmbeddedSignalling[16];
|
||||||
|
unsigned int EmbeddedSignallingOk;
|
||||||
|
|
||||||
|
//cach fec
|
||||||
|
char cachdata[25] = {0}; //increased from 24 to 25 to see if this fixes ubuntu 32 bug
|
||||||
|
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
|
||||||
|
|
||||||
char cachdata[25];
|
|
||||||
int cachInterleave[24] =
|
|
||||||
{0, 7, 8, 9, 1, 10,
|
|
||||||
11, 12, 2, 13, 14,
|
|
||||||
15, 3, 16, 4, 17, 18,
|
|
||||||
19, 5, 20, 21, 22, 6, 23
|
|
||||||
};
|
|
||||||
//add time to mirror printFrameSync
|
//add time to mirror printFrameSync
|
||||||
time_t now;
|
time_t now;
|
||||||
char * getTime(void) //get pretty hh:mm:ss timestamp
|
char * getTime(void) //get pretty hh:mm:ss timestamp
|
||||||
@@ -504,24 +678,27 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
//cach tact bits
|
//decode and correct cach and compare
|
||||||
uint8_t tact_bits[7];
|
int cach_okay = 69;
|
||||||
for (i = 0; i < 7; i++)
|
if ( Hamming_7_4_decode (cachdata) ) //see if we need to de-interleave this...probably do.
|
||||||
{
|
{
|
||||||
tact_bits[i] = cachdata[i];
|
cach_okay = 1;
|
||||||
}
|
}
|
||||||
|
if (cach_okay == 1)
|
||||||
//decode and correct tact and compare
|
|
||||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
|
||||||
if (tact_okay != 1)
|
|
||||||
{
|
{
|
||||||
|
//fprintf (stderr, "CACH Okay ");
|
||||||
|
}
|
||||||
|
if (cach_okay != 1)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
|
||||||
if (opts->aggressive_framesync == 1)
|
if (opts->aggressive_framesync == 1)
|
||||||
{
|
{
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->currentslot = tact_bits[1];
|
state->currentslot = cachdata[1];
|
||||||
|
|
||||||
//Setup for first AMBE Frame
|
//Setup for first AMBE Frame
|
||||||
|
|
||||||
@@ -574,14 +751,11 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
sync[i] = (dibit | 1) + 48;
|
sync[i] = (dibit | 1) + 48;
|
||||||
}
|
}
|
||||||
sync[24] = 0;
|
sync[24] = 0;
|
||||||
|
//this method seems to work to make sure we aren't in a data sync especially when inverted signal
|
||||||
|
//consider only testing this when inverted signal if this causes issues with sync later on
|
||||||
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
||||||
{
|
{
|
||||||
if (opts->aggressive_framesync == 1)
|
goto END;
|
||||||
{
|
|
||||||
sync_okay = 0;
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
@@ -621,72 +795,95 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", getTime());
|
||||||
char polarity[3];
|
if (opts->inverted_dmr == 0)
|
||||||
char light[18];
|
|
||||||
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
{
|
||||||
sprintf (light, "%s", " [SLOT1] slot2 ");
|
fprintf (stderr,"Sync: +DMR | Frame Sync | DMRSTEREO | VC1 ");
|
||||||
}
|
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||||
|
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||||
|
(state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sprintf (light, "%s", " slot1 [SLOT2] ");
|
fprintf (stderr,"Sync: -DMR | Frame Sync | DMRSTEREO | VC1 ");
|
||||||
}
|
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||||
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) )
|
||||||
else sprintf (polarity, "%s", "-");
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code);
|
}
|
||||||
|
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||||
|
(state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
dmr_alg_reset (opts, state);
|
state->DMRvcL = 0;
|
||||||
|
state->DMRvcR = 0;
|
||||||
|
|
||||||
//copy ambe_fr frames first, running process mbe will correct them,
|
|
||||||
//but this also leads to issues extracting good le mi values when
|
|
||||||
//we go to do correction on them there too
|
|
||||||
memcpy (m1, ambe_fr, sizeof(m1));
|
|
||||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
|
||||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
|
||||||
|
|
||||||
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
|
||||||
//collect the mi fragment
|
|
||||||
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
|
|
||||||
|
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
|
||||||
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);
|
|
||||||
|
|
||||||
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
|
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
|
||||||
END:
|
END:
|
||||||
//if we have a tact err, then produce sync pattern/err message
|
//if we have a cach err, then produce sync pattern/err message
|
||||||
if (tact_okay != 1 || sync_okay != 1)
|
if (cach_okay != 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", getTime());
|
||||||
fprintf (stderr,"Sync: DMR ");
|
fprintf (stderr,"Sync: DMR ");
|
||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf (stderr, "| VOICE CACH/SYNC ERR");
|
fprintf (stderr, "| ** VOICE CACH ERR ** ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
//run LFSR if either slot had an active MI in it.
|
//run LFSR if either slot had an active MI in it.
|
||||||
if (state->payload_algid >= 0x21)
|
state->currentslot = 0;
|
||||||
{
|
if (state->payload_algid >= 0x21) LFSR(state);
|
||||||
state->currentslot = 0;
|
state->currentslot = 1;
|
||||||
LFSR(state);
|
if (state->payload_algidR >= 0x21) LFSR(state);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
|
||||||
if (state->payload_algidR >= 0x21)
|
|
||||||
{
|
|
||||||
state->currentslot = 1;
|
|
||||||
LFSR(state);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
//failsafe to reset all data header and blocks when bad tact or emb
|
|
||||||
dmr_reset_blocks (opts, state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
862
src/dmr_csbk.c
862
src/dmr_csbk.c
@@ -1,862 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_csbk.c
|
|
||||||
* DMR Control Signal Data PDU (CSBK, MBC, UDT) Handler and Related Functions
|
|
||||||
*
|
|
||||||
* Portions of Connect+ code reworked from Boatbod OP25
|
|
||||||
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
|
||||||
*
|
|
||||||
* Portions of Capacity+ code reworked from Eric Cottrell
|
|
||||||
* Source: https://github.com/LinuxSheeple-E/dsd/blob/Feature/DMRECC/dmr_csbk.c
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
//function for handling Control Signalling PDUs (CSBK, MBC, UDT) messages
|
|
||||||
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
|
||||||
{
|
|
||||||
|
|
||||||
int csbk_lb = 0;
|
|
||||||
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
|
|
||||||
csbk_pf = ( (cs_pdu[1] & 0x80) >> 7);
|
|
||||||
csbk = ((cs_pdu[0] & 0x3F) << 8) | cs_pdu[1]; //opcode and fid combo set
|
|
||||||
|
|
||||||
//set overarching manufacturer in use when non-standard feature id set is up
|
|
||||||
if (csbk_fid != 0) state->dmr_mfid = csbk_fid;
|
|
||||||
|
|
||||||
//TIII standard with fid of 0 - (opcodes in decimal)
|
|
||||||
if (0 == 0) //some non-standard fid values (0x58 - tait, 0x68 - hytera, 0x10-motcapmax) also use a good chunk of these
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
|
|
||||||
//7.1.1.1.1 Channel Grant CSBK/MBC PDU
|
|
||||||
if (csbk_o > 47 && csbk_o < 55 )
|
|
||||||
{
|
|
||||||
|
|
||||||
//maintain this to allow users to hardset the cc freq as map[0]; otherwise, set from rigctl or rtl freq at c_aloha_sys_parms
|
|
||||||
// if (state->p25_cc_freq == 0 && state->trunk_chan_map[0] != 0) state->p25_cc_freq = state->trunk_chan_map[0];
|
|
||||||
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
|
|
||||||
long int freq = 0;
|
|
||||||
|
|
||||||
//all of these messages share the same format (thankfully)
|
|
||||||
if (csbk_o == 48) fprintf (stderr, " Private Voice Channel Grant (PV_GRANT)");
|
|
||||||
if (csbk_o == 49) fprintf (stderr, " Talkgroup Voice Channel Grant (TV_GRANT)");
|
|
||||||
if (csbk_o == 50) fprintf (stderr, " Private Broadcast Voice Channel Grant (BTV_GRANT)");
|
|
||||||
if (csbk_o == 51) fprintf (stderr, " Private Data Channel Grant: Single Item (PD_GRANT)");
|
|
||||||
if (csbk_o == 52) fprintf (stderr, " Talkgroup Data Channel Grant: Single Item (TD_GRANT)");
|
|
||||||
if (csbk_o == 53) fprintf (stderr, " Duplex Private Voice Channel Grant (PV_GRANT_DX)");
|
|
||||||
if (csbk_o == 54) fprintf (stderr, " Duplex Private Data Channel Grant (PD_GRANT_DX)");
|
|
||||||
|
|
||||||
//Logical Physical Channel Number
|
|
||||||
uint16_t lpchannum = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 12);
|
|
||||||
if (lpchannum == 0) fprintf (stderr, " - Invalid Channel"); //invalid channel, not sure why this would even be transmitted
|
|
||||||
else if (lpchannum == 0xFFF) fprintf (stderr, " - Absolute"); //This is from an MBC, signalling an absolute and not a logical
|
|
||||||
else fprintf (stderr, " - Logical");
|
|
||||||
|
|
||||||
//dsdplus channel values
|
|
||||||
uint16_t pluschannum = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 13); //lcn bit included
|
|
||||||
pluschannum += 1; //add a one for good measure
|
|
||||||
|
|
||||||
//LCN conveyed here is the tdma timeslot variety, and not the RF frequency variety
|
|
||||||
uint8_t lcn = cs_pdu_bits[28];
|
|
||||||
//the next three bits can have different meanings depending on which item above for context
|
|
||||||
uint8_t st1 = cs_pdu_bits[29]; //late_entry, hi_rate, reserved(dx)
|
|
||||||
uint8_t st2 = cs_pdu_bits[30]; //emergency
|
|
||||||
uint8_t st3 = cs_pdu_bits[31]; //offset, call direction
|
|
||||||
//target and source are always the same bits
|
|
||||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
|
||||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
//added my best guess as to how dsdplus arrives at a dmr channel value (seems pretty consistent) as C+
|
|
||||||
fprintf (stderr, " Ch [%03X] Cd [%04d] C+ [%04d] - TS [%d] - Target [%08d] - Source [%08d]", lpchannum, lpchannum, pluschannum, lcn, target, source);
|
|
||||||
|
|
||||||
if (st2) fprintf (stderr, " Emergency");
|
|
||||||
|
|
||||||
if (lpchannum == 0xFFF) //This is from an MBC, signalling an absolute and not a logical
|
|
||||||
{
|
|
||||||
|
|
||||||
//7.1.1.1.2 Channel Grant Absolute Parameters CG_AP appended MBC PDU
|
|
||||||
uint8_t mbc_lb = cs_pdu_bits[96]; //
|
|
||||||
uint8_t mbc_pf = cs_pdu_bits[97];
|
|
||||||
uint8_t mbc_csbko = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[98], 6);
|
|
||||||
uint8_t mbc_res = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[104], 4);
|
|
||||||
uint8_t mbc_cc = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[108], 4);
|
|
||||||
uint8_t mbc_cdeftype = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[112], 4); //see 7.2.19.7 = 0 for channel parms, 1 through FFFF reserved
|
|
||||||
uint8_t mbc_res2 = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[116], 2);
|
|
||||||
uint64_t mbc_cdefparms = (uint64_t)ConvertBitIntoBytes(&cs_pdu_bits[118], 58); //see 7.2.19.7.1
|
|
||||||
|
|
||||||
//this is how you read the 58 parm bits according to the appendix 7.2.19.7.1
|
|
||||||
if (mbc_cdeftype == 0) //if 0, then absolute channel parms
|
|
||||||
{
|
|
||||||
uint16_t mbc_lpchannum = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[118], 12);
|
|
||||||
uint16_t mbc_abs_tx_int = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[130], 10);
|
|
||||||
uint16_t mbc_abs_tx_step = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[140], 13);
|
|
||||||
uint16_t mbc_abs_rx_int = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[153], 10);
|
|
||||||
uint16_t mbc_abs_rx_step = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[163], 13);
|
|
||||||
|
|
||||||
//tx_int (Mhz) + (tx_step * 125) = tx_freq
|
|
||||||
//rx_int (Mhz) + (rx_step * 125) = rx_freq
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " ABS-CHAN [%03X][%04d] - RX INT [%d][%X] - RX STEP [%d][%X]", mbc_lpchannum, mbc_lpchannum, mbc_abs_rx_int, mbc_abs_rx_int, mbc_abs_rx_step, mbc_abs_rx_step );
|
|
||||||
//DOULBE CHECK APPENDIX C - C.1.1.2 Fixed Channel Plan for accurate information (found it after figuring out calc, so might be worth double checking)
|
|
||||||
//The Frequency we want to tune is the RX Frequency
|
|
||||||
freq = (mbc_abs_rx_int * 1000000 ) + (mbc_abs_rx_step * 125);
|
|
||||||
//tx_freq = (mbc_abs_tx_int * 1000000 ) + (mbc_abs_tx_step * 125);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//print frequency from absolute
|
|
||||||
if (freq != 0 && lpchannum == 0xFFF) fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
|
||||||
|
|
||||||
//run external channel map function on logical
|
|
||||||
if (lpchannum != 0 && lpchannum != 0xFFF)
|
|
||||||
{
|
|
||||||
freq = state->trunk_chan_map[lpchannum];
|
|
||||||
(stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Skip tuning private calls if private calls are disabled
|
|
||||||
if (opts->trunk_tune_private_calls == 0 && csbk_o != 49) goto SKIPCALL;
|
|
||||||
|
|
||||||
//Allow tuning of data calls if user wishes by flipping the csbk_o to a group voice call
|
|
||||||
if (csbk_o == 51 || csbk_o == 52 || csbk_o == 54)
|
|
||||||
{
|
|
||||||
if (opts->trunk_tune_data_calls == 1) csbk_o = 49;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if not a data channel grant (only tuning to voice channel grants)
|
|
||||||
if (csbk_o == 48 || csbk_o == 49 || csbk_o == 50 || csbk_o == 53) //48, 49, 50 are voice grants, 51 and 52 are data grants, 53 Duplex Private Voice, 54 Duplex Private Data
|
|
||||||
{
|
|
||||||
//shim in here for ncurses freq display when not trunking (playback, not live)
|
|
||||||
if (opts->p25_trunk == 0 && freq != 0)
|
|
||||||
{
|
|
||||||
//just set to both for now, could go on tslot later
|
|
||||||
state->p25_vc_freq[0] = freq;
|
|
||||||
state->p25_vc_freq[1] = freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
//don't tune if currently a vc on the control channel
|
|
||||||
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
|
||||||
{
|
|
||||||
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 == target)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0))
|
|
||||||
{
|
|
||||||
if (freq != 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, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, freq);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SKIPCALL: ; //do nothing
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Move
|
|
||||||
if (csbk_o == 57)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Move (C_MOVE) (TODO)");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Aloha
|
|
||||||
if (csbk_o == 25)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
uint8_t reserved = cs_pdu_bits[16];
|
|
||||||
uint8_t tsccas = cs_pdu_bits[17];
|
|
||||||
uint8_t sync = cs_pdu_bits[18];
|
|
||||||
uint8_t version = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[19], 3); //Document Version Control
|
|
||||||
uint8_t offset = cs_pdu_bits[22]; //0-TSCC uses aligned timing; 1-TSCC uses offset timing
|
|
||||||
uint8_t active = cs_pdu_bits[23]; //Active_Connection
|
|
||||||
uint8_t mask = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24], 5);
|
|
||||||
uint8_t sf = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[29], 2); //service function
|
|
||||||
uint8_t nrandwait = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[31], 4);
|
|
||||||
uint8_t regreq = cs_pdu_bits[35];
|
|
||||||
uint8_t backoff = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[36], 4);
|
|
||||||
|
|
||||||
uint8_t model = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 2);
|
|
||||||
uint16_t net = 0;
|
|
||||||
uint16_t site = 0;
|
|
||||||
|
|
||||||
//DMR Location Area - DMRLA
|
|
||||||
uint16_t sys_area = 0;
|
|
||||||
uint16_t sub_area = 0;
|
|
||||||
uint16_t sub_mask = 0x1;
|
|
||||||
//tiny n 1-3; small 1-5; large 1-8; huge 1-10
|
|
||||||
uint16_t n = 1; //The minimum value of DMRLA is normally ≥ 1, 0 is reserved
|
|
||||||
|
|
||||||
|
|
||||||
char model_str[8];
|
|
||||||
char par_str[8]; //category A, B, AB, or reserved
|
|
||||||
|
|
||||||
sprintf (model_str, "%s", " ");
|
|
||||||
sprintf (par_str, "%s", "Res");
|
|
||||||
|
|
||||||
if (model == 0) //Tiny
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 9);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[51], 3);
|
|
||||||
sprintf (model_str, "%s", "Tiny");
|
|
||||||
n = 3;
|
|
||||||
}
|
|
||||||
else if (model == 1) //Small
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 7);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[49], 5);
|
|
||||||
sprintf (model_str, "%s", "Small");
|
|
||||||
n = 5;
|
|
||||||
}
|
|
||||||
else if (model == 2) //Large
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 4);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[46], 8);
|
|
||||||
sprintf (model_str, "%s", "Large");
|
|
||||||
n = 8;
|
|
||||||
}
|
|
||||||
else if (model == 3) //Huge
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 2);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[44], 10);
|
|
||||||
sprintf (model_str, "%s", "Huge");
|
|
||||||
n = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
//honestly can't say that this is accurate, just a guess
|
|
||||||
uint8_t is_capmax = 0; //capmax(mot) flag
|
|
||||||
if (csbk_fid == 0x10)
|
|
||||||
{
|
|
||||||
n = 0;
|
|
||||||
is_capmax = 1;
|
|
||||||
opts->dmr_dmrla_is_set = 1;
|
|
||||||
opts->dmr_dmrla_n = 0;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf (state->dmr_branding_sub, "%s", "CapMax ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->dmr_dmrla_is_set == 1) n = opts->dmr_dmrla_n;
|
|
||||||
|
|
||||||
if (n == 0) sub_mask = 0x0;
|
|
||||||
if (n == 1) sub_mask = 0x1;
|
|
||||||
if (n == 2) sub_mask = 0x3;
|
|
||||||
if (n == 3) sub_mask = 0x7;
|
|
||||||
if (n == 4) sub_mask = 0xF;
|
|
||||||
if (n == 5) sub_mask = 0x1F;
|
|
||||||
if (n == 6) sub_mask = 0x3F;
|
|
||||||
if (n == 7) sub_mask = 0x7F;
|
|
||||||
if (n == 8) sub_mask = 0xFF;
|
|
||||||
if (n == 9) sub_mask = 0x1FF;
|
|
||||||
if (n == 10) sub_mask = 0x3FF;
|
|
||||||
|
|
||||||
uint8_t par = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[54], 2);
|
|
||||||
if (par == 1) sprintf (par_str, "%s", "A ");
|
|
||||||
if (par == 2) sprintf (par_str, "%s", "B ");
|
|
||||||
if (par == 3) sprintf (par_str, "%s", "AB");
|
|
||||||
|
|
||||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
|
||||||
|
|
||||||
if (n != 0) fprintf (stderr, " C_ALOHA_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d Cat: %s\n Reg Req: %d V: %d Mask: %02X MS: %d", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, par_str, regreq, version, mask, target);
|
|
||||||
else fprintf (stderr, " C_ALOHA_SYS_PARMS - %s - Net ID: %d Site ID: %d Cat: %s\n Reg Req: %d V: %d Mask: %02X MS: %d", model_str, net, site, par_str, regreq, version, mask, target);
|
|
||||||
if (is_capmax) fprintf (stderr, " Capacity Max ");
|
|
||||||
|
|
||||||
//add string for ncurses terminal display
|
|
||||||
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
|
||||||
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
|
||||||
|
|
||||||
//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
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
ccfreq = (long int)opts->rtlsdr_center_freq;
|
|
||||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
|
||||||
}
|
|
||||||
|
|
||||||
//nullify any previous branding sub (bugfix for bad assignments or system type switching)
|
|
||||||
//sprintf(state->dmr_branding_sub, "%s", "");
|
|
||||||
|
|
||||||
//debug print
|
|
||||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16);
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %016b", syscode);
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %02b.%b.%b", model, net, site); //won't show leading zeroes, but may not be required
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %04X", syscode);
|
|
||||||
}
|
|
||||||
|
|
||||||
//P_CLEAR
|
|
||||||
if (csbk_o == 46)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Clear (P_CLEAR) (TODO)");
|
|
||||||
}
|
|
||||||
|
|
||||||
//(P_PROTECT) - Could be a useful way of determining who is on the current timeslot p_kind 2
|
|
||||||
if (csbk_o == 47)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Protect (P_PROTECT) -");
|
|
||||||
uint16_t reserved = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 12);
|
|
||||||
uint8_t p_kind = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[28], 3);
|
|
||||||
uint8_t gi = cs_pdu_bits[31];
|
|
||||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
|
||||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
|
||||||
|
|
||||||
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 == 3) fprintf (stderr, " Enable Target MS PTT (EN_PTT_ONE_MS)");
|
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Target [%08d] - Source [%08d]", target, source);
|
|
||||||
|
|
||||||
//Illegally Parked is another way to say a private call is outbound on this channel
|
|
||||||
//so, we can use this as a form of 'link control'
|
|
||||||
if (p_kind == 2)
|
|
||||||
{
|
|
||||||
//don't set this if operating out of dmr mono or using call alert beep
|
|
||||||
if (opts->dmr_mono == 0 && opts->call_alert == 0)
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->lasttg = target;
|
|
||||||
state->lastsrc = source;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->lasttgR = target;
|
|
||||||
state->lastsrcR = source;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk_o == 40) //0x28 //check to see if these are fid 0 only, or also in other manufacturer fids
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Announcements (C_BCAST)"); //site it, vote for etc etc
|
|
||||||
uint8_t a_type = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 5);
|
|
||||||
if (a_type == 0) fprintf (stderr, " Announce/Withdraw TSCC (Ann_WD_TSCC)");
|
|
||||||
if (a_type == 1) fprintf (stderr, " Specify Call Timer Parameters (CallTimer_Parms)");
|
|
||||||
if (a_type == 2) fprintf (stderr, " Vote Now Advice (Vote_Now)");
|
|
||||||
if (a_type == 3) fprintf (stderr, " Broadcast Local Time (Local_Time)");
|
|
||||||
if (a_type == 4) fprintf (stderr, " Mass Registration (MassReg)");
|
|
||||||
if (a_type == 5) fprintf (stderr, " Announce Logical Channel/Frequency Relationship (Chan_Freq)");
|
|
||||||
if (a_type == 6) fprintf (stderr, " Adjacent Site Information (Adjacent_Site)");
|
|
||||||
if (a_type == 7) fprintf (stderr, " General Site Parameters (Gen_Site_Params)");
|
|
||||||
if (a_type > 7 && a_type < 0x1E) fprintf (stderr, " Reserved %02X", a_type);
|
|
||||||
if (a_type == 0x1E || a_type == 0x1F) fprintf (stderr, " Manufacturer Specific %02X", a_type);
|
|
||||||
|
|
||||||
//sysidcode, probably would have been easier to make this its own external function
|
|
||||||
uint8_t model = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 2);
|
|
||||||
uint16_t net = 0;
|
|
||||||
uint16_t site = 0;
|
|
||||||
|
|
||||||
//DMR Location Area - DMRLA
|
|
||||||
uint16_t sys_area = 0;
|
|
||||||
uint16_t sub_area = 0;
|
|
||||||
uint16_t sub_mask = 0x1;
|
|
||||||
//tiny n 1-3; small 1-5; large 1-8; huge 1-10
|
|
||||||
uint16_t n = 1; //The minimum value of DMRLA is normally ≥ 1, 0 is reserved
|
|
||||||
|
|
||||||
//channel number from Broadcast Parms 2
|
|
||||||
uint16_t lpchannum = 0;
|
|
||||||
|
|
||||||
char model_str[8];
|
|
||||||
char par_str[8]; //category A, B, AB, or reserved
|
|
||||||
|
|
||||||
sprintf (model_str, "%s", " ");
|
|
||||||
sprintf (par_str, "%s", "Res");
|
|
||||||
|
|
||||||
if (model == 0) //Tiny
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 9);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[51], 3);
|
|
||||||
sprintf (model_str, "%s", "Tiny");
|
|
||||||
n = 3;
|
|
||||||
}
|
|
||||||
else if (model == 1) //Small
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 7);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[49], 5);
|
|
||||||
sprintf (model_str, "%s", "Small");
|
|
||||||
n = 5;
|
|
||||||
}
|
|
||||||
else if (model == 2) //Large
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 4);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[46], 8);
|
|
||||||
sprintf (model_str, "%s", "Large");
|
|
||||||
n = 8;
|
|
||||||
}
|
|
||||||
else if (model == 3) //Huge
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 2);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[44], 10);
|
|
||||||
sprintf (model_str, "%s", "Huge");
|
|
||||||
n = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
//honestly can't say that this is accurate, just a guess
|
|
||||||
uint8_t is_capmax = 0; //capmax(mot) flag
|
|
||||||
if (csbk_fid == 0x10)
|
|
||||||
{
|
|
||||||
n = 0;
|
|
||||||
is_capmax = 1;
|
|
||||||
opts->dmr_dmrla_is_set = 1;
|
|
||||||
opts->dmr_dmrla_n = 0;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf (state->dmr_branding_sub, "%s", "CapMax ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->dmr_dmrla_is_set == 1) n = opts->dmr_dmrla_n;
|
|
||||||
|
|
||||||
if (n == 0) sub_mask = 0x0;
|
|
||||||
if (n == 1) sub_mask = 0x1;
|
|
||||||
if (n == 2) sub_mask = 0x3;
|
|
||||||
if (n == 3) sub_mask = 0x7;
|
|
||||||
if (n == 4) sub_mask = 0xF;
|
|
||||||
if (n == 5) sub_mask = 0x1F;
|
|
||||||
if (n == 6) sub_mask = 0x3F;
|
|
||||||
if (n == 7) sub_mask = 0x7F;
|
|
||||||
if (n == 8) sub_mask = 0xFF;
|
|
||||||
if (n == 9) sub_mask = 0x1FF;
|
|
||||||
if (n == 10) sub_mask = 0x3FF;
|
|
||||||
|
|
||||||
uint8_t par = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[54], 2);
|
|
||||||
if (par == 1) sprintf (par_str, "%s", "A ");
|
|
||||||
if (par == 2) sprintf (par_str, "%s", "B ");
|
|
||||||
if (par == 3) sprintf (par_str, "%s", "AB");
|
|
||||||
|
|
||||||
uint32_t parms2 = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
|
||||||
lpchannum = parms2 & 0xFFF; //7.2.19.7, for DSDPlus, this value is (lpchannum << 1) + 1;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
if (n != 0) fprintf (stderr, " C_BCAST_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d Cat: %s Cd: %d C+: %d", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, par_str, lpchannum, (lpchannum << 1)+1 );
|
|
||||||
else fprintf (stderr, " C_BCAST_SYS_PARMS - %s - Net ID: %d Site ID: %d Cat: %s Cd: %d C+: %d", model_str, net, site, par_str, lpchannum, (lpchannum << 1)+1 );
|
|
||||||
if (is_capmax) fprintf (stderr, " Capacity Max");
|
|
||||||
|
|
||||||
//add string for ncurses terminal display, if not adjacent site info
|
|
||||||
if (a_type != 6 && n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1);
|
|
||||||
if (a_type != 6 && n == 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
|
||||||
|
|
||||||
//nullify any previous branding sub (bugfix for bad assignments or system type switching)
|
|
||||||
//sprintf(state->dmr_branding_sub, "%s", "");
|
|
||||||
|
|
||||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16);
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %016b", syscode);
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %04X", syscode);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk == 28)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " C_AHOY (TODO)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk == 42)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " P_MAINT (TODO)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk == 30)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " C_ACKVIT (TODO)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk == 31)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " C_RAND (TODO)");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Capacity+ Section
|
|
||||||
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 slot = cs_pdu_bits[18];
|
|
||||||
uint8_t rest_channel = cs_pdu[2] & 0xF; //0xF, or 0x1F?
|
|
||||||
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 - Rest Channel %d", 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);
|
|
||||||
fprintf (stderr, " %03d ", tg);
|
|
||||||
//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);
|
|
||||||
|
|
||||||
//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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Connect+ Section
|
|
||||||
if (csbk_fid == 0x06)
|
|
||||||
{
|
|
||||||
|
|
||||||
//maintain this to allow users to hardset the cc freq as map[0]; otherwise, set from rigctl or rtl freq
|
|
||||||
// if (state->p25_cc_freq == 0 && state->trunk_chan_map[0] != 0) state->p25_cc_freq = state->trunk_chan_map[0];
|
|
||||||
|
|
||||||
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;
|
|
||||||
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);
|
|
||||||
state->dmr_mfid = 0x06;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk_o == 0x03)
|
|
||||||
{
|
|
||||||
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
uint32_t srcAddr = ( (cs_pdu[2] << 16) + (cs_pdu[3] << 8) + cs_pdu[4] );
|
|
||||||
uint32_t grpAddr = ( (cs_pdu[5] << 16) + (cs_pdu[6] << 8) + cs_pdu[7] );
|
|
||||||
uint8_t lcn = ( (cs_pdu[8] & 0xF0 ) >> 4 );
|
|
||||||
uint8_t tslot = ( (cs_pdu[8] & 0x08 ) >> 3 );
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Connect Plus Voice Channel Grant\n");
|
|
||||||
fprintf (stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot);
|
|
||||||
state->dmr_mfid = 0x06;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
|
||||||
|
|
||||||
//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
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
ccfreq = (long int)opts->rtlsdr_center_freq;
|
|
||||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
|
||||||
}
|
|
||||||
|
|
||||||
//shim in here for ncurses freq display when not trunking (playback, not live)
|
|
||||||
if (opts->p25_trunk == 0 && state->trunk_chan_map[lcn] != 0)
|
|
||||||
{
|
|
||||||
//just set to both for now, could go on tslot later
|
|
||||||
state->p25_vc_freq[0] = state->trunk_chan_map[lcn];
|
|
||||||
state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
|
|
||||||
}
|
|
||||||
|
|
||||||
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 == grpAddr)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//don't tune if currently a vc on the control channel
|
|
||||||
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
|
||||||
{
|
|
||||||
|
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0) )
|
|
||||||
{
|
|
||||||
if (state->trunk_chan_map[lcn] != 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[lcn]);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, state->trunk_chan_map[lcn]);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk_o == 0x06)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
uint32_t srcAddr = ( (cs_pdu[2] << 16) + (cs_pdu[3] << 8) + cs_pdu[4] );
|
|
||||||
uint32_t grpAddr = ( (cs_pdu[5] << 16) + (cs_pdu[6] << 8) + cs_pdu[7] );
|
|
||||||
uint8_t lcn = ( (cs_pdu[8] & 0xF0 ) >> 4 ) ;
|
|
||||||
uint8_t tslot = ( (cs_pdu[8] & 0x08 ) >> 3 );
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Connect Plus Data Channel Grant\n");
|
|
||||||
fprintf (stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot);
|
|
||||||
state->dmr_mfid = 0x06;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk_o == 0x0C)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
uint32_t srcAddr = ( (cs_pdu[2] << 16) + (cs_pdu[3] << 8) + cs_pdu[4] );
|
|
||||||
uint32_t grpAddr = ( (cs_pdu[5] << 16) + (cs_pdu[6] << 8) + cs_pdu[7] );
|
|
||||||
uint8_t lcn = ( (cs_pdu[8] & 0xF0 ) >> 4 );
|
|
||||||
uint8_t tslot = ( (cs_pdu[8] & 0x08 ) >> 3 );
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Connect Plus Terminate Channel Grant\n");
|
|
||||||
fprintf (stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot);
|
|
||||||
state->dmr_mfid = 0x06;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk_o == 0x11)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Connect Plus Registration Request");
|
|
||||||
state->dmr_mfid = 0x06;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk_o == 0x12)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Connect Plus Registration Response");
|
|
||||||
state->dmr_mfid = 0x06;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csbk_o == 0x18)
|
|
||||||
{
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Connect Plus Talkgroup Affiliation");
|
|
||||||
state->dmr_mfid = 0x06;
|
|
||||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
END:
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
}
|
|
||||||
422
src/dmr_data.c
422
src/dmr_data.c
@@ -18,32 +18,35 @@
|
|||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
processDMRdata (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i, dibit;
|
int i, dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
char sync[25];
|
char sync[25];
|
||||||
char syncdata[48];
|
char syncdata[48];
|
||||||
char cachdata[25];
|
char cachdata[25] = {0};
|
||||||
|
char cc[5] = {0};
|
||||||
|
char ccAscii[5] = {0};
|
||||||
char bursttype[5];
|
char bursttype[5];
|
||||||
uint8_t burst;
|
unsigned int burst;
|
||||||
char info[196];
|
char info[196];
|
||||||
|
|
||||||
unsigned char SlotType[20];
|
unsigned char SlotType[20];
|
||||||
unsigned int SlotTypeOk;
|
unsigned int SlotTypeOk;
|
||||||
uint8_t cach_err = 0;
|
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
|
||||||
|
|
||||||
int cachInterleave[24] =
|
#ifdef DMR_DUMP
|
||||||
{0, 7, 8, 9, 1, 10,
|
char syncbits[49];
|
||||||
11, 12, 2, 13, 14,
|
char cachbits[25];
|
||||||
15, 3, 16, 4, 17, 18,
|
int k;
|
||||||
19, 5, 20, 21, 22, 6, 23
|
#endif
|
||||||
};
|
|
||||||
|
ccAscii[4] = 0;
|
||||||
|
bursttype[4] = 0;
|
||||||
|
|
||||||
dibit_p = state->dmr_payload_p - 90;
|
dibit_p = state->dmr_payload_p - 90;
|
||||||
|
// CACH
|
||||||
//collect cach and de-interleave
|
|
||||||
for (i = 0; i < 12; i++)
|
for (i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
@@ -62,34 +65,67 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//seperate tact bits from cach
|
//decode and correct cach and compare
|
||||||
uint8_t tact_bits[7];
|
int cach_okay = 69;
|
||||||
for (i = 0; i < 7; i++)
|
if ( Hamming_7_4_decode (cachdata) ) //is now de-interleaved appropriately
|
||||||
{
|
{
|
||||||
tact_bits[i] = cachdata[i];
|
cach_okay = 1;
|
||||||
}
|
}
|
||||||
|
if (cach_okay == 1)
|
||||||
//run hamming on tact bits
|
|
||||||
int cach_okay = -1;
|
|
||||||
if ( Hamming_7_4_decode (tact_bits) ) cach_okay = 1;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
cach_okay = -1;
|
//fprintf (stderr, "CACH Okay ");
|
||||||
if (opts->aggressive_framesync == 1)
|
}
|
||||||
|
if (cach_okay != 1)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
|
||||||
|
if (opts->aggressive_framesync == 1) //may not worry about it on data
|
||||||
{
|
{
|
||||||
SlotTypeOk = 0;
|
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->currentslot = tact_bits[1];
|
state->currentslot = cachdata[1];
|
||||||
|
|
||||||
//in the future, maybe we will remove the hard set on this
|
//minor bugfix for MSData when MSData has accumulated data blocks
|
||||||
|
//(i.e. data header and 1/2 rate data), force all data into slot 0
|
||||||
if (state->dmr_ms_mode == 1)
|
if (state->dmr_ms_mode == 1)
|
||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 0 && state->dmr_ms_mode == 0)
|
||||||
|
{
|
||||||
|
state->slot1light[0] = '[';
|
||||||
|
state->slot1light[6] = ']';
|
||||||
|
state->slot2light[0] = ' ';
|
||||||
|
state->slot2light[6] = ' ';
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
if (state->currentslot == 1 && state->dmr_ms_mode == 0)
|
||||||
|
{
|
||||||
|
state->slot2light[0] = '[';
|
||||||
|
state->slot2light[6] = ']';
|
||||||
|
state->slot1light[0] = ' ';
|
||||||
|
state->slot1light[6] = ' ';
|
||||||
|
}
|
||||||
|
//end correct cach and set slot
|
||||||
|
|
||||||
|
#ifdef DMR_DUMP
|
||||||
|
k = 0;
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
dibit = cachdata[i];
|
||||||
|
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||||
|
k++;
|
||||||
|
cachbits[k] = (1 & dibit) + 48; // bit 0
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
cachbits[24] = 0;
|
||||||
|
fprintf(stderr, "%s ", cachbits);
|
||||||
|
#endif
|
||||||
|
//trellis bits
|
||||||
|
unsigned char trellisdibits[98];
|
||||||
// Current slot - First half - Data Payload - 1st part
|
// Current slot - First half - Data Payload - 1st part
|
||||||
for (i = 0; i < 49; i++)
|
for (i = 0; i < 49; i++)
|
||||||
{
|
{
|
||||||
@@ -103,6 +139,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+12];
|
dibit = (int)state->dmr_stereo_payload[i+12];
|
||||||
}
|
}
|
||||||
|
trellisdibits[i] = dibit;
|
||||||
info[2*i] = (1 & (dibit >> 1)); // bit 1
|
info[2*i] = (1 & (dibit >> 1)); // bit 1
|
||||||
info[(2*i)+1] = (1 & dibit); // bit 0
|
info[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
@@ -116,9 +153,10 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[61];
|
dibit = (int)state->dmr_stereo_payload[61]; //61, not i+61
|
||||||
}
|
}
|
||||||
|
cc[0] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
cc[1] = (1 & dibit); // bit 0
|
||||||
SlotType[0] = (1 & (dibit >> 1)); // bit 1
|
SlotType[0] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[1] = (1 & dibit); // bit 0
|
SlotType[1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -132,10 +170,17 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[62];
|
dibit = (int)state->dmr_stereo_payload[62];
|
||||||
}
|
}
|
||||||
|
cc[2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
cc[3] = (1 & dibit); // bit 0
|
||||||
SlotType[2] = (1 & (dibit >> 1)); // bit 1
|
SlotType[2] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[3] = (1 & dibit); // bit 0
|
SlotType[3] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
ccAscii[0] = cc[0] + '0';
|
||||||
|
ccAscii[1] = cc[1] + '0';
|
||||||
|
ccAscii[2] = cc[2] + '0';
|
||||||
|
ccAscii[3] = cc[3] + '0';
|
||||||
|
ccAscii[4] = cc[4] + '\0';
|
||||||
|
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
dibit_p++;
|
dibit_p++;
|
||||||
if (opts->inverted_dmr == 1)
|
if (opts->inverted_dmr == 1)
|
||||||
@@ -144,7 +189,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1) //state
|
if (state->dmr_stereo == 1) //state
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[63];
|
dibit = (int)state->dmr_stereo_payload[63]; //(int)
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotType[4] = (1 & (dibit >> 1)); // bit 1
|
SlotType[4] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -158,7 +203,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1) //state
|
if (state->dmr_stereo == 1) //state
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[64];
|
dibit = (int)state->dmr_stereo_payload[64]; //(int)
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotType[6] = (1 & (dibit >> 1)); // bit 1
|
SlotType[6] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -178,7 +223,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
SlotType[8] = (1 & (dibit >> 1)); // bit 1
|
SlotType[8] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[9] = (1 & dibit); // bit 0
|
SlotType[9] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
// signaling data or sync
|
// signalling data or sync
|
||||||
for (i = 0; i < 24; i++)
|
for (i = 0; i < 24; i++)
|
||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
@@ -194,19 +239,32 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
syncdata[2*i] = (1 & (dibit >> 1)); // bit 1
|
syncdata[2*i] = (1 & (dibit >> 1)); // bit 1
|
||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
sync[i] = (dibit | 1) + 48;
|
sync[i] = (dibit | 1) + 48;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
sync[24] = 0;
|
sync[24] = 0;
|
||||||
|
|
||||||
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )
|
#ifdef DMR_DUMP
|
||||||
|
k = 0;
|
||||||
|
for (i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
syncbits[k] = syncdata[(k*2)] + 48; // bit 1
|
||||||
|
k++;
|
||||||
|
syncbits[k] = syncdata[(k*2)+1] + 48; // bit 0
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
syncbits[48] = 0;
|
||||||
|
fprintf(stderr, "%s ", syncbits);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )//|| (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
||||||
{
|
{
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
sprintf(state->slot1light, "[slot1]");
|
sprintf(state->slot1light, "[slot1]");
|
||||||
sprintf(state->slot2light, " slot2 ");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sprintf(state->slot1light, " slot1 ");
|
|
||||||
sprintf(state->slot2light, "[slot2]");
|
sprintf(state->slot2light, "[slot2]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,17 +273,15 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
sprintf(state->slot1light, "[sLoT1]");
|
sprintf(state->slot1light, "[sLoT1]");
|
||||||
sprintf(state->slot2light, "[DMODE]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
||||||
{
|
{
|
||||||
state->currentslot = 1;
|
state->currentslot = 1;
|
||||||
sprintf(state->slot1light, "[DMODE]");
|
|
||||||
sprintf(state->slot2light, "[sLoT2]");
|
sprintf(state->slot2light, "[sLoT2]");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->dmr_ms_mode == 0)
|
if (opts->errorbars == 1 && state->dmr_ms_mode == 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
||||||
}
|
}
|
||||||
@@ -233,113 +289,283 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
// Slot type - Second part - Parity bit
|
// Slot type - Second part - Parity bit
|
||||||
for (i = 0; i < 5; i++)
|
for (i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
if (state->dmr_stereo == 0)
|
if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
}
|
}
|
||||||
|
if (opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
//dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+90];
|
dibit = (int)state->dmr_stereo_payload[i+90]; //double checked these i+ values make sure we are on the right ones
|
||||||
}
|
}
|
||||||
SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1
|
SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[(i*2) + 11] = (1 & dibit); // bit 0
|
SlotType[(i*2) + 11] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
|
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
|
||||||
|
|
||||||
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
|
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
|
||||||
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
|
if( Golay_20_8_decode(SlotType) )
|
||||||
|
{
|
||||||
|
SlotTypeOk = 1;
|
||||||
|
//fprintf (stderr, "Slot Okay ");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SlotTypeOk = -1;
|
|
||||||
if (opts->aggressive_framesync == 1)
|
if (opts->aggressive_framesync == 1)
|
||||||
{
|
{
|
||||||
SlotTypeOk = 0;
|
SlotTypeOk = 0;
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Slot Type parity checked => Fill the color code */
|
||||||
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
||||||
state->color_code_ok = SlotTypeOk;
|
state->color_code_ok = SlotTypeOk;
|
||||||
|
|
||||||
//not sure why I still have two variables for this, need to look and see what state->color_code still ties into
|
/* Reconstitute the burst type */
|
||||||
if (SlotTypeOk == 1) state->dmr_color_code = state->color_code;
|
//consider assigning this only when slottypeok or similar check passes, eliminate bad burst types from decoding randomly
|
||||||
|
burst = (unsigned int)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
state->dmrburstL = burst;
|
||||||
|
}
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
state->dmrburstR = burst;
|
||||||
|
}
|
||||||
|
|
||||||
/* Reconstitute the burst type */
|
/* Reconstitute the burst type */
|
||||||
burst = (uint8_t)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
|
bursttype[0] = SlotType[4] + '0';
|
||||||
if (state->currentslot == 0) state->dmrburstL = burst;
|
bursttype[1] = SlotType[5] + '0';
|
||||||
if (state->currentslot == 1) state->dmrburstR = burst;
|
bursttype[2] = SlotType[6] + '0';
|
||||||
|
bursttype[3] = SlotType[7] + '0';
|
||||||
|
bursttype[4] = '\0';
|
||||||
|
|
||||||
|
if (strcmp (bursttype, "0000") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " PI Header ");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "0001") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " VOICE LC ");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "0010") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " TLC ");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "0011") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " CSBK ");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "0100") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " MBC Header ");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "0101") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " MBC ");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "0110") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " DATA Header ");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "0111") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " RATE 1/2 DATA");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "1000") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " RATE 3/4 DATA");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "1001") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " Slot idle ");
|
||||||
|
}
|
||||||
|
else if (strcmp (bursttype, "1010") == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " Rate 1 DATA ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(state->fsubtype, " ");
|
||||||
|
}
|
||||||
|
|
||||||
// Current slot - Second Half - Data Payload - 2nd part
|
// Current slot - First half - Data Payload - 1st part
|
||||||
|
//trellis
|
||||||
for (i = 0; i < 49; i++)
|
for (i = 0; i < 49; i++)
|
||||||
{
|
{
|
||||||
if (state->dmr_stereo == 0)
|
if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
}
|
}
|
||||||
|
if (opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
//dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+95];
|
dibit = (int)state->dmr_stereo_payload[i+95]; //double checked these i+ values make sure we are on the right ones
|
||||||
}
|
}
|
||||||
|
trellisdibits[i+49] = dibit;
|
||||||
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
|
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
|
||||||
info[(2*i) + 99] = (1 & dibit); // bit 0
|
info[(2*i) + 99] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
|
|
||||||
|
|
||||||
//don't run cach on simplex or mono
|
|
||||||
if (state->dmr_ms_mode == 0 || opts->dmr_mono == 0)
|
|
||||||
{
|
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
//ending line break
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
|
|
||||||
END:
|
|
||||||
if (SlotTypeOk == 0 || cach_okay != 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, "| CACH/Burst FEC ERR");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
dmr_reset_blocks (opts, state); //failsafe to reset all data header and blocks when bad tact or slottype
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit)
|
// Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit)
|
||||||
if (state->dmr_stereo == 0)
|
if (state->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
skipDibit (opts, state, 12 + 49 + 5);
|
skipDibit (opts, state, 12 + 49 + 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if using con+ trunking and last voice observed more than x seconds ago, snap back to con+ cc
|
if (opts->errorbars == 1)
|
||||||
//con+ voice channels can have extremely long idle periods without properly tearing down
|
|
||||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1)
|
|
||||||
{
|
{
|
||||||
//at 3 seconds, the CC was sending back to the VC even without voice present, increased to 5
|
/* Print the color code */
|
||||||
if ( (time(NULL) - state->last_vc_sync_time > 5) )
|
//if (burst == 0b1001 && state->color_code_ok) //only print on idle for data types, otherwise prints on voice frames.
|
||||||
|
if (SlotTypeOk == 1)
|
||||||
{
|
{
|
||||||
if (opts->use_rigctl == 1) //rigctl tuning
|
fprintf (stderr, "%s", KCYN);
|
||||||
{
|
fprintf(stderr, "| Color Code=%02d ", (int)state->color_code);
|
||||||
if (opts->setmod_bw != 0) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
fprintf (stderr, "%s", KNRM);
|
||||||
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
//state->dmr_color_code = state->color_code;
|
||||||
}
|
}
|
||||||
|
else fprintf(stderr, "| ");
|
||||||
|
|
||||||
else if (opts->audio_in_type == 3) //rtl_fm tuning
|
//if(state->color_code_ok) fprintf(stderr, "(OK) |");
|
||||||
{
|
if(state->color_code_ok) fprintf(stderr, "| (CRC OK ) |"); //add line break
|
||||||
//UDP command to tune the RTL dongle
|
else
|
||||||
rtl_udp_tune(opts, state, state->p25_cc_freq);
|
{
|
||||||
}
|
fprintf (stderr, "%s", KRED);
|
||||||
opts->p25_is_tuned = 0;
|
fprintf(stderr, "| (CRC ERR) |");
|
||||||
//zero out vc frequencies
|
fprintf (stderr, "%s", KNRM);
|
||||||
state->p25_vc_freq[0] = 0;
|
}
|
||||||
state->p25_vc_freq[1] = 0;
|
//fprintf (stderr, "\n"); //print line break
|
||||||
state->last_cc_sync_time = time(NULL);
|
if (strcmp (state->fsubtype, " ") == 0)
|
||||||
state->is_con_plus = 0; //con+ flag off
|
{
|
||||||
|
//fprintf(stderr, " Unknown burst type: %s", bursttype);
|
||||||
|
fprintf(stderr, " Unknown");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s", state->fsubtype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
switch(burst)
|
||||||
|
{
|
||||||
|
/* Burst = PI header */
|
||||||
|
case 0b0000:
|
||||||
|
{
|
||||||
|
ProcessDmrPIHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = Voice LC header */
|
||||||
|
case 0b0001:
|
||||||
|
{
|
||||||
|
/* Extract data from Voice LC Header */
|
||||||
|
ProcessDmrVoiceLcHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = TLC */
|
||||||
|
case 0b0010:
|
||||||
|
{
|
||||||
|
//Not sure if I really care about this in the context of what it shows
|
||||||
|
ProcessDmrTerminaisonLC(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = CSBK */
|
||||||
|
case 0b0011:
|
||||||
|
{
|
||||||
|
//going to disable until I can find more useful info to present
|
||||||
|
ProcessCSBK(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = MBC Header */
|
||||||
|
case 0b0100:
|
||||||
|
{
|
||||||
|
ProcessMBChData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = MBC Continuation*/
|
||||||
|
case 0b0101:
|
||||||
|
{
|
||||||
|
ProcessMBCData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = DATA Header */
|
||||||
|
case 0b0110:
|
||||||
|
{
|
||||||
|
ProcessDataData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = RATE 1/2 DATA */
|
||||||
|
case 0b0111:
|
||||||
|
{
|
||||||
|
Process12Data(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = RATE 3/4 DATA */
|
||||||
|
case 0b1000:
|
||||||
|
{
|
||||||
|
//need to keep working on improving the trellis decode, but is partially viable now
|
||||||
|
Process34Data(opts, state, trellisdibits, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = Slot idle */
|
||||||
|
case 0b1001:
|
||||||
|
{
|
||||||
|
if(state->color_code_ok && state->dmr_stereo == 0) state->dmr_color_code = state->color_code; //try setting this on idle if crc ok
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = Rate 1 DATA */
|
||||||
|
case 0b1010:
|
||||||
|
{
|
||||||
|
Process1Data(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burst = Unified Single Block DATA */
|
||||||
|
case 0b1101:
|
||||||
|
{
|
||||||
|
ProcessUnifiedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default */
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
ProcessReservedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} /* End switch(burst) */
|
||||||
|
|
||||||
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
END:
|
||||||
|
if (SlotTypeOk == 0 || cach_okay != 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "| ** CACH or Burst Type FEC ERR ** ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* End processDMRdata() */
|
||||||
|
|||||||
447
src/dmr_dburst.c
447
src/dmr_dburst.c
@@ -1,447 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_dburst.c
|
|
||||||
* DMR Data Burst Handling and related BPTC/FEC/CRC Functions
|
|
||||||
*
|
|
||||||
* Portions of BPTC/FEC/CRC code from LouisErigHerve
|
|
||||||
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
//TODO: CRC9 on confirmed data blocks; CRC32 on completed data sf;
|
|
||||||
//TODO: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC
|
|
||||||
//TODO: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst)
|
|
||||||
{
|
|
||||||
|
|
||||||
uint32_t i, j, k;
|
|
||||||
uint32_t CRCExtracted = 0;
|
|
||||||
uint32_t CRCComputed = 0;
|
|
||||||
uint32_t CRCCorrect = 0;
|
|
||||||
uint32_t IrrecoverableErrors = 0;
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
uint8_t block = state->data_block_counter[slot];
|
|
||||||
|
|
||||||
//BPTC 196x96 Specific
|
|
||||||
uint8_t BPTCDeInteleavedData[196];
|
|
||||||
uint8_t BPTCDmrDataBit[96];
|
|
||||||
uint8_t BPTCDmrDataByte[12];
|
|
||||||
|
|
||||||
//Embedded Signalling Specific
|
|
||||||
uint8_t BptcDataMatrix[8][16];
|
|
||||||
uint8_t LC_DataBit[77];
|
|
||||||
uint8_t LC_DataBytes[10];
|
|
||||||
int Burst = -1;
|
|
||||||
|
|
||||||
//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
|
|
||||||
memset (DMR_PDU, 0, sizeof (DMR_PDU));
|
|
||||||
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
|
||||||
|
|
||||||
uint8_t R[3];
|
|
||||||
uint8_t BPTCReservedBits = 0;
|
|
||||||
|
|
||||||
uint32_t crcmask = 0;
|
|
||||||
uint8_t crclen = 0;
|
|
||||||
|
|
||||||
uint8_t is_bptc = 0;
|
|
||||||
uint8_t is_trellis = 0;
|
|
||||||
uint8_t is_emb = 0;
|
|
||||||
uint8_t is_lc = 0;
|
|
||||||
uint8_t is_full = 0;
|
|
||||||
uint8_t pdu_len = 0;
|
|
||||||
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
|
||||||
|
|
||||||
switch(databurst){
|
|
||||||
case 0x00: //PI
|
|
||||||
is_bptc = 1;
|
|
||||||
crclen = 16;
|
|
||||||
crcmask = 0x6969; //insert pi and 69 jokes here
|
|
||||||
pdu_len = 12; //12 bytes
|
|
||||||
sprintf(state->fsubtype, " PI ");
|
|
||||||
break;
|
|
||||||
case 0x01: //VLC
|
|
||||||
is_bptc = 1;
|
|
||||||
is_lc = 1;
|
|
||||||
crclen = 24;
|
|
||||||
crcmask = 0x969696;
|
|
||||||
pdu_len = 12; //12 bytes
|
|
||||||
sprintf(state->fsubtype, " VLC ");
|
|
||||||
break;
|
|
||||||
case 0x02: //TLC
|
|
||||||
is_bptc = 1;
|
|
||||||
is_lc = 1;
|
|
||||||
crcmask = 0x999999;
|
|
||||||
crclen = 24;
|
|
||||||
pdu_len = 12; //12 bytes
|
|
||||||
sprintf(state->fsubtype, " TLC ");
|
|
||||||
break;
|
|
||||||
case 0x03: //CSBK
|
|
||||||
is_bptc = 1;
|
|
||||||
crclen = 16;
|
|
||||||
crcmask = 0xA5A5;
|
|
||||||
pdu_len = 12; //12 bytes
|
|
||||||
sprintf(state->fsubtype, " CSBK");
|
|
||||||
break;
|
|
||||||
case 0x04: //MBC Header
|
|
||||||
is_bptc = 1;
|
|
||||||
crclen = 16;
|
|
||||||
crcmask = 0xAAAA;
|
|
||||||
pdu_len = 12; //12 bytes
|
|
||||||
sprintf(state->fsubtype, " MBCH");
|
|
||||||
break;
|
|
||||||
case 0x05: //MBC Continuation
|
|
||||||
is_bptc = 1;
|
|
||||||
//let block assembler carry out crc on the completed message
|
|
||||||
pdu_len = 12; //12 bytes
|
|
||||||
sprintf(state->fsubtype, " MBCC");
|
|
||||||
break;
|
|
||||||
case 0x06: //Data Header
|
|
||||||
is_bptc = 1;
|
|
||||||
crclen = 16;
|
|
||||||
crcmask = 0xCCCC;
|
|
||||||
pdu_len = 12; //12 bytes
|
|
||||||
sprintf(state->fsubtype, " DATA");
|
|
||||||
break;
|
|
||||||
//TODO - 1/2 0x0F0 - 3/4 0x1FF - Rate 1 0x10F
|
|
||||||
//going to set crclen and mask according to confirmed data here,
|
|
||||||
//use block assmebler to carry out completed message crc
|
|
||||||
case 0x07: //1/2 Rate Data
|
|
||||||
is_bptc = 1;
|
|
||||||
crclen = 9; //confirmed data only
|
|
||||||
crcmask = 0x0F0;
|
|
||||||
pdu_len = 12; //12 bytes unconfirmed
|
|
||||||
if (state->data_conf_data[slot] == 1)
|
|
||||||
{
|
|
||||||
pdu_len = 10;
|
|
||||||
pdu_start = 2; //start at plus two when assembling
|
|
||||||
}
|
|
||||||
sprintf(state->fsubtype, " R12D ");
|
|
||||||
break;
|
|
||||||
case 0x08: //3/4 Rate Data
|
|
||||||
is_trellis = 1;
|
|
||||||
crclen = 9; //confirmed data only
|
|
||||||
crcmask = 0x1FF;
|
|
||||||
pdu_len = 18; //18 bytes unconfirmed
|
|
||||||
if (state->data_conf_data[slot] == 1)
|
|
||||||
{
|
|
||||||
pdu_len = 16;
|
|
||||||
pdu_start = 2; //start at plus two when assembling
|
|
||||||
}
|
|
||||||
sprintf(state->fsubtype, " R34D ");
|
|
||||||
break;
|
|
||||||
case 0x09: //Idle
|
|
||||||
//pseudo-random data fill, no need to do anything with this
|
|
||||||
sprintf(state->fsubtype, " IDLE ");
|
|
||||||
break;
|
|
||||||
case 0x0A: //1 Rate Data
|
|
||||||
crclen = 9; //confirmed data only
|
|
||||||
crcmask = 0x1FF;
|
|
||||||
is_full = 1;
|
|
||||||
pdu_len = 25; //196 bits - 24.5 bytes
|
|
||||||
sprintf(state->fsubtype, " R_1D ");
|
|
||||||
break;
|
|
||||||
case 0x0B: //Unified Data
|
|
||||||
is_bptc = 1;
|
|
||||||
crclen = 16;
|
|
||||||
crcmask = 0x3333;
|
|
||||||
pdu_len = 12; //12 bytes
|
|
||||||
sprintf(state->fsubtype, " UDAT ");
|
|
||||||
break;
|
|
||||||
//special types (not real data 'sync' bursts)
|
|
||||||
case 0xEB: //Embedded Signalling
|
|
||||||
is_emb = 1;
|
|
||||||
pdu_len = 9;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (databurst != 0xEB)
|
|
||||||
{
|
|
||||||
if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
|
||||||
fprintf(stderr, "|%s", state->fsubtype);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Most Data Sync Burst types will use the bptc 196x96
|
|
||||||
if (is_bptc)
|
|
||||||
{
|
|
||||||
CRCExtracted = 0;
|
|
||||||
CRCComputed = 0;
|
|
||||||
IrrecoverableErrors = 0;
|
|
||||||
|
|
||||||
/* Deinterleave DMR data */
|
|
||||||
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
|
|
||||||
|
|
||||||
/* Extract the BPTC 196,96 DMR data */
|
|
||||||
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
|
||||||
|
|
||||||
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
|
|
||||||
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x08);
|
|
||||||
|
|
||||||
/* Convert the 96 bits BPTC data into 12 bytes */
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
BPTCDmrDataByte[i] = 0;
|
|
||||||
for(j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1;
|
|
||||||
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01);
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
|
|
||||||
CRCExtracted = 0;
|
|
||||||
for(i = 0; i < crclen; i++)
|
|
||||||
{
|
|
||||||
CRCExtracted = CRCExtracted << 1;
|
|
||||||
CRCExtracted = CRCExtracted | (uint32_t)(BPTCDmrDataBit[i + 96 - crclen] & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */
|
|
||||||
CRCExtracted = CRCExtracted ^ crcmask;
|
|
||||||
|
|
||||||
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
|
||||||
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed 1/2 data blocks
|
|
||||||
else if (state->data_conf_data[slot] == 1)
|
|
||||||
{
|
|
||||||
//run CRC on individual blocks according to type and validate each individual block
|
|
||||||
}
|
|
||||||
|
|
||||||
//run CCITT on other data forms
|
|
||||||
else //will still run on unconfirmed data (shouldn't matter other than a possible bogus warning message)
|
|
||||||
{
|
|
||||||
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
|
||||||
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
|
||||||
else CRCCorrect = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
|
|
||||||
{
|
|
||||||
if (CRCCorrect)
|
|
||||||
{
|
|
||||||
state->data_block_crc_valid[slot][0] = 1;
|
|
||||||
}
|
|
||||||
else state->data_block_crc_valid[slot][0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert corrected x bytes into x*8 bits */
|
|
||||||
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
|
||||||
{
|
|
||||||
BPTCDmrDataBit[j + 0] = (BPTCDmrDataByte[i+pdu_start] >> 7) & 0x01;
|
|
||||||
BPTCDmrDataBit[j + 1] = (BPTCDmrDataByte[i+pdu_start] >> 6) & 0x01;
|
|
||||||
BPTCDmrDataBit[j + 2] = (BPTCDmrDataByte[i+pdu_start] >> 5) & 0x01;
|
|
||||||
BPTCDmrDataBit[j + 3] = (BPTCDmrDataByte[i+pdu_start] >> 4) & 0x01;
|
|
||||||
BPTCDmrDataBit[j + 4] = (BPTCDmrDataByte[i+pdu_start] >> 3) & 0x01;
|
|
||||||
BPTCDmrDataBit[j + 5] = (BPTCDmrDataByte[i+pdu_start] >> 2) & 0x01;
|
|
||||||
BPTCDmrDataBit[j + 6] = (BPTCDmrDataByte[i+pdu_start] >> 1) & 0x01;
|
|
||||||
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
//convert to unified DMR_PDU and DMR_PDU_bits
|
|
||||||
for (i = 0; i < pdu_len; i++)
|
|
||||||
{
|
|
||||||
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < pdu_len * 8; i++)
|
|
||||||
{
|
|
||||||
DMR_PDU_bits[i] = BPTCDmrDataBit[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Embedded Signalling will use BPTC 128x77
|
|
||||||
if (is_emb)
|
|
||||||
{
|
|
||||||
|
|
||||||
CRCExtracted = 0;
|
|
||||||
CRCComputed = 0;
|
|
||||||
IrrecoverableErrors = 0;
|
|
||||||
|
|
||||||
/* First step : Reconstitute the BPTC 16x8 matrix */
|
|
||||||
Burst = 1; /* Burst B to E contains embedded signaling data */
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
for(j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
/* Only the LSBit of the byte is stored */
|
|
||||||
BptcDataMatrix[j][i] = state->dmr_embedded_signalling[slot][Burst][k+8];
|
|
||||||
k++;
|
|
||||||
|
|
||||||
/* Go on to the next burst once 32 bit
|
|
||||||
* of the SNYC have been stored */
|
|
||||||
if(k >= 32)
|
|
||||||
{
|
|
||||||
k = 0;
|
|
||||||
Burst++;
|
|
||||||
}
|
|
||||||
} /* End for(j = 0; j < 8; j++) */
|
|
||||||
} /* End for(i = 0; i < 16; i++) */
|
|
||||||
|
|
||||||
/* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */
|
|
||||||
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
|
||||||
|
|
||||||
/* Reconstitute the 5 bit CRC */
|
|
||||||
CRCExtracted = (LC_DataBit[72] & 1) << 4;
|
|
||||||
CRCExtracted |= (LC_DataBit[73] & 1) << 3;
|
|
||||||
CRCExtracted |= (LC_DataBit[74] & 1) << 2;
|
|
||||||
CRCExtracted |= (LC_DataBit[75] & 1) << 1;
|
|
||||||
CRCExtracted |= (LC_DataBit[76] & 1) << 0;
|
|
||||||
|
|
||||||
/* Compute the 5 bit CRC */
|
|
||||||
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
|
||||||
|
|
||||||
if(CRCExtracted == CRCComputed) CRCCorrect = 1;
|
|
||||||
else CRCCorrect = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < 72; i++)
|
|
||||||
{
|
|
||||||
DMR_PDU_bits[i] = LC_DataBit[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 9; i++)
|
|
||||||
{
|
|
||||||
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&LC_DataBit[i*8], 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//the ugly one
|
|
||||||
if (is_trellis)
|
|
||||||
{
|
|
||||||
CRCExtracted = 0;
|
|
||||||
CRCComputed = 0;
|
|
||||||
IrrecoverableErrors = 1;
|
|
||||||
bool trellis_good = TRUE;
|
|
||||||
|
|
||||||
uint8_t tdibits[98];
|
|
||||||
memset (tdibits, 0, sizeof(tdibits));
|
|
||||||
|
|
||||||
//reconstitute info bits into reverse ordered dibits for the trellis decoder
|
|
||||||
for (i = 0; i < 98; i++)
|
|
||||||
{
|
|
||||||
tdibits[97-i] = (info[i*2] << 1) | info[i*2+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char TrellisReturn[18];
|
|
||||||
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
|
||||||
|
|
||||||
trellis_good = CDMRTrellisDecode(tdibits, TrellisReturn);
|
|
||||||
if (trellis_good == TRUE) IrrecoverableErrors = 0;
|
|
||||||
// else (fprintf (stderr, " Trellis Failure")); //debug print
|
|
||||||
|
|
||||||
for (i = 0; i < pdu_len; i++) //18
|
|
||||||
{
|
|
||||||
DMR_PDU[i] = (uint8_t)TrellisReturn[i+pdu_start];
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
|
||||||
{
|
|
||||||
DMR_PDU_bits[j + 0] = (TrellisReturn[i+pdu_start] >> 7) & 0x01;
|
|
||||||
DMR_PDU_bits[j + 1] = (TrellisReturn[i+pdu_start] >> 6) & 0x01;
|
|
||||||
DMR_PDU_bits[j + 2] = (TrellisReturn[i+pdu_start] >> 5) & 0x01;
|
|
||||||
DMR_PDU_bits[j + 3] = (TrellisReturn[i+pdu_start] >> 4) & 0x01;
|
|
||||||
DMR_PDU_bits[j + 4] = (TrellisReturn[i+pdu_start] >> 3) & 0x01;
|
|
||||||
DMR_PDU_bits[j + 5] = (TrellisReturn[i+pdu_start] >> 2) & 0x01;
|
|
||||||
DMR_PDU_bits[j + 6] = (TrellisReturn[i+pdu_start] >> 1) & 0x01;
|
|
||||||
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed 3/4 data blocks
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_full) //Rate 1 Data
|
|
||||||
{
|
|
||||||
CRCExtracted = 0;
|
|
||||||
CRCComputed = 0;
|
|
||||||
IrrecoverableErrors = 0;
|
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed data
|
|
||||||
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8);
|
|
||||||
}
|
|
||||||
//leftover 4 bits, manual doesn't say which 4 so assuming last 4
|
|
||||||
//pushing last 4 to 25 and shifting it
|
|
||||||
DMR_PDU[25] = (uint8_t)ConvertBitIntoBytes(&info[192], 4);
|
|
||||||
DMR_PDU[25] = DMR_PDU[25] << 4;
|
|
||||||
|
|
||||||
//copy info to dmr_pdu_bits
|
|
||||||
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//time for some pi
|
|
||||||
if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
|
||||||
|
|
||||||
//full link control
|
|
||||||
if (databurst == 0x01) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 1); //VLC
|
|
||||||
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC
|
|
||||||
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 3); //EMB
|
|
||||||
|
|
||||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, 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
|
|
||||||
if (databurst == 0x08) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //3/4 Rate Data
|
|
||||||
//if (databurst == 0x0A) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Full Rate Data - Disabled until further testing can be done
|
|
||||||
|
|
||||||
//is unified data handled like normal data, or control signalling data?
|
|
||||||
if (databurst == 0x0B) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Unified Data
|
|
||||||
//if (databurst == 0x0B) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
|
||||||
|
|
||||||
//control signalling types (CSBK, MBC, UDT)
|
|
||||||
if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
|
||||||
|
|
||||||
//both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu
|
|
||||||
if (databurst == 0x04)
|
|
||||||
{
|
|
||||||
state->data_block_counter[slot] = 0; //zero block counter before running header
|
|
||||||
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);
|
|
||||||
|
|
||||||
//print the unified PDU format here, if not slot idle
|
|
||||||
if (opts->payload == 1 && databurst != 0x09)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KCYN);
|
|
||||||
fprintf (stderr, " DMR PDU Payload ");
|
|
||||||
for (i = 0; i < pdu_len; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", DMR_PDU[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
// fprintf (stderr, "%s", KCYN);
|
|
||||||
// fprintf (stderr, "\n Hex to Ascii - ");
|
|
||||||
// for (i = 0; i < 12; i++)
|
|
||||||
// {
|
|
||||||
// if (DMR_PDU[i] <= 0x7E && DMR_PDU[i] >=0x20)
|
|
||||||
// {
|
|
||||||
// fprintf (stderr, "%c", DMR_PDU[i]);
|
|
||||||
// }
|
|
||||||
// else fprintf (stderr, ".");
|
|
||||||
// }
|
|
||||||
|
|
||||||
fprintf (stderr,"%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
808
src/dmr_flco.c
808
src/dmr_flco.c
@@ -1,808 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_flco.c
|
|
||||||
* DMR Full Link Control, Short Link Control, TACT/CACH and related funtions
|
|
||||||
*
|
|
||||||
* Portions of link control/voice burst/vlc/tlc from LouisErigHerve
|
|
||||||
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
//combined flco handler (vlc, tlc, emb), minus the superfluous structs and strings
|
|
||||||
void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors, uint8_t type)
|
|
||||||
{
|
|
||||||
|
|
||||||
//force slot to 0 if using dmr mono handling
|
|
||||||
if (opts->dmr_mono == 1) state->currentslot = 0;
|
|
||||||
|
|
||||||
uint8_t pf = 0;
|
|
||||||
uint8_t reserved = 0;
|
|
||||||
uint8_t flco = 0;
|
|
||||||
uint8_t fid = 0;
|
|
||||||
uint8_t so = 0;
|
|
||||||
uint32_t target = 0;
|
|
||||||
uint32_t source = 0;
|
|
||||||
int restchannel = -1;
|
|
||||||
int is_cap_plus = 0;
|
|
||||||
int is_alias = 0;
|
|
||||||
int is_gps = 0;
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
|
|
||||||
|
|
||||||
pf = (uint8_t)(lc_bits[0]); //Protect Flag
|
|
||||||
reserved = (uint8_t)(lc_bits[1]); //Reserved
|
|
||||||
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);
|
|
||||||
|
|
||||||
//look for Cap+ on VLC header, then set source and/or rest channel appropriately
|
|
||||||
if (type == 1 && fid == 0x10 && flco == 0x04)
|
|
||||||
{
|
|
||||||
is_cap_plus = 1;
|
|
||||||
restchannel = (int)ConvertBitIntoBytes(&lc_bits[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)
|
|
||||||
{
|
|
||||||
//Embedded Talker Alias Header Only (format and len storage)
|
|
||||||
if (type == 3 && flco == 0x04)
|
|
||||||
{
|
|
||||||
dmr_embedded_alias_header(opts, state, lc_bits);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Embedded Talker Alias Header (continuation) and Blocks
|
|
||||||
if (type == 3 && flco > 0x3 && flco < 0x08)
|
|
||||||
{
|
|
||||||
is_alias = 1;
|
|
||||||
dmr_embedded_alias_blocks(opts, state, lc_bits);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Embedded GPS
|
|
||||||
if (type == 3 && flco == 0x08)
|
|
||||||
{
|
|
||||||
is_gps = 1;
|
|
||||||
if (fid == 0) dmr_embedded_gps(opts, state, lc_bits); //issues with fid 0x10 and flco 0x08
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->dmr_fid = fid;
|
|
||||||
state->dmr_so = so;
|
|
||||||
state->lasttg = target;
|
|
||||||
state->lastsrc = source;
|
|
||||||
}
|
|
||||||
if (state->currentslot == 1)
|
|
||||||
{
|
|
||||||
state->dmr_fidR = fid;
|
|
||||||
state->dmr_soR = so;
|
|
||||||
state->lasttgR = target;
|
|
||||||
state->lastsrcR = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
//update cc amd vc sync time for trunking purposes (particularly Con+)
|
|
||||||
if (opts->p25_is_tuned == 1)
|
|
||||||
{
|
|
||||||
state->last_vc_sync_time = time(NULL);
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == 2) //TLC, zero out target, source, so, and fid per channel, and other odd and ends
|
|
||||||
{
|
|
||||||
//I wonder which of these we truly want to zero out, possibly none of them
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->dmr_fid = 0;
|
|
||||||
state->dmr_so = 0;
|
|
||||||
// state->lasttg = 0;
|
|
||||||
// state->lastsrc = 0;
|
|
||||||
state->payload_algid = 0;
|
|
||||||
state->payload_mi = 0;
|
|
||||||
}
|
|
||||||
if (state->currentslot == 1)
|
|
||||||
{
|
|
||||||
state->dmr_fidR = 0;
|
|
||||||
state->dmr_soR = 0;
|
|
||||||
// state->lasttgR = 0;
|
|
||||||
// state->lastsrcR = 0;
|
|
||||||
state->payload_algidR = 0;
|
|
||||||
state->payload_miR = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (restchannel != state->dmr_rest_channel && restchannel != -1)
|
|
||||||
{
|
|
||||||
state->dmr_rest_channel = restchannel;
|
|
||||||
//assign to cc freq
|
|
||||||
if (state->trunk_chan_map[restchannel] != 0)
|
|
||||||
{
|
|
||||||
state->p25_cc_freq = state->trunk_chan_map[restchannel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == 1) fprintf (stderr, "%s \n", KGRN);
|
|
||||||
if (type == 2) fprintf (stderr, "%s \n", KRED);
|
|
||||||
if (type == 3) fprintf (stderr, "%s", KGRN);
|
|
||||||
|
|
||||||
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
|
||||||
fprintf(stderr, "TGT=%u SRC=%u ", target, source);
|
|
||||||
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
|
|
||||||
|
|
||||||
if (flco == 3) //UU_V_Ch_Usr
|
|
||||||
{
|
|
||||||
sprintf (state->call_string[slot], " Private");
|
|
||||||
fprintf (stderr, "Private ");
|
|
||||||
}
|
|
||||||
else //Grp_V_Ch_Usr
|
|
||||||
{
|
|
||||||
sprintf (state->call_string[slot], " Group");
|
|
||||||
fprintf (stderr, "Group ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(so & 0x80)
|
|
||||||
{
|
|
||||||
strcat (state->call_string[slot], " Emergency ");
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf(stderr, "Emergency ");
|
|
||||||
}
|
|
||||||
else strcat (state->call_string[slot], " ");
|
|
||||||
|
|
||||||
if(so & 0x40)
|
|
||||||
{
|
|
||||||
//REMUS! Uncomment Line Below if desired
|
|
||||||
// strcat (state->call_string[slot], " Encrypted");
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf(stderr, "Encrypted ");
|
|
||||||
}
|
|
||||||
//REMUS! Uncomment Line Below if desired
|
|
||||||
// else strcat (state->call_string[slot], " ");
|
|
||||||
|
|
||||||
/* 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, "Reserved=%d ", (so & 0x30) >> 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(so & 0x08)
|
|
||||||
{
|
|
||||||
//REMUS! Uncomment Line Below if desired
|
|
||||||
// strcat (state->call_string[slot], "-BC ");
|
|
||||||
fprintf(stderr, "Broadcast ");
|
|
||||||
}
|
|
||||||
if(so & 0x04)
|
|
||||||
{
|
|
||||||
//REMUS! Uncomment Line Below if desired
|
|
||||||
// strcat (state->call_string[slot], "-OVCM ");
|
|
||||||
fprintf(stderr, "OVCM ");
|
|
||||||
}
|
|
||||||
if(so & 0x03)
|
|
||||||
{
|
|
||||||
if((so & 0x03) == 0x01)
|
|
||||||
{
|
|
||||||
//REMUS! Uncomment Line Below if desired
|
|
||||||
// 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 ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Call ");
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
if (CRCCorrect == 1) ; //CRCCorrect 1 is good, else is bad CRC; no print on good
|
|
||||||
else if(IrrecoverableErrors == 0) fprintf(stderr, "(FEC/RAS)");
|
|
||||||
else fprintf(stderr, "(FEC/CRC ERR)");
|
|
||||||
|
|
||||||
//check Cap+ rest channel info if available and good fec
|
|
||||||
if (is_cap_plus == 1)
|
|
||||||
{
|
|
||||||
if (restchannel != -1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s ", KYEL);
|
|
||||||
fprintf (stderr, "Cap+ R-Ch %d", restchannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
|
||||||
|
|
||||||
//group labels
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
//Remus! Change target to source if you prefer
|
|
||||||
if (state->group_array[i].groupNumber == target)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KCYN);
|
|
||||||
fprintf (stderr, "[%s] ", state->group_array[i].groupName);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->K != 0 && fid == 0x10 && so & 0x40)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, "Key %lld ", state->K);
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->K1 != 0 && fid == 0x68 && so & 0x40)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Key %010llX ", state->K1);
|
|
||||||
if (state->K2 != 0) fprintf (stderr, "%016llX ", state->K2);
|
|
||||||
if (state->K4 != 0) fprintf (stderr, "%016llX %016llX", state->K3, state->K4);
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//externalized dmr cach - tact and slco fragment handling
|
|
||||||
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25])
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
uint8_t err = 0;
|
|
||||||
uint8_t tact_valid = 0;
|
|
||||||
uint8_t cach_valid = 0;
|
|
||||||
|
|
||||||
bool h1, h2, h3, crc;
|
|
||||||
|
|
||||||
//dump payload - testing
|
|
||||||
uint8_t slco_raw_bits[68]; //raw
|
|
||||||
uint8_t slco_bits[68]; //de-interleaved
|
|
||||||
|
|
||||||
//tact pdu
|
|
||||||
uint8_t tact_bits[7];
|
|
||||||
uint8_t at = 0; //access type, set to 1 during continuous transmission mode
|
|
||||||
uint8_t slot = 2; //tdma time slot
|
|
||||||
uint8_t lcss = 0; //link control start stop (9.3.3) NOTE: There is no Single fragment LC defined for CACH signalling
|
|
||||||
|
|
||||||
//cach_bits are already de-interleaved upon initial collection (still needs secodary slco de-interleave)
|
|
||||||
for (i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
tact_bits[i] = cach_bits[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
//run hamming 7_4 on the tact_bits (redundant, since we do it earlier, but need the lcss)
|
|
||||||
if ( Hamming_7_4_decode (tact_bits) )
|
|
||||||
{
|
|
||||||
at = tact_bits[0]; //any useful tricks with this? csbk on/off etc?
|
|
||||||
slot = tact_bits[1]; //
|
|
||||||
lcss = (tact_bits[2] << 1) | tact_bits[3];
|
|
||||||
tact_valid = 1; //set to 1 for valid, else remains 0.
|
|
||||||
//fprintf (stderr, "AT=%d LCSS=%d - ", at, lcss); //debug print
|
|
||||||
}
|
|
||||||
else //probably skip/memset/zeroes with else statement?
|
|
||||||
{
|
|
||||||
//do something?
|
|
||||||
err = 1;
|
|
||||||
//return (err);
|
|
||||||
}
|
|
||||||
|
|
||||||
//determine counter value based on lcss value
|
|
||||||
if (lcss == 0) //run as single block/fragment NOTE: There is no Single fragment LC defined for CACH signalling (but is mentioned in the manual table)
|
|
||||||
{
|
|
||||||
//reset the full cach, even if we aren't going to use it, may be beneficial for next time
|
|
||||||
state->dmr_cach_counter = 0; //first fragment, set to zero.
|
|
||||||
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
|
||||||
|
|
||||||
//seperate
|
|
||||||
for (i = 0; i < 17; i++)
|
|
||||||
{
|
|
||||||
slco_raw_bits[i] = cach_bits[i+7];
|
|
||||||
}
|
|
||||||
|
|
||||||
//De-interleave
|
|
||||||
int src = 0;
|
|
||||||
for (i = 0; i < 17; i++)
|
|
||||||
{
|
|
||||||
src = (i * 4) % 67;
|
|
||||||
slco_bits[i] = slco_raw_bits[src];
|
|
||||||
}
|
|
||||||
//slco_bits[i] = slco_raw_bits[i];
|
|
||||||
|
|
||||||
//hamming check here
|
|
||||||
h1 = Hamming17123 (slco_bits + 0);
|
|
||||||
|
|
||||||
//run single - manual would suggest that though this exists, there is no support? or perhaps its manufacturer only thing?
|
|
||||||
if (h1) ; // dmr_slco (opts, state, slco_bits); //random false positives and sets bad parms/mfid etc
|
|
||||||
else
|
|
||||||
{
|
|
||||||
err = 1;
|
|
||||||
//return (err);
|
|
||||||
}
|
|
||||||
return (err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lcss == 1) //first block, reset counters and memset
|
|
||||||
{
|
|
||||||
//reset the full cach and counter
|
|
||||||
state->dmr_cach_counter = 0;
|
|
||||||
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
|
||||||
}
|
|
||||||
if (lcss == 3) state->dmr_cach_counter++; //continuation, so increment counter by one.
|
|
||||||
if (lcss == 2) //final segment - assemble, de-interleave, hamming, crc, and execute
|
|
||||||
{
|
|
||||||
state->dmr_cach_counter = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
//sanity check
|
|
||||||
if (state->dmr_cach_counter > 3) //marginal/shaky/bad signal or tuned away
|
|
||||||
{
|
|
||||||
//zero out complete fragment array
|
|
||||||
lcss = 5; //toss away value
|
|
||||||
state->dmr_cach_counter = 0;
|
|
||||||
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
|
||||||
err = 1;
|
|
||||||
return (err);
|
|
||||||
}
|
|
||||||
|
|
||||||
//add fragment to array
|
|
||||||
for (i = 0; i < 17; i++)
|
|
||||||
{
|
|
||||||
state->dmr_cach_fragment[state->dmr_cach_counter][i] = cach_bits[i+7];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lcss == 2) //last block arrived, compile, hamming, crc and send off to dmr_slco
|
|
||||||
{
|
|
||||||
//assemble
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 17; i++)
|
|
||||||
{
|
|
||||||
slco_raw_bits[i+(17*j)] = state->dmr_cach_fragment[j][i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//De-interleave method, hamming, and crc from Boatbod OP25
|
|
||||||
|
|
||||||
//De-interleave
|
|
||||||
int src = 0;
|
|
||||||
for (i = 0; i < 67; i++)
|
|
||||||
{
|
|
||||||
src = (i * 4) % 67;
|
|
||||||
slco_bits[i] = slco_raw_bits[src];
|
|
||||||
}
|
|
||||||
slco_bits[i] = slco_raw_bits[i];
|
|
||||||
|
|
||||||
//hamming checks here
|
|
||||||
h1 = Hamming17123 (slco_bits + 0);
|
|
||||||
h2 = Hamming17123 (slco_bits + 17);
|
|
||||||
h3 = Hamming17123 (slco_bits + 34);
|
|
||||||
|
|
||||||
// remove hamming and leave 36 bits of Short LC
|
|
||||||
for (i = 17; i < 29; i++) {
|
|
||||||
slco_bits[i-5] = slco_bits[i];
|
|
||||||
}
|
|
||||||
for (i = 34; i < 46; i++) {
|
|
||||||
slco_bits[i-10] = slco_bits[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
//zero out the hangover bits
|
|
||||||
for (i = 36; i < 68; i++)
|
|
||||||
{
|
|
||||||
slco_bits[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//run crc8
|
|
||||||
crc = crc8_ok(slco_bits, 36);
|
|
||||||
|
|
||||||
//only run SLCO on good everything
|
|
||||||
if (h1 && h2 && h3 && crc) dmr_slco (opts, state, slco_bits);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, " SLCO CRC ERR");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return (err); //return err value based on success or failure, even if we aren't checking it
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
|
||||||
{
|
|
||||||
long int ccfreq = 0;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
uint8_t slco_bytes[6]; //completed byte blocks for payload print
|
|
||||||
for (i = 0; i < 5; i++) slco_bytes[i] = (uint8_t)ConvertBitIntoBytes(&slco_bits[i*8], 8);
|
|
||||||
slco_bytes[5] = (uint8_t)ConvertBitIntoBytes(&slco_bits[32], 4);
|
|
||||||
|
|
||||||
|
|
||||||
//just going to decode the Short LC with all potential parameters known
|
|
||||||
uint8_t slco = (uint8_t)ConvertBitIntoBytes(&slco_bits[0], 4);
|
|
||||||
uint8_t model = (uint8_t)ConvertBitIntoBytes(&slco_bits[4], 2);
|
|
||||||
uint16_t netsite = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 12);
|
|
||||||
uint8_t reg = slco_bits[18]; //registration required/not required or normalchanneltype/composite cch
|
|
||||||
uint8_t csc = (uint16_t)ConvertBitIntoBytes(&slco_bits[19], 9); //common slot counter, 0-511
|
|
||||||
uint16_t net = 0;
|
|
||||||
uint16_t site = 0;
|
|
||||||
char model_str[8];
|
|
||||||
sprintf (model_str, "%s", "");
|
|
||||||
//activity update stuff
|
|
||||||
uint8_t ts1_act = (uint8_t)ConvertBitIntoBytes(&slco_bits[4], 4); //activity update ts1
|
|
||||||
uint8_t ts2_act = (uint8_t)ConvertBitIntoBytes(&slco_bits[8], 4); //activity update ts2
|
|
||||||
uint8_t ts1_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 8); //ts1 address hash (crc8) //361-1 B.3.7
|
|
||||||
uint8_t ts2_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 8); //ts2 address hash (crc8) //361-1 B.3.7
|
|
||||||
|
|
||||||
//DMR Location Area - DMRLA - should probably be state variables so we can use this in both slc and csbk
|
|
||||||
uint16_t sys_area = 0;
|
|
||||||
uint16_t sub_area = 0;
|
|
||||||
uint16_t sub_mask = 0x1;
|
|
||||||
//tiny n 1-3; small 1-5; large 1-8; huge 1-10
|
|
||||||
uint16_t n = 1; //The minimum value of DMRLA is normally ≥ 1, 0 is reserved
|
|
||||||
|
|
||||||
//TODO: Add logic to set n and sub_mask values for DMRLA
|
|
||||||
//assigning n as largest possible value for model type
|
|
||||||
|
|
||||||
//Sys_Parms
|
|
||||||
if (slco == 0x2 || slco == 0x3)
|
|
||||||
{
|
|
||||||
if (model == 0) //Tiny
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 9);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[15], 3);
|
|
||||||
sprintf (model_str, "%s", "Tiny");
|
|
||||||
n = 3;
|
|
||||||
}
|
|
||||||
else if (model == 1) //Small
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 7);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[13], 5);
|
|
||||||
sprintf (model_str, "%s", "Small");
|
|
||||||
n = 5;
|
|
||||||
}
|
|
||||||
else if (model == 2) //Large
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 4);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[10], 8);
|
|
||||||
sprintf (model_str, "%s", "Large");
|
|
||||||
n = 8;
|
|
||||||
}
|
|
||||||
else if (model == 3) //Huge
|
|
||||||
{
|
|
||||||
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 2);
|
|
||||||
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[8], 10);
|
|
||||||
sprintf (model_str, "%s", "Huge");
|
|
||||||
n = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->dmr_dmrla_is_set == 1) n = opts->dmr_dmrla_n;
|
|
||||||
|
|
||||||
if (n == 0) sub_mask = 0x0;
|
|
||||||
if (n == 1) sub_mask = 0x1;
|
|
||||||
if (n == 2) sub_mask = 0x3;
|
|
||||||
if (n == 3) sub_mask = 0x7;
|
|
||||||
if (n == 4) sub_mask = 0xF;
|
|
||||||
if (n == 5) sub_mask = 0x1F;
|
|
||||||
if (n == 6) sub_mask = 0x3F;
|
|
||||||
if (n == 7) sub_mask = 0x7F;
|
|
||||||
if (n == 8) sub_mask = 0xFF;
|
|
||||||
if (n == 9) sub_mask = 0x1FF;
|
|
||||||
if (n == 10) sub_mask = 0x3FF;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 restchannel = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 8);
|
|
||||||
|
|
||||||
//initial line break
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
|
|
||||||
if (slco == 0x2) //C_SYS_Parms
|
|
||||||
{
|
|
||||||
if (n != 0) fprintf (stderr, " C_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d - Reg Req: %d - CSC: %d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, reg, csc);
|
|
||||||
else fprintf (stderr, " C_SYS_PARMS - %s - Net ID: %d Site ID: %d - Reg Req: %d - CSC: %d ", model_str, net, site, reg, csc);
|
|
||||||
|
|
||||||
//add string for ncurses terminal display
|
|
||||||
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
|
||||||
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
|
||||||
|
|
||||||
//if using rigctl we can set an unknown cc frequency by polling rigctl for the current frequency
|
|
||||||
|
|
||||||
if (opts->use_rigctl == 1 && state->p25_cc_freq == 0) //if not set from channel map 0
|
|
||||||
{
|
|
||||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
|
||||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
|
||||||
}
|
|
||||||
|
|
||||||
//nullify any previous branding sub (bugfix for bad assignments or system type switching)
|
|
||||||
//sprintf(state->dmr_branding_sub, "%s", "");
|
|
||||||
|
|
||||||
//debug print
|
|
||||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&slco_bits[4], 14);
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %014b", syscode);
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %02b.%b.%b", model, net, site); //won't show leading zeroes, but may not be required
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (slco == 0x3) //P_SYS_Parms
|
|
||||||
{
|
|
||||||
fprintf (stderr, " P_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d - Comp CC: %d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, reg);
|
|
||||||
|
|
||||||
//add string for ncurses terminal display
|
|
||||||
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
|
||||||
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
|
||||||
|
|
||||||
//nullify any previous branding sub (bugfix for bad assignments or system type switching)
|
|
||||||
//sprintf(state->dmr_branding_sub, "%s", "");
|
|
||||||
|
|
||||||
//debug print
|
|
||||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&slco_bits[4], 14);
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %014b", syscode);
|
|
||||||
//fprintf (stderr, "\n SYSCODE: %02b.%b.%b", model, net, site); //won't show leading zeroes, but may not be required
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (slco == 0x0) //null
|
|
||||||
fprintf (stderr, " SLCO NULL ");
|
|
||||||
else if (slco == 0x1)
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
//if using rigctl we can set an unknown cc frequency by polling rigctl for the current frequency
|
|
||||||
if (opts->use_rigctl == 1 && state->p25_cc_freq == 0) //if not set from channel map 0
|
|
||||||
{
|
|
||||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
|
||||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 Rest Channel %d", restchannel);
|
|
||||||
state->dmr_rest_channel = restchannel;
|
|
||||||
//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 fprintf (stderr, " SLCO Unknown - %d ", slco);
|
|
||||||
|
|
||||||
if (opts->payload == 1 && slco != 0) //if not SLCO NULL
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n SLCO Completed Block ");
|
|
||||||
for (i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", slco_bytes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//externalize embedded alias to keep the flco function relatively clean
|
|
||||||
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
|
||||||
{
|
|
||||||
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
uint8_t pf;
|
|
||||||
uint8_t res;
|
|
||||||
uint8_t format = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 2);
|
|
||||||
uint8_t len;
|
|
||||||
|
|
||||||
//this len seems to pertain to number of blocks? not bit len.
|
|
||||||
//len = (uint8_t)ConvertBitIntoBytes(&lc_bits[18], 5);
|
|
||||||
|
|
||||||
if (format == 0) len = 7;
|
|
||||||
else if (format == 1 || format == 2) len = 8;
|
|
||||||
else len = 16;
|
|
||||||
|
|
||||||
state->dmr_alias_format[slot] = format;
|
|
||||||
state->dmr_alias_len[slot] = len;
|
|
||||||
|
|
||||||
//fprintf (stderr, "F: %d L: %d - ", format, len);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Embedded Alias: ");
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
uint8_t block = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //FLCO equals block number
|
|
||||||
uint8_t format = state->dmr_alias_format[slot]; //0=7-bit; 1=ISO8; 2=UTF-8; 3=UTF16BE
|
|
||||||
uint8_t len = state->dmr_alias_len[slot];
|
|
||||||
uint8_t start; //starting position depends on context of block and format
|
|
||||||
|
|
||||||
//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
|
|
||||||
//else start = 24;
|
|
||||||
|
|
||||||
//forcing start to 16 make it work on 8-bit alias, len seems okay when set off of format
|
|
||||||
start = 16;
|
|
||||||
len = 8;
|
|
||||||
|
|
||||||
// fprintf (stderr, "block: %d start: %d len: %d ", block, start, len);
|
|
||||||
|
|
||||||
//all may not be used depending on format, len, start.
|
|
||||||
uint16_t A0, A1, A2, A3, A4, A5, A6;
|
|
||||||
|
|
||||||
//sanity check
|
|
||||||
if (block > 7) block = 4; //prevent oob array (although we should never get here)
|
|
||||||
|
|
||||||
if (len > 6) //if not greater than zero, then the header hasn't arrived yet
|
|
||||||
{
|
|
||||||
A0 = 0; A1 = 0; A2 = 0; A3 = 0; A4 = 0; A5 = 0; A6 = 0; //NULL ASCII Characters
|
|
||||||
A0 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*0], len);
|
|
||||||
A1 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*1], len);
|
|
||||||
A2 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*2], len);
|
|
||||||
A3 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*3], len);
|
|
||||||
A4 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*4], len);
|
|
||||||
A5 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*5], len);
|
|
||||||
A6 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*6], len);
|
|
||||||
|
|
||||||
//just going to assign the usual ascii set here to prevent 'naughty' characters, sans diacriticals
|
|
||||||
if (A0 > 0x19 && A0 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][0], "%c", A0);
|
|
||||||
if (A1 > 0x19 && A1 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][1], "%c", A1);
|
|
||||||
if (A2 > 0x19 && A2 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][2], "%c", A2);
|
|
||||||
if (A3 > 0x19 && A3 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][3], "%c", A3);
|
|
||||||
if (A4 > 0x19 && A4 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][4], "%c", A4);
|
|
||||||
if (A5 > 0x19 && A5 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][5], "%c", A5);
|
|
||||||
if (A6 > 0x19 && A6 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][6], "%c", A6);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 7; j++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", state->dmr_alias_block_segment[slot][i][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else fprintf (stderr, "Missing Header Block Format and Len Data");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//externalize embedded GPS - Needs samples to test/fix function
|
|
||||||
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Embedded GPS:");
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
uint8_t pf = lc_bits[0];
|
|
||||||
uint8_t res_a = lc_bits[1];
|
|
||||||
uint8_t res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4);
|
|
||||||
uint8_t pos_err = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 3);
|
|
||||||
|
|
||||||
uint32_t lon_sign = lc_bits[23];
|
|
||||||
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24);
|
|
||||||
uint32_t lat_sign = lc_bits[48];
|
|
||||||
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23);
|
|
||||||
|
|
||||||
double lat_unit = (double)180/ pow (2.0, 24); //180 divided by 2^24
|
|
||||||
double lon_unit = (double)360/ pow (2.0, 25); //360 divided by 2^25
|
|
||||||
|
|
||||||
char latstr[3];
|
|
||||||
char lonstr[3];
|
|
||||||
sprintf (latstr, "%s", "N");
|
|
||||||
sprintf (lonstr, "%s", "E");
|
|
||||||
|
|
||||||
//run calculations and print (still cannot verify as accurate)
|
|
||||||
//7.2.16 and 7.2.17 (two's compliment)
|
|
||||||
|
|
||||||
double latitude = 0;
|
|
||||||
double longitude = 0;
|
|
||||||
|
|
||||||
if (pf) fprintf (stderr, " Protected");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (lat_sign)
|
|
||||||
{
|
|
||||||
lat = 0x800001 - lat;
|
|
||||||
sprintf (latstr, "%s", "S");
|
|
||||||
}
|
|
||||||
latitude = ((double)lat * lat_unit);
|
|
||||||
|
|
||||||
if (lon_sign)
|
|
||||||
{
|
|
||||||
lon = 0x1000001 - lon;
|
|
||||||
sprintf (lonstr, "%s", "W");
|
|
||||||
}
|
|
||||||
longitude = ((double)lon * lon_unit);
|
|
||||||
|
|
||||||
//sanity check
|
|
||||||
if (abs (latitude) < 90 && abs(longitude) < 180)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " Lat: %.5lf %s Lon: %.5lf %s", latitude, latstr, longitude, lonstr);
|
|
||||||
|
|
||||||
//7.2.15 Position Error
|
|
||||||
uint16_t position_error = 2 * pow(10, pos_err); //2 * 10^pos_err
|
|
||||||
if (pos_err == 0x7 ) fprintf (stderr, " Position Error: Unknown or Invalid");
|
|
||||||
else fprintf (stderr, " Position Error: Less than %dm", position_error);
|
|
||||||
|
|
||||||
//save to array for ncurses
|
|
||||||
if (pos_err != 0x7)
|
|
||||||
{
|
|
||||||
sprintf (state->dmr_embedded_gps[slot], "E-GPS (%lf %s %lf %s) Err: %dm", latitude, latstr, longitude, lonstr, position_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
139
src/dmr_le.c
139
src/dmr_le.c
@@ -1,139 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_le.c
|
|
||||||
* DMR Late Entry MI Fragment Assembly, Procesing, and related Alg functions
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
//gather ambe_fr mi fragments for processing
|
|
||||||
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, char ambe_fr[4][24], char ambe_fr2[4][24], char ambe_fr3[4][24])
|
|
||||||
{
|
|
||||||
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
|
|
||||||
//collect our fragments and place them into storage
|
|
||||||
state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
|
|
||||||
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
|
|
||||||
state->late_entry_mi_fragment[slot][vc][2] = (uint64_t)ConvertBitIntoBytes(&ambe_fr3[3][0], 4);
|
|
||||||
|
|
||||||
if (vc == 6) dmr_late_entry_mi (opts, state);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
int i, j;
|
|
||||||
int g[3];
|
|
||||||
unsigned char mi_go_bits[24];
|
|
||||||
|
|
||||||
uint64_t mi_test = 0;
|
|
||||||
uint64_t go_test = 0;
|
|
||||||
uint64_t mi_corrected = 0;
|
|
||||||
uint64_t go_corrected = 0;
|
|
||||||
|
|
||||||
mi_test = (state->late_entry_mi_fragment[slot][1][0] << 32L) | (state->late_entry_mi_fragment[slot][2][0] << 28) | (state->late_entry_mi_fragment[slot][3][0] << 24) |
|
|
||||||
(state->late_entry_mi_fragment[slot][1][1] << 20) | (state->late_entry_mi_fragment[slot][2][1] << 16) | (state->late_entry_mi_fragment[slot][3][1] << 12) |
|
|
||||||
(state->late_entry_mi_fragment[slot][1][2] << 8) | (state->late_entry_mi_fragment[slot][2][2] << 4) | (state->late_entry_mi_fragment[slot][3][2] << 0);
|
|
||||||
|
|
||||||
go_test = (state->late_entry_mi_fragment[slot][4][0] << 32L) | (state->late_entry_mi_fragment[slot][5][0] << 28) | (state->late_entry_mi_fragment[slot][6][0] << 24) |
|
|
||||||
(state->late_entry_mi_fragment[slot][4][1] << 20) | (state->late_entry_mi_fragment[slot][5][1] << 16) | (state->late_entry_mi_fragment[slot][6][1] << 12) |
|
|
||||||
(state->late_entry_mi_fragment[slot][4][2] << 8) | (state->late_entry_mi_fragment[slot][5][2] << 4) | (state->late_entry_mi_fragment[slot][6][2] << 0);
|
|
||||||
|
|
||||||
for (j = 0; j < 3; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
mi_go_bits[i] = (( mi_test << (i+j*12) ) & 0x800000000) >> 35;
|
|
||||||
mi_go_bits[i+12] = (( go_test << (i+j*12) ) & 0x800000000) >> 35;
|
|
||||||
}
|
|
||||||
//execute golay decode and assign pass or fail to g
|
|
||||||
if ( Golay_24_12_decode(mi_go_bits) ) g[j] = 1;
|
|
||||||
else g[j] = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
mi_corrected = mi_corrected << 1;
|
|
||||||
mi_corrected |= mi_go_bits[i];
|
|
||||||
go_corrected = go_corrected << 1;
|
|
||||||
go_corrected |= mi_go_bits[i+12];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int mi_final = 0;
|
|
||||||
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
|
|
||||||
|
|
||||||
if (g[0] && g[1] && g[2])
|
|
||||||
{
|
|
||||||
if (slot == 0 && state->payload_algid != 0)
|
|
||||||
{
|
|
||||||
if (state->payload_mi != mi_final)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KCYN);
|
|
||||||
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
|
|
||||||
state->payload_mi = mi_final;
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (slot == 1 && state->payload_algidR != 0)
|
|
||||||
{
|
|
||||||
if (state->payload_miR != mi_final)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KCYN);
|
|
||||||
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
|
|
||||||
state->payload_miR = mi_final;
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->dropL = 256;
|
|
||||||
|
|
||||||
if (state->K1 != 0)
|
|
||||||
{
|
|
||||||
state->DMRvcL = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->payload_algid >= 0x21)
|
|
||||||
{
|
|
||||||
LFSR (state);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state->currentslot == 1)
|
|
||||||
{
|
|
||||||
state->dropR = 256;
|
|
||||||
|
|
||||||
if (state->K1 != 0)
|
|
||||||
{
|
|
||||||
state->DMRvcR = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->payload_algidR >= 0x21)
|
|
||||||
{
|
|
||||||
LFSR (state);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
state->dropL = 256;
|
|
||||||
state->dropR = 256;
|
|
||||||
state->DMRvcL = 0;
|
|
||||||
state->DMRvcR = 0;
|
|
||||||
state->payload_miP = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
710
src/dmr_ms.c
710
src/dmr_ms.c
@@ -1,58 +1,40 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_ms.c
|
|
||||||
* DMR MS/Simplex/Direct Mode Voice Handling and Data Gathering Routines
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
#include "dmr_const.h"
|
#include "dmr_const.h"
|
||||||
|
|
||||||
//A subroutine for processing MS voice
|
//A subroutine for processing MS voice
|
||||||
void dmrMS (dsd_opts * opts, dsd_state * state)
|
void dmrMS (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
//fprintf (stderr, "\n Quick DMR Voice Testing\n");
|
||||||
int i, j, k, l, dibit;
|
int i, j, k, l, dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24] = {0};
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr2[4][24] = {0};
|
||||||
char ambe_fr3[4][24];
|
char ambe_fr3[4][24] = {0};
|
||||||
char ambe_fr4[4][24];
|
char ambe_fr4[4][24] = {0};
|
||||||
|
char t_ambe_fr[4][24] = {9};
|
||||||
//memcpy of ambe_fr for late entry
|
char t_ambe_fr2[4][24] = {9};
|
||||||
char m1[4][24];
|
char t_ambe_fr3[4][24] = {9};
|
||||||
char m2[4][24];
|
|
||||||
char m3[4][24];
|
|
||||||
|
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
char sync[25];
|
char sync[25];
|
||||||
char syncdata[48];
|
char syncdata[48];
|
||||||
char EmbeddedSignalling[16];
|
//standalone inbound rc sync exists on dibits 48-96?
|
||||||
|
//or 44-92? of each potential payload, middle 10 ms or 30 ms frame
|
||||||
//cach
|
char rcsync[25];
|
||||||
char cachdata[25];
|
char rcsyncdata[96]; //96 bit RC sync data
|
||||||
int cachInterleave[24] =
|
char cachdata[13] = {0};
|
||||||
{0, 7, 8, 9, 1, 10,
|
int mutecurrentslot;
|
||||||
11, 12, 2, 13, 14,
|
int msMode;
|
||||||
15, 3, 16, 4, 17, 18,
|
char cc[4];
|
||||||
19, 5, 20, 21, 22, 6, 23
|
unsigned char EmbeddedSignalling[16];
|
||||||
};
|
unsigned char EmbeddedSignallingInverse[16];
|
||||||
|
int EmbeddedSignallingOk;
|
||||||
//cach tact bits
|
unsigned int internalcolorcode;
|
||||||
uint8_t tact_bits[7];
|
int internalslot;
|
||||||
|
int activeslot;
|
||||||
uint8_t emb_ok = 0;
|
char redundancyA[36];
|
||||||
uint8_t tact_okay = 0;
|
char redundancyB[36];
|
||||||
uint8_t cach_err = 0;
|
unsigned short int vc1;
|
||||||
uint8_t EmbeddedSignallingOk = 0;
|
unsigned short int vc2;
|
||||||
|
|
||||||
uint8_t internalslot;
|
|
||||||
uint8_t vc;
|
|
||||||
|
|
||||||
//dummy bits to pass to dburst for link control
|
|
||||||
uint8_t dummy_bits[196];
|
|
||||||
memset (dummy_bits, 0, sizeof (dummy_bits));
|
|
||||||
|
|
||||||
//add time to mirror printFrameSync
|
//add time to mirror printFrameSync
|
||||||
time_t now;
|
time_t now;
|
||||||
@@ -71,43 +53,39 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
return curr;
|
return curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
vc = 2;
|
//Init the color code status
|
||||||
|
state->color_code_ok = 0;
|
||||||
|
|
||||||
|
vc1 = 2;
|
||||||
|
vc2 = 2;
|
||||||
|
|
||||||
|
short int loop = 1;
|
||||||
short int skipcount = 0;
|
short int skipcount = 0;
|
||||||
|
|
||||||
//Hardset variables for MS/Mono
|
//Hardset variables for MS
|
||||||
state->currentslot = 0; //0
|
state->currentslot = 0; //0
|
||||||
|
internalslot = 0;
|
||||||
|
activeslot = 0;
|
||||||
|
|
||||||
//Note: Manual dibit inversion required here since I didn't seperate inverted return from normal return in framesync,
|
//Run Loop while the getting is good
|
||||||
//so getDibit doesn't know to invert it before it gets here
|
while (loop == 1) {
|
||||||
|
|
||||||
for (j = 0; j < 6; j++) {
|
short int m = 0;
|
||||||
state->dmrburstL = 16;
|
|
||||||
|
state->dmrburstL = 16; //Use 16 for Voice
|
||||||
|
|
||||||
for(i = 0; i < 12; i++)
|
for(i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
cachdata[i] = dibit;
|
cachdata[i] = dibit;
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
tact_bits[i] = cachdata[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tact_okay = 0;
|
|
||||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
|
||||||
if (tact_okay != 1)
|
|
||||||
{
|
|
||||||
//do nothing since we aren't loop locked forever.
|
|
||||||
}
|
|
||||||
|
|
||||||
//internalslot = tact_bits[1];
|
|
||||||
internalslot = 0;
|
|
||||||
|
|
||||||
//Setup for first AMBE Frame
|
//Setup for first AMBE Frame
|
||||||
//Interleave Schedule
|
//Interleave Schedule
|
||||||
w = rW;
|
w = rW;
|
||||||
@@ -119,10 +97,12 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i+12] = dibit;
|
state->dmr_stereo_payload[i+12] = dibit;
|
||||||
|
redundancyA[i] = dibit;
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -133,6 +113,20 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//check for repetitive data if caught in a 'no carrier' loop? Just picking random values.
|
||||||
|
//this will test for no carrier (input signal) and return us to no sync state if necessary
|
||||||
|
if (redundancyA[16] == redundancyB[16] && redundancyA[27] == redundancyB[27] &&
|
||||||
|
redundancyA[01] == redundancyB[01] && redundancyA[32] == redundancyB[32] &&
|
||||||
|
redundancyA[03] == redundancyB[03] && redundancyA[33] == redundancyB[33] &&
|
||||||
|
redundancyA[13] == redundancyB[13] && redundancyA[07] == redundancyB[07] )
|
||||||
|
{
|
||||||
|
//goto END;
|
||||||
|
}
|
||||||
|
for(i = 0; i < 36; i++)
|
||||||
|
{
|
||||||
|
redundancyB[i] = redundancyA[i];
|
||||||
|
}
|
||||||
|
|
||||||
//Setup for Second AMBE Frame
|
//Setup for Second AMBE Frame
|
||||||
//Interleave Schedule
|
//Interleave Schedule
|
||||||
w = rW;
|
w = rW;
|
||||||
@@ -144,8 +138,10 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i+48] = dibit;
|
state->dmr_stereo_payload[i+48] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
@@ -161,37 +157,85 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 24; i++)
|
for(i = 0; i < 24; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i+66] = dibit;
|
state->dmr_stereo_payload[i+66] = dibit;
|
||||||
|
|
||||||
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
sync[i] = (dibit | 1) + 48;
|
sync[i] = (dibit | 1) + 48;
|
||||||
|
|
||||||
// load the superframe to do embedded signal processing
|
if (i < 4 || i > 19) //
|
||||||
if(vc > 1 && vc < 6) //grab on vc1 values 2-5 B C D and E
|
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
EmbeddedSignalling[2*i] = (1 & (dibit >> 1)); // bit 1
|
||||||
state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 0
|
EmbeddedSignalling[2*i+1] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the superframe to do the voiceburstsync processing
|
||||||
|
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
|
||||||
|
{
|
||||||
|
/* Time slot 1 superframe buffer filling => SYNC data */
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2+1] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
|
||||||
|
{
|
||||||
|
/* Time slot 2 superframe buffer filling => SYNC data */
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2+1] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sync[24] = 0;
|
sync[24] = 0;
|
||||||
EmbeddedSignallingOk = -1;
|
EmbeddedSignallingOk = -1;
|
||||||
|
|
||||||
|
// for (i = 0; i < 16; i++)
|
||||||
|
// {
|
||||||
|
// EmbeddedSignallingInverse[i] = EmbeddedSignalling[i] ^ 1;
|
||||||
|
// }
|
||||||
|
|
||||||
if(QR_16_7_6_decode(EmbeddedSignalling))
|
if(QR_16_7_6_decode(EmbeddedSignalling))
|
||||||
{
|
{
|
||||||
EmbeddedSignallingOk = 1;
|
EmbeddedSignallingOk = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//test for inverted signal - this should move to data, and have a slottype check here perhaps?
|
||||||
|
// else if (QR_16_7_6_decode(EmbeddedSignallingInverse))
|
||||||
|
// {
|
||||||
|
// if (opts->inverted_dmr == 0)
|
||||||
|
// {
|
||||||
|
// opts->inverted_dmr = 1;
|
||||||
|
// }
|
||||||
|
// else opts->inverted_dmr = 0;
|
||||||
|
// goto END;
|
||||||
|
// }
|
||||||
|
|
||||||
|
internalcolorcode = 69; //set so we know if this value is being set properly
|
||||||
|
if(EmbeddedSignallingOk == 1)
|
||||||
|
{
|
||||||
|
state->color_code = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||||
|
internalcolorcode = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||||
|
state->color_code_ok = EmbeddedSignallingOk;
|
||||||
|
//Power Indicator, not the other PI (header)
|
||||||
|
state->PI = (unsigned int)EmbeddedSignalling[4];
|
||||||
|
state->PI_ok = EmbeddedSignallingOk;
|
||||||
|
//Link Control Start Stop Indicator
|
||||||
|
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
||||||
|
state->LCSS_ok = EmbeddedSignallingOk;
|
||||||
|
|
||||||
|
}
|
||||||
|
//else skipcount++;
|
||||||
|
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i+90] = dibit;
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
@@ -201,6 +245,12 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
y++;
|
y++;
|
||||||
z++;
|
z++;
|
||||||
|
|
||||||
|
//WIP for RC inbound sync data, part 2, second 24 bits (12 dibits)
|
||||||
|
if (i <= 12)
|
||||||
|
{
|
||||||
|
rcsyncdata[i+12] = (dibit | 1) + 48; //test and double check positions for this sync data
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setup for Third AMBE Frame
|
//Setup for Third AMBE Frame
|
||||||
@@ -214,8 +264,10 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i+108] = dibit;
|
state->dmr_stereo_payload[i+108] = dibit;
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||||
@@ -225,67 +277,229 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
y++;
|
y++;
|
||||||
z++;
|
z++;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->dmr_ms_mode = 1;
|
if ( ((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) && opts->inverted_dmr == 0) ||
|
||||||
|
((strcmp (sync, DMR_MS_DATA_SYNC) == 0) && opts->inverted_dmr == 1) )
|
||||||
|
|
||||||
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
|
||||||
|
|
||||||
//copy ambe_fr frames first, running process mbe will correct them,
|
|
||||||
//but this also leads to issues extracting good le mi values when
|
|
||||||
//we go to do correction on them there too
|
|
||||||
memcpy (m1, ambe_fr, sizeof(m1));
|
|
||||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
|
||||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
|
||||||
|
|
||||||
if (state->directmode == 0)
|
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
if (internalslot == 0)
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
vc1 = 1;
|
||||||
|
state->DMRvcL = 0; //reset here if jitter/sync forces reset
|
||||||
|
fprintf (stderr, "MS Slot 1 Voice Sync \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (internalslot == 1)
|
||||||
|
{
|
||||||
|
vc2 = 1;
|
||||||
|
state->DMRvcR = 0; //reset here if jitter/sync forces reset
|
||||||
|
}
|
||||||
|
|
||||||
|
state->dmr_ms_mode = 1; //set to 1 here??
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
state->dmr_ms_mode = 1; //set to 1 here?
|
||||||
|
|
||||||
|
//don't know if we will catch an RC sync in here or not, should be on VC6 if it occurs
|
||||||
|
if ( (strcmp (rcsync, DMR_RC_DATA_SYNC) == 0) || (strcmp (sync, DMR_RC_DATA_SYNC) == 0) ) //should be in same position as sync
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
|
fprintf (stderr,"%s ", getTime());
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
if (internalslot == 0)
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
{
|
||||||
|
sprintf(state->slot1light, "[slot1]");
|
||||||
|
sprintf(state->slot2light, " slot2 ");
|
||||||
|
//fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
|
||||||
|
//test with vc1 reset disabled, if all is well, leave disabled
|
||||||
|
//vc1 = 1;
|
||||||
|
}
|
||||||
|
if (internalslot == 1)
|
||||||
|
{
|
||||||
|
sprintf(state->slot2light, "[slot2]");
|
||||||
|
sprintf(state->slot1light, " slot1 ");
|
||||||
|
//fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
|
||||||
|
//test with vc1 reset disabled, if all is well, leave disabled
|
||||||
|
//vc2 = 1;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n "); //21 spaces to line up the sync
|
||||||
|
//processDMRdata (opts, state);
|
||||||
|
state->dmr_ms_rc = 1; //don't know what to do with this, if anything...
|
||||||
|
goto SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vc == 6)
|
//check for sync pattern here after collected the rest of the payload, decide what to do with it
|
||||||
|
//if ( (strcmp (sync, DMR_MS_DATA_SYNC) == 0) )
|
||||||
|
//fixed to compensate for inverted signal
|
||||||
|
if ( ((strcmp (sync, DMR_MS_DATA_SYNC) == 0) && opts->inverted_dmr == 0) ||
|
||||||
|
((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) && opts->inverted_dmr == 1) )
|
||||||
{
|
{
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
fprintf (stderr,"%s ", getTime());
|
||||||
fprintf (stderr, "\n");
|
if (internalslot == 0)
|
||||||
dmr_alg_refresh (opts, state);
|
{
|
||||||
|
sprintf(state->slot1light, "[slot1]");
|
||||||
|
sprintf(state->slot2light, " slot2 ");
|
||||||
|
//fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | MS Data ", state->color_code);
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
//test with vc1 reset disabled, if all is well, leave disabled
|
||||||
|
//vc1 = 1;
|
||||||
|
}
|
||||||
|
if (internalslot == 1)
|
||||||
|
{
|
||||||
|
sprintf(state->slot2light, "[slot2]");
|
||||||
|
sprintf(state->slot1light, " slot1 ");
|
||||||
|
//fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | Data ", state->color_code);
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR MS/DM MODE ");
|
||||||
|
//test with vc1 reset disabled, if all is well, leave disabled
|
||||||
|
//vc2 = 1;
|
||||||
|
}
|
||||||
|
//fprintf (stderr, "\n "); //21 spaces to line up the sync
|
||||||
|
//fprintf (stderr, " "); //21 spaces to line up the sync
|
||||||
|
state->dmr_ms_mode = 1; //set to 1 here??
|
||||||
|
processDMRdata (opts, state);
|
||||||
|
skipcount++;
|
||||||
|
//goto SKIP;
|
||||||
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
//collect the mi fragment
|
if( ( ( ((strcmp (sync, DMR_MS_DATA_SYNC) != 0) && opts->inverted_dmr == 0) ||
|
||||||
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
|
((strcmp (sync, DMR_MS_VOICE_SYNC) != 0) && opts->inverted_dmr == 1) ) )
|
||||||
|
&& internalslot == activeslot && vc1 < 7)
|
||||||
|
|
||||||
//errors in ms/mono since we skip the other slot
|
{
|
||||||
// cach_err = dmr_cach (opts, state, cachdata);
|
|
||||||
|
|
||||||
//update voice sync time for trunking purposes (particularly Con+)
|
skipcount = 0; //reset skip count if processing voice frames
|
||||||
state->last_vc_sync_time = time(NULL);
|
fprintf (stderr,"%s ", getTime());
|
||||||
|
if (internalslot == 0 && opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | VC%d ", state->color_code, vc1);
|
||||||
|
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
vc++;
|
if (internalslot == 0 && opts->inverted_dmr == 1)
|
||||||
//this is necessary because we need to skip and collect dibits, not just skip them
|
{
|
||||||
if (vc > 6) goto END;
|
fprintf (stderr,"Sync: -DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | VC%d ", state->color_code, vc1);
|
||||||
|
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues)
|
||||||
|
{
|
||||||
|
//process voice burst
|
||||||
|
ProcessVoiceBurstSync(opts, state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
state->dmr_ms_mode == 1;
|
||||||
|
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
||||||
|
|
||||||
|
//state->directmode = 0; //flag off for testing
|
||||||
|
|
||||||
|
if (state->directmode == 0)
|
||||||
|
{
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vc1 == 6 && state->payload_algid >= 21)
|
||||||
|
{
|
||||||
|
state->DMRvcL = 0;
|
||||||
|
if (state->payload_mi != 0 && state->payload_algid >= 0x21)
|
||||||
|
{
|
||||||
|
LFSR(state);
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vc1++;
|
||||||
|
//6 seems optimal for MS, allows to quickly exit and resync if wonky or something
|
||||||
|
if ( (vc1 > 6) || skipcount > 2) //escape to find new sync if deadspins without a renewed voice frame sync inside of loop
|
||||||
|
{
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
SKIP:
|
||||||
skipDibit (opts, state, 144); //skip to next tdma channel
|
skipDibit (opts, state, 144); //skip to next tdma channel
|
||||||
state->dmr_ms_rc = 0;
|
state->dmr_ms_rc = 0;
|
||||||
|
|
||||||
//since we are in a loop, run ncursesPrinter here
|
//since we are in a repetitive loop, run ncursesPrinter here
|
||||||
if (opts->use_ncurses_terminal == 1)
|
if (opts->use_ncurses_terminal == 1)
|
||||||
{
|
{
|
||||||
ncursesPrinter(opts, state);
|
ncursesPrinter(opts, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end loop
|
} // end while loop
|
||||||
|
//reset these variables when exiting MS/DM mode.
|
||||||
END:
|
END:
|
||||||
//get first half payload dibits and store them in the payload for the next repitition
|
//get first half payload dibits and store them in the payload for the next repitition
|
||||||
skipDibit (opts, state, 144); //should we have two of these?
|
skipDibit (opts, state, 144);
|
||||||
|
|
||||||
//CACH + First Half Payload = 12 + 54
|
//CACH + First Half Payload = 12 + 54
|
||||||
for (i = 0; i < 66; i++) //66
|
for (i = 0; i < 66; i++) //66
|
||||||
{
|
{
|
||||||
@@ -302,7 +516,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
state->dmr_ms_mode = 0;
|
state->dmr_ms_mode = 0;
|
||||||
state->dmr_ms_rc = 0;
|
state->dmr_ms_rc = 0;
|
||||||
state->directmode = 0; //flag off
|
state->directmode = 0; //flag off
|
||||||
|
state->DMRvcL = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,39 +525,19 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
int i, j, k, l, dibit;
|
int i, j, k, l, dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
|
char ambe_fr[4][24] = {0};
|
||||||
char ambe_fr[4][24];
|
char ambe_fr2[4][24] = {0};
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr3[4][24] = {0};
|
||||||
char ambe_fr3[4][24];
|
char ambe_fr4[4][24] = {0}; //shim for playing back direct mode samples where ambe1 is bad, duplicate 2 to 4 and play both to smooth audio
|
||||||
char ambe_fr4[4][24]; //shim for crappy tdma direct, need more samples to see if this is really a necessary shim or not
|
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
|
||||||
char m1[4][24];
|
|
||||||
char m2[4][24];
|
|
||||||
char m3[4][24];
|
|
||||||
|
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
char sync[25];
|
char sync[25];
|
||||||
char syncdata[48];
|
char syncdata[48];
|
||||||
|
char cachdata[13] = {0};
|
||||||
//cach
|
|
||||||
char cachdata[25];
|
|
||||||
int cachInterleave[24] =
|
|
||||||
{0, 7, 8, 9, 1, 10,
|
|
||||||
11, 12, 2, 13, 14,
|
|
||||||
15, 3, 16, 4, 17, 18,
|
|
||||||
19, 5, 20, 21, 22, 6, 23
|
|
||||||
};
|
|
||||||
|
|
||||||
int mutecurrentslot;
|
int mutecurrentslot;
|
||||||
int msMode;
|
int msMode;
|
||||||
char cc[4];
|
char cc[4];
|
||||||
char EmbeddedSignalling[16];
|
unsigned char EmbeddedSignalling[16];
|
||||||
|
unsigned int EmbeddedSignallingOk;
|
||||||
uint8_t emb_ok = 0;
|
|
||||||
uint8_t tact_okay = 0;
|
|
||||||
uint8_t cach_err = 0;
|
|
||||||
uint8_t EmbeddedSignallingOk = 0;
|
|
||||||
|
|
||||||
//add time to mirror sync
|
//add time to mirror sync
|
||||||
time_t now;
|
time_t now;
|
||||||
@@ -361,8 +555,8 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
return curr;
|
return curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->dmrburstL = 16;
|
state->dmrburstL = 16; //Use 16 for Voice?
|
||||||
state->currentslot = 0; //force to slot 0
|
state->currentslot = 0; //force to slot 0
|
||||||
|
|
||||||
dibit_p = state->dmr_payload_p - 90;
|
dibit_p = state->dmr_payload_p - 90;
|
||||||
@@ -400,7 +594,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2);
|
||||||
}
|
}
|
||||||
state->dmr_stereo_payload[i+12] = dibit;
|
//state->dmr_stereo_payload[i+12] = dibit;
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -436,11 +630,12 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
z++;
|
z++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2);
|
||||||
@@ -480,28 +675,86 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
z++;
|
z++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//work around to set erroneous PI header values to 0 if K is active
|
||||||
|
if (state->K > 0 || state->K1 > 0)
|
||||||
|
{
|
||||||
|
state->payload_keyid = 0; //just for testing
|
||||||
|
state->payload_keyidR = 0; //just for testing
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr,"%s ", getTime());
|
||||||
|
|
||||||
fprintf (stderr, "%s ", getTime());
|
|
||||||
if (opts->inverted_dmr == 0)
|
if (opts->inverted_dmr == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
fprintf (stderr,"Sync: +DMR MS/DM MODE | Frame Sync | DMRSTEREO | VC1 ");
|
||||||
|
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||||
|
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x10) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
if ( (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||||
|
(state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x68) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: -DMR MS/DM MODE | Frame Sync | DMRSTEREO | VC1 ");
|
||||||
|
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||||
|
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x10) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
if ( (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||||
|
(state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x68) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %016llX", state->K1);
|
||||||
|
if (state->K2 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K2);
|
||||||
|
}
|
||||||
|
if (state->K3 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K3);
|
||||||
|
}
|
||||||
|
if (state->K4 != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %016llX", state->K4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
|
||||||
|
|
||||||
fprintf (stderr, "| VC* ");
|
state->dmr_ms_mode = 1;
|
||||||
fprintf (stderr, "\n");
|
state->DMRvcL = 0;
|
||||||
|
|
||||||
//alg reset
|
|
||||||
//dmr_alg_reset (opts, state);
|
|
||||||
|
|
||||||
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
||||||
|
|
||||||
//copy ambe_fr frames first, running process mbe will correct them,
|
//state->directmode = 0; //flag off for testing samples
|
||||||
//but this also leads to issues extracting good le mi values when
|
|
||||||
//we go to do correction on them there too
|
|
||||||
memcpy (m1, ambe_fr, sizeof(m1));
|
|
||||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
|
||||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
|
||||||
|
|
||||||
if (state->directmode == 0)
|
if (state->directmode == 0)
|
||||||
{
|
{
|
||||||
@@ -515,24 +768,25 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
//collect the mi fragment
|
|
||||||
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
|
|
||||||
|
|
||||||
//errors due to skipping other slot
|
|
||||||
// cach_err = dmr_cach (opts, state, cachdata);
|
|
||||||
|
|
||||||
skipDibit (opts, state, 144); //skip to next TDMA slot
|
skipDibit (opts, state, 144); //skip to next TDMA slot
|
||||||
dmrMS (opts, state); //bootstrap into full TDMA frame
|
dmrMS (opts, state); //bootstrap into full TDMA frame
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//simplied to a simple data collector, and then passed on to dmr_data_sync for the usual processing
|
|
||||||
void dmrMSData (dsd_opts * opts, dsd_state * state)
|
void dmrMSData (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
int i, b, c;
|
int i, b, c;
|
||||||
int dibit;
|
int dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
|
unsigned int burst;
|
||||||
|
|
||||||
|
unsigned char MSCach[24] = {0}; //12 dibits = 24 bits
|
||||||
|
unsigned char MSSync[48] = {0}; //24 dibits = 48 bits
|
||||||
|
unsigned char MSSlot[20] = {0}; //10 dibits = 20 bits
|
||||||
|
unsigned char MSBurst[5] = {0};
|
||||||
|
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
|
||||||
|
|
||||||
//add time to mirror sync
|
//add time to mirror sync
|
||||||
time_t now;
|
time_t now;
|
||||||
@@ -550,7 +804,8 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
return curr;
|
return curr;
|
||||||
}
|
}
|
||||||
|
b = 0;
|
||||||
|
c = 0;
|
||||||
|
|
||||||
//CACH + First Half Payload + Sync = 12 + 54 + 24
|
//CACH + First Half Payload + Sync = 12 + 54 + 24
|
||||||
dibit_p = state->dmr_payload_p - 90;
|
dibit_p = state->dmr_payload_p - 90;
|
||||||
@@ -558,25 +813,94 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
dibit_p++;
|
dibit_p++;
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
//load cach
|
||||||
|
if (i < 12)
|
||||||
|
{
|
||||||
|
MSCach[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
MSCach[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
//load slot, first half
|
||||||
|
if (i > 60 && i < 66)
|
||||||
|
{
|
||||||
|
MSSlot[(b*2)] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
MSSlot[(b*2)+1] = (1 & dibit); // bit
|
||||||
|
b++;
|
||||||
|
}
|
||||||
|
//load sync
|
||||||
|
if (i > 66)
|
||||||
|
{
|
||||||
|
MSSync[(c*2)] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
MSSync[(c*2)+1] = (1 & dibit); // bit 0
|
||||||
|
c++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 54; i++)
|
for (i = 0; i < 54; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
state->dmr_stereo_payload[i+90] = dibit;
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
|
//load slot, second half
|
||||||
|
if (i < 6)
|
||||||
|
{
|
||||||
|
MSSlot[(b*2)] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
MSSlot[(b*2)+1] = (1 & dibit); // bit
|
||||||
|
b++;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", getTime());
|
//state->directmode = 0; //flag off for testing
|
||||||
if (opts->inverted_dmr == 0)
|
|
||||||
|
//with FEC fix, now we can (hopefully) feel confident in running MS data
|
||||||
|
if (1 == 1) //opts->payload == 1 && state->directmode == 0
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
fprintf (stderr, "%s ", getTime());
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR MS/DM MODE ");
|
||||||
}
|
}
|
||||||
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
|
||||||
|
// decode and correct cach and slot
|
||||||
|
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
|
||||||
|
int cach_okay = -1;
|
||||||
|
if ( Hamming_7_4_decode (MSCach) )
|
||||||
|
{
|
||||||
|
cach_okay = 1;
|
||||||
|
}
|
||||||
|
if (cach_okay == 1)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, "CACH Okay ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//fprintf (stderr, "CACH ERR ");
|
||||||
|
if (opts->aggressive_framesync == 1) //may not worry about it on data
|
||||||
|
{
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int slot_okay = -1;
|
||||||
|
if(Golay_20_8_decode(MSSlot))
|
||||||
|
{
|
||||||
|
slot_okay = 1;
|
||||||
|
}
|
||||||
|
if (slot_okay == 1)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, "Slot Okay ");
|
||||||
|
}
|
||||||
|
else goto END;
|
||||||
|
|
||||||
|
|
||||||
sprintf(state->slot1light, "%s", "");
|
sprintf(state->slot1light, "%s", "");
|
||||||
@@ -586,23 +910,33 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
|
|||||||
state->dmr_stereo = 1;
|
state->dmr_stereo = 1;
|
||||||
state->dmr_ms_mode = 1;
|
state->dmr_ms_mode = 1;
|
||||||
|
|
||||||
dmr_data_sync (opts, state);
|
//with FEC fix, now we can (hopefully) feel confident in running MS data
|
||||||
|
if (1 == 1) //opts->payload == 1 && state->directmode == 0
|
||||||
|
{
|
||||||
|
processDMRdata (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
END:
|
||||||
|
if (slot_okay != 1 || cach_okay != 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "| ** CACH or Burst Type FEC ERR ** ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
state->dmr_stereo = 0;
|
state->dmr_stereo = 0;
|
||||||
state->dmr_ms_mode = 0;
|
state->dmr_ms_mode = 0;
|
||||||
state->directmode = 0; //flag off
|
state->directmode = 0; //flag off
|
||||||
|
|
||||||
//should just be loaded in the dmr_payload_buffer instead now
|
//get potential first half payload dibits and store them in the payload for the next repitition, MS voice or data.
|
||||||
for (i = 0; i < 144; i++) //66
|
skipDibit (opts, state, 144);
|
||||||
{
|
|
||||||
dibit = getDibit(opts, state);
|
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
|
||||||
}
|
|
||||||
//CACH + First Half Payload = 12 + 54
|
//CACH + First Half Payload = 12 + 54
|
||||||
for (i = 0; i < 66; i++) //66
|
for (i = 0; i < 66; i++) //66
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
414
src/dmr_pdu.c
414
src/dmr_pdu.c
@@ -1,414 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_bs.c
|
|
||||||
* DMR Data (1/2, 3/4, 1) PDU Decoding
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
char * getTimeL(void) //get pretty hh:mm:ss timestamp
|
|
||||||
{
|
|
||||||
time_t t = time(NULL);
|
|
||||||
|
|
||||||
char * curr;
|
|
||||||
char * stamp = asctime(localtime( & t));
|
|
||||||
|
|
||||||
curr = strtok(stamp, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
|
|
||||||
return curr;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * getDateL(void) {
|
|
||||||
char datename[120];
|
|
||||||
char * curr2;
|
|
||||||
struct tm * to;
|
|
||||||
time_t t;
|
|
||||||
t = time(NULL);
|
|
||||||
to = localtime( & t);
|
|
||||||
strftime(datename, sizeof(datename), "%Y/%m/%d", to);
|
|
||||||
curr2 = strtok(datename, " ");
|
|
||||||
return curr2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
|
||||||
{
|
|
||||||
|
|
||||||
uint8_t message_len = 0;
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
uint8_t blocks = state->data_header_blocks[slot];
|
|
||||||
uint8_t padding = state->data_header_padding[slot];
|
|
||||||
uint8_t lrrp_conf = 0;
|
|
||||||
|
|
||||||
//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);
|
|
||||||
|
|
||||||
//maybe one day we will have more things to do here
|
|
||||||
state->data_conf_data[slot] = 0; //flag off confirmed data after processing it
|
|
||||||
state->data_p_head[slot] = 0; //flag off prop_head data after processing it
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//The contents of this function are mostly my reversed engineered efforts by observing DSDPlus output and matching data bytes
|
|
||||||
//combined with a few external sources such as OK-DMR for some token values and extra data values (rad and alt)
|
|
||||||
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
|
|
||||||
uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
uint16_t message_len = 0;
|
|
||||||
uint8_t slot = state->currentslot;
|
|
||||||
uint8_t blocks = state->data_header_blocks[slot];
|
|
||||||
uint8_t padding = state->data_header_padding[slot];
|
|
||||||
uint8_t lrrp_confidence = 0; //variable to increment based on number of tokens found, the more, the higher the confidence level
|
|
||||||
|
|
||||||
//source/dest and ports
|
|
||||||
uint32_t source = 0;
|
|
||||||
uint32_t dest = 0;
|
|
||||||
uint16_t port_s = 0;
|
|
||||||
uint16_t port_d = 0;
|
|
||||||
|
|
||||||
//time
|
|
||||||
uint16_t year = 0;
|
|
||||||
uint16_t month = 0;
|
|
||||||
uint16_t day = 0;
|
|
||||||
uint16_t hour = 0;
|
|
||||||
uint16_t minute = 0;
|
|
||||||
uint16_t second = 0;
|
|
||||||
|
|
||||||
//lat-long-radius-alt
|
|
||||||
uint32_t lat = 0;
|
|
||||||
uint8_t lat_sign = 0;
|
|
||||||
uint32_t lon = 0;
|
|
||||||
uint16_t rad = 0;
|
|
||||||
uint8_t alt = 0;
|
|
||||||
double lat_unit = (double)180/(double)4294967295;
|
|
||||||
double lon_unit = (double)360/(double)4294967295;
|
|
||||||
|
|
||||||
//speed/velocity
|
|
||||||
uint16_t vel = 0;
|
|
||||||
double velocity = 0;
|
|
||||||
uint8_t vel_set = 0;
|
|
||||||
|
|
||||||
//track, direction, degrees
|
|
||||||
uint8_t degrees = 0;
|
|
||||||
uint8_t deg_set = 0;
|
|
||||||
|
|
||||||
//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++)
|
|
||||||
{
|
|
||||||
|
|
||||||
switch(DMR_PDU[i]){
|
|
||||||
case 0x0C: //LRRP_TriggeredLocationReport
|
|
||||||
if (source == 0)
|
|
||||||
{
|
|
||||||
source = (DMR_PDU[i+1] << 16 ) + (DMR_PDU[i+2] << 8) + DMR_PDU[i+3];
|
|
||||||
dest = (DMR_PDU[i+5] << 16 ) + (DMR_PDU[i+6] << 8) + DMR_PDU[i+7];
|
|
||||||
port_s = (DMR_PDU[i+8] << 8) + DMR_PDU[i+9];
|
|
||||||
port_d = (DMR_PDU[i+10] << 8) + DMR_PDU[i+11];
|
|
||||||
i += 11;
|
|
||||||
lrrp_confidence++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
//may need more 'report' types to trigger various scenarios
|
|
||||||
//disable all but 0x1F if issues or falsing happens often
|
|
||||||
//case 0x1D: //ARRP_TriggeredInformationAnswer_NCDT
|
|
||||||
//case 0x1E: //ARRP_TriggeredInformationReport = FALSE?
|
|
||||||
//case 0x25: //ARRP_UnsolicitedInformationReport_NCDT = (0x25, True, "")
|
|
||||||
//case 0x26: //ARRP_UnsolicitedInformationReport_NCDT
|
|
||||||
//case 0x13: //LRRP_UnsolicitedLocationReport_NCDT
|
|
||||||
//case 0x15: //LRRP_LocationProtocolReport_NCDT
|
|
||||||
//case 0x21: //ARRP_TriggeredInformationStopRequest_NCDT
|
|
||||||
case 0x1F: //ARRP_TriggeredInformationReport_NCDT
|
|
||||||
if (report == 0)
|
|
||||||
{
|
|
||||||
report = DMR_PDU[i];
|
|
||||||
i += 1;
|
|
||||||
lrrp_confidence++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
//0x0D always seems to follow 0x1F or other 'report' types two/three bytes later
|
|
||||||
case 0x0D: //message len indicator //LRRP_TriggeredLocationReport_NCDT
|
|
||||||
if (report > 0 && message_len == 0)
|
|
||||||
{
|
|
||||||
message_len = DMR_PDU[i+1];
|
|
||||||
i += 1;
|
|
||||||
lrrp_confidence++;
|
|
||||||
}
|
|
||||||
else if (message_len == 0)
|
|
||||||
{
|
|
||||||
if (i > 3)
|
|
||||||
{
|
|
||||||
if (DMR_PDU[i-3] > 0) pot_report = DMR_PDU[i-3];
|
|
||||||
if (pot_report < 0x27)
|
|
||||||
{
|
|
||||||
report = pot_report;
|
|
||||||
message_len = DMR_PDU[i+1];
|
|
||||||
i += 1;
|
|
||||||
//no lrrp_confidence on speculative reporting, but will print it as such.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
//RESULT TOKEN or Request ID!
|
|
||||||
case 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?
|
|
||||||
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 );
|
|
||||||
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
|
|
||||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
|
||||||
else lat = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x34: //Time Interval Periodic Trigger (timestamp)
|
|
||||||
case 0x35: //Time Interval Periodic Trigger (timestamp)
|
|
||||||
if (message_len > 0 && year == 0)
|
|
||||||
{
|
|
||||||
year = (DMR_PDU[i+1] << 6) + (DMR_PDU[i+2] >> 2);
|
|
||||||
month = ((DMR_PDU[i+2] & 0x3) << 2) + ((DMR_PDU[i+3] & 0xC0) >> 6);
|
|
||||||
day = ((DMR_PDU[i+3] & 0x30) >> 1) + ((DMR_PDU[i+3] & 0x0E) >> 1);
|
|
||||||
hour = ((DMR_PDU[i+3] & 0x01) << 4) + ((DMR_PDU[i+4] & 0xF0) >> 4);
|
|
||||||
minute = ((DMR_PDU[i+4] & 0x0F) << 2) + ((DMR_PDU[i+5] & 0xC0) >> 6);
|
|
||||||
second = (DMR_PDU[i+5] & 0x3F);
|
|
||||||
i += 5;
|
|
||||||
//sanity check
|
|
||||||
if (year > 2000 && year <= 2025) lrrp_confidence++;
|
|
||||||
if (year > 2025 || year < 2000) year = 0; //needs future proofing
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x66: //point-2d
|
|
||||||
if (message_len > 0 && lat == 0)
|
|
||||||
{
|
|
||||||
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 );
|
|
||||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
|
||||||
i += 8; //check
|
|
||||||
lrrp_confidence++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x69: //point-3d
|
|
||||||
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 );
|
|
||||||
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?)
|
|
||||||
else lat = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x36: //protocol-version
|
|
||||||
i += 1;
|
|
||||||
//lrrp_confidence++;
|
|
||||||
break;
|
|
||||||
//speed/velocity
|
|
||||||
case 0x70: //speed-virt
|
|
||||||
case 0x6C: //speed-hor
|
|
||||||
if (message_len > 0 && vel_set == 0)
|
|
||||||
{
|
|
||||||
vel = (DMR_PDU[i+1] << 8) + DMR_PDU[i+2]; //don't think this is meant to be shifted, but doing it anyways for comparison
|
|
||||||
velocity = ( ((double)( (DMR_PDU[i+1] ) + DMR_PDU[i+2] )) / ( (double)128));
|
|
||||||
vel_set = 1;
|
|
||||||
i += 2;
|
|
||||||
lrrp_confidence++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x56: //direction-hor
|
|
||||||
if (message_len > 0 && deg_set == 0)
|
|
||||||
{
|
|
||||||
degrees = DMR_PDU[i+1] * 2;
|
|
||||||
deg_set = 1;
|
|
||||||
i += 1;
|
|
||||||
lrrp_confidence++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
//below all unknown
|
|
||||||
case 0x37: //result
|
|
||||||
case 0x38: //result
|
|
||||||
case 0x39: //result 'operations error'? attributes 0x22?
|
|
||||||
case 0x6B: //unknown-uint8
|
|
||||||
case 0x65: //lev-conf
|
|
||||||
default:
|
|
||||||
//do nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (report && message_len > 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
|
||||||
if (lrrp_confidence >= 4) //find the sweet magical number
|
|
||||||
{
|
|
||||||
//now we can open our lrrp file and write to it as well
|
|
||||||
FILE * pFile; //file pointer
|
|
||||||
if (opts->lrrp_file_output == 1)
|
|
||||||
{
|
|
||||||
//open file by name that is supplied in the ncurses terminal, or cli
|
|
||||||
pFile = fopen (opts->lrrp_out_file, "a");
|
|
||||||
//write current date/time if not present in LRRP data
|
|
||||||
if (!year) fprintf (pFile, "%s\t", getDateL() ); //current date, only add this IF no included timestamp in LRRP data?
|
|
||||||
if (!year) fprintf (pFile, "%s\t", getTimeL() ); //current timestamp, only add this IF no included timestamp in LRRP data?
|
|
||||||
if (year) fprintf (pFile, "%04d/%02d/%02d\t%02d:%02d:%02d\t", year, month, day, hour, minute, second); //add timestamp from decoded audio if available
|
|
||||||
//write data header source (or destination?) if not available in lrrp data
|
|
||||||
if (!source) fprintf (pFile, "%08lld\t", state->dmr_lrrp_source[state->currentslot]); //source address from data header
|
|
||||||
if (source) fprintf (pFile, "%08d\t", source); //add source form decoded audio if available, else its from the header
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pot_report)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
|
||||||
}
|
|
||||||
if (report)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Report: 0x%02X", report);
|
|
||||||
if (report == 0x1F) fprintf (stderr, " ARRP_TriggeredInformationReport_NCDT "); //customize later when more is learned
|
|
||||||
if (report == 0x21) fprintf (stderr, " ARRP_TriggeredInformationStopRequest_NCDT ");
|
|
||||||
if (report == 0x22) fprintf (stderr, " ARRP_TriggeredInformationStopAnswer ");
|
|
||||||
if (report == 0x25) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
|
|
||||||
if (report == 0x26) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
|
|
||||||
if (report == 0x27) fprintf (stderr, " ARRP_InformationProtocolRequest_NCDT ");
|
|
||||||
if (report == 0x13) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
|
|
||||||
if (report == 0x15) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
|
|
||||||
|
|
||||||
}
|
|
||||||
if (source)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Source: %08d - %04d", source, port_s);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Destination: %08d - %04d", dest, port_d);
|
|
||||||
|
|
||||||
}
|
|
||||||
if (year)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " LRRP - Time: ");
|
|
||||||
fprintf (stderr, " %04d.%02d.%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
|
|
||||||
|
|
||||||
}
|
|
||||||
if (lat)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (rad)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " LRRP - Radius: %d", 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?)
|
|
||||||
}
|
|
||||||
if (vel_set)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " LRRP - Velocity: %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity));
|
|
||||||
if (opts->lrrp_file_output == 1) fprintf (pFile, "%.3lf\t ", (velocity * 3.6) );
|
|
||||||
}
|
|
||||||
if (deg_set)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " LRRP - Track: %d Degrees", degrees);
|
|
||||||
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
|
||||||
}
|
|
||||||
|
|
||||||
//close open file
|
|
||||||
if (opts->lrrp_file_output == 1)
|
|
||||||
{
|
|
||||||
fprintf (pFile, "\n");
|
|
||||||
fclose (pFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
//save to array 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 (vel_set) sprintf (velstr, " %.4lf km/h", velocity * 3.6);
|
|
||||||
if (deg_set) sprintf (degstr, " %d deg", degrees);
|
|
||||||
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (pot_report)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
136
src/dmr_pi.c
136
src/dmr_pi.c
@@ -1,136 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dmr_pi.c
|
|
||||||
* DMR Privacy Indicator and LFSR Function
|
|
||||||
*
|
|
||||||
* LFSR code courtesy of https://github.com/mattames/LFSR/
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
|
||||||
{
|
|
||||||
|
|
||||||
if((IrrecoverableErrors == 0))
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (0 == 0)
|
|
||||||
{
|
|
||||||
state->payload_algid = PI_BYTE[0];
|
|
||||||
state->payload_keyid = PI_BYTE[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
|
||||||
if (state->payload_algid < 0x26)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s ", KYEL);
|
|
||||||
fprintf (stderr, "\n Slot 1");
|
|
||||||
fprintf (stderr, " DMR PI Header 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 >= 0x26)
|
|
||||||
{
|
|
||||||
if (0 == 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_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
|
||||||
if (state->payload_algidR < 0x26)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s ", KYEL);
|
|
||||||
fprintf (stderr, "\n Slot 2");
|
|
||||||
fprintf (stderr, " DMR PI Header 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 >= 0x26)
|
|
||||||
{
|
|
||||||
if (0== 0)
|
|
||||||
{
|
|
||||||
state->payload_algidR = 0;
|
|
||||||
state->payload_keyidR = 0;
|
|
||||||
state->payload_miR = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if((IrrecoverableErrors == 0) && CRCCorrect)
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " (PI CRC Okay)");
|
|
||||||
}
|
|
||||||
else if((IrrecoverableErrors == 0))
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " (PI FEC Okay)");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, (" (PI FEC ERR)"));
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void LFSR(dsd_state * state)
|
|
||||||
{
|
|
||||||
int lfsr = 0;
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
lfsr = state->payload_mi;
|
|
||||||
}
|
|
||||||
else lfsr = state->payload_miR;
|
|
||||||
|
|
||||||
uint8_t cnt = 0;
|
|
||||||
|
|
||||||
for(cnt=0;cnt<32;cnt++)
|
|
||||||
{
|
|
||||||
// Polynomial is C(x) = x^32 + x^4 + x^2 + 1
|
|
||||||
int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 0x1;
|
|
||||||
lfsr = (lfsr << 1) | (bit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
if (1 == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Slot 1");
|
|
||||||
fprintf (stderr, " DMR PI Continuation 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);
|
|
||||||
}
|
|
||||||
state->payload_mi = lfsr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->currentslot == 1)
|
|
||||||
{
|
|
||||||
if (1 == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Slot 2");
|
|
||||||
fprintf (stderr, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
|
||||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
state->payload_miR = lfsr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
395
src/dmr_process_voice.c
Normal file
395
src/dmr_process_voice.c
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 DSD Author
|
||||||
|
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
#include "dmr_const.h"
|
||||||
|
#include "p25p1_check_hdu.h"
|
||||||
|
|
||||||
|
void ProcessDMR (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
uint32_t i, j;
|
||||||
|
uint32_t Frame;
|
||||||
|
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameL = NULL;
|
||||||
|
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameR = NULL;
|
||||||
|
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameRC = NULL; //Reverse Test for MS Backwards
|
||||||
|
|
||||||
|
int *errs;
|
||||||
|
int *errs2;
|
||||||
|
unsigned long long int k;
|
||||||
|
int x;
|
||||||
|
k = 0;
|
||||||
|
|
||||||
|
TSVoiceSupFrameL = &state->TS1SuperFrame;
|
||||||
|
TSVoiceSupFrameR = &state->TS2SuperFrame;
|
||||||
|
|
||||||
|
int Pr[256] = {
|
||||||
|
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
|
||||||
|
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
|
||||||
|
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
|
||||||
|
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E, //31
|
||||||
|
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
|
||||||
|
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
|
||||||
|
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
|
||||||
|
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36, //63
|
||||||
|
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
|
||||||
|
0x614C, 0xAE4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
|
||||||
|
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
|
||||||
|
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57, //95
|
||||||
|
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
|
||||||
|
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
|
||||||
|
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
|
||||||
|
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F, //127
|
||||||
|
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
|
||||||
|
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
|
||||||
|
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
|
||||||
|
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897, //159
|
||||||
|
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
|
||||||
|
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
|
||||||
|
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
|
||||||
|
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF, //191
|
||||||
|
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
|
||||||
|
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
|
||||||
|
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
|
||||||
|
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3, //223
|
||||||
|
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
|
||||||
|
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
|
||||||
|
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
|
||||||
|
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if ( (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||||
|
(state->currentslot == 0 && state->K > 0 && state->M == 1) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
k = Pr[state->K];
|
||||||
|
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||||
|
|
||||||
|
for(Frame = 0; Frame < 6; Frame++)
|
||||||
|
{
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
for(j = 0; j < 48; j++)
|
||||||
|
{
|
||||||
|
x = ( ((k << j) & 0x800000000000) >> 47 );
|
||||||
|
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (state->currentslot == 1 && state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
||||||
|
(state->currentslot == 1 && state->K > 0 && state->M == 1) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " PrK %lld", state->K);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
k = Pr[state->K];
|
||||||
|
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||||
|
|
||||||
|
for(Frame = 0; Frame < 6; Frame++)
|
||||||
|
{
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
for(j = 0; j < 48; j++)
|
||||||
|
{
|
||||||
|
x = ( ((k << j) & 0x800000000000) >> 47 );
|
||||||
|
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (state->currentslot == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||||
|
(state->currentslot == 0 && state->K1 > 0 && state->M == 1) )
|
||||||
|
{
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
unsigned long long int k1 = state->K1;
|
||||||
|
unsigned long long int k2 = state->K2;
|
||||||
|
unsigned long long int k3 = state->K3;
|
||||||
|
unsigned long long int k4 = state->K4;
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %010llX", k1);
|
||||||
|
|
||||||
|
int T_Key[256] = {0};
|
||||||
|
int pN[882] = {0};
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (k2 == 0)
|
||||||
|
{
|
||||||
|
len = 39;
|
||||||
|
k1 = k1 << 24;
|
||||||
|
}
|
||||||
|
if (k2 != 0)
|
||||||
|
{
|
||||||
|
len = 127;
|
||||||
|
fprintf(stderr, " %016llX", k2);
|
||||||
|
}
|
||||||
|
if (k4 != 0)
|
||||||
|
{
|
||||||
|
len = 255;
|
||||||
|
fprintf(stderr, " %016llX", k3);
|
||||||
|
fprintf(stderr, " %016llX", k4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
for (i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
|
||||||
|
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
|
||||||
|
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
|
||||||
|
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 882; i++)
|
||||||
|
{
|
||||||
|
pN[i] = T_Key[pos];
|
||||||
|
pos++;
|
||||||
|
if (pos > len)
|
||||||
|
{
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
for(Frame = 0; Frame < 6; Frame++)
|
||||||
|
{
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
for(j = 0; j < 49; j++)
|
||||||
|
{
|
||||||
|
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= pN[pos];
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (state->currentslot == 1 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
|
||||||
|
(state->currentslot == 1 && state->K1 > 0 && state->M == 1) )
|
||||||
|
{
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
unsigned long long int k1 = state->K1;
|
||||||
|
unsigned long long int k2 = state->K2;
|
||||||
|
unsigned long long int k3 = state->K3;
|
||||||
|
unsigned long long int k4 = state->K4;
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf(stderr, " SPT %010llX", k1);
|
||||||
|
|
||||||
|
int T_Key[256] = {0};
|
||||||
|
int pN[882] = {0};
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (k2 == 0)
|
||||||
|
{
|
||||||
|
len = 39;
|
||||||
|
k1 = k1 << 24;
|
||||||
|
}
|
||||||
|
if (k2 != 0)
|
||||||
|
{
|
||||||
|
len = 127;
|
||||||
|
fprintf(stderr, " %016llX", k2);
|
||||||
|
}
|
||||||
|
if (k4 != 0)
|
||||||
|
{
|
||||||
|
len = 255;
|
||||||
|
fprintf(stderr, " %016llX", k3);
|
||||||
|
fprintf(stderr, " %016llX", k4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
for (i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
|
||||||
|
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
|
||||||
|
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
|
||||||
|
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 882; i++)
|
||||||
|
{
|
||||||
|
pN[i] = T_Key[pos];
|
||||||
|
pos++;
|
||||||
|
if (pos > len)
|
||||||
|
{
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
for(Frame = 0; Frame < 6; Frame++)
|
||||||
|
{
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
for(j = 0; j < 49; j++)
|
||||||
|
{
|
||||||
|
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= pN[pos];
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(Frame = 0; Frame < 6; Frame++) //6
|
||||||
|
{
|
||||||
|
/* 1 DMR frame contains 3 AMBE voice samples */
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
errs = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]);
|
||||||
|
errs2 = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]);
|
||||||
|
state->errs = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement
|
||||||
|
state->errs2 = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement
|
||||||
|
|
||||||
|
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str,
|
||||||
|
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i],
|
||||||
|
state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
PrintAMBEData(opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//quick enc toggle to determine whether or not to play enc traffic
|
||||||
|
int enc_bit = 0;
|
||||||
|
enc_bit = (state->dmr_so >> 6) & 0x1;
|
||||||
|
if (enc_bit == 1)
|
||||||
|
{
|
||||||
|
state->dmr_encL = 1;
|
||||||
|
}
|
||||||
|
else state->dmr_encL = 0;
|
||||||
|
|
||||||
|
if (state->K != 0 || state->K1 != 0)
|
||||||
|
{
|
||||||
|
state->dmr_encL = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//quick test to determine whether or not to run the next few steps below
|
||||||
|
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL)
|
||||||
|
{
|
||||||
|
saveAmbe2450Data (opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->debug_audio_errors += *errs2;
|
||||||
|
|
||||||
|
processAudio(opts, state);
|
||||||
|
|
||||||
|
if (opts->wav_out_f != NULL)
|
||||||
|
{
|
||||||
|
writeSynthesizedVoice (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->audio_out == 1) //opts->audio_out == 1
|
||||||
|
{
|
||||||
|
playSynthesizedVoice (opts, state);
|
||||||
|
}
|
||||||
|
} //second bracket for the test enc condition
|
||||||
|
} /* End for(i = 0; i < 3; i++) */
|
||||||
|
} /* End for(Frame = 0; Frame < 6; Frame++) */
|
||||||
|
} //End if Current Slot == 0
|
||||||
|
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
for(Frame = 0; Frame < 6; Frame++)
|
||||||
|
{
|
||||||
|
/* 1 DMR frame contains 3 AMBE voice samples */
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
errs = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]);
|
||||||
|
errs2 = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]);
|
||||||
|
state->errs = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement
|
||||||
|
state->errs2 = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement
|
||||||
|
|
||||||
|
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str,
|
||||||
|
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i],
|
||||||
|
state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
|
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
PrintAMBEData(opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//quick enc toggle to determine whether or not to play enc traffic
|
||||||
|
int enc_bit = 0;
|
||||||
|
enc_bit = (state->dmr_soR >> 6) & 0x1;
|
||||||
|
if (enc_bit == 1)
|
||||||
|
{
|
||||||
|
state->dmr_encR = 1;
|
||||||
|
}
|
||||||
|
else state->dmr_encR = 0;
|
||||||
|
|
||||||
|
if (state->K != 0 || state->K1 != 0)
|
||||||
|
{
|
||||||
|
state->dmr_encR = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//quick test to determine whether or not to run the next few steps below
|
||||||
|
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL)
|
||||||
|
{
|
||||||
|
saveAmbe2450Data (opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->debug_audio_errors += *errs2;
|
||||||
|
|
||||||
|
if (opts->audio_out == 1)
|
||||||
|
{
|
||||||
|
processAudio(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->wav_out_f != NULL)
|
||||||
|
{
|
||||||
|
writeSynthesizedVoice (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->audio_out == 1)
|
||||||
|
{
|
||||||
|
playSynthesizedVoice (opts, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /* End for(i = 0; i < 3; i++) */
|
||||||
|
} /* End for(Frame = 0; Frame < 6; Frame++) */
|
||||||
|
} //End if Current Slot == 1
|
||||||
|
|
||||||
|
|
||||||
|
} /* End ProcessDMR */
|
||||||
|
/* End of file */
|
||||||
2922
src/dmr_sync.c
Normal file
2922
src/dmr_sync.c
Normal file
File diff suppressed because it is too large
Load Diff
332
src/dmr_utils.c
332
src/dmr_utils.c
@@ -1,332 +0,0 @@
|
|||||||
//DMR CRC/Utility Functions
|
|
||||||
//Original File - dmr_sync.c
|
|
||||||
//ConvertBitIntoBytes, ComputeCrcCCITT, ComputeCrc5Bit, ComputeAndCorrectFullLinkControlCrc, CRC32, CRC9
|
|
||||||
//Original Source - https://github.com/LouisErigHerve/dsd
|
|
||||||
|
|
||||||
//Additional Functions
|
|
||||||
//Hamming17123, crc8, crc8ok functions
|
|
||||||
//Original Souce - https://github.com/boatbod/op25
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
//modified to accept variable payload size and len
|
|
||||||
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
|
||||||
/* Polynomial x^16 + x^12 + x^5 + 1
|
|
||||||
* Normal = 0x1021
|
|
||||||
* Reciprocal = 0x0811
|
|
||||||
* Reversed = 0x8408
|
|
||||||
* Reversed reciprocal = 0x8810 */
|
|
||||||
uint16_t Polynome = 0x1021;
|
|
||||||
for(i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
if(((CRC >> 15) & 1) ^ (buf[i] & 1))
|
|
||||||
{
|
|
||||||
CRC = (CRC << 1) ^ Polynome;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CRC <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invert the CRC */
|
|
||||||
CRC ^= 0xFFFF;
|
|
||||||
|
|
||||||
/* Return the CRC */
|
|
||||||
return CRC;
|
|
||||||
} /* End ComputeCrcCCITTd() */
|
|
||||||
|
|
||||||
// A Hamming (17,12,3) Check for completed SLC message
|
|
||||||
bool Hamming17123(uint8_t* d)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Calculate the checksum this column should have
|
|
||||||
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
|
|
||||||
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
|
|
||||||
bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
|
|
||||||
bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
|
|
||||||
bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
|
|
||||||
|
|
||||||
// Compare these with the actual bits
|
|
||||||
unsigned char n = 0x00U;
|
|
||||||
n |= (c0 != d[12]) ? 0x01U : 0x00U;
|
|
||||||
n |= (c1 != d[13]) ? 0x02U : 0x00U;
|
|
||||||
n |= (c2 != d[14]) ? 0x04U : 0x00U;
|
|
||||||
n |= (c3 != d[15]) ? 0x08U : 0x00U;
|
|
||||||
n |= (c4 != d[16]) ? 0x10U : 0x00U;
|
|
||||||
|
|
||||||
switch (n) {
|
|
||||||
// Parity bit errors
|
|
||||||
case 0x01U: d[12] = !d[12]; return true;
|
|
||||||
case 0x02U: d[13] = !d[13]; return true;
|
|
||||||
case 0x04U: d[14] = !d[14]; return true;
|
|
||||||
case 0x08U: d[15] = !d[15]; return true;
|
|
||||||
case 0x10U: d[16] = !d[16]; return true;
|
|
||||||
|
|
||||||
// Data bit errors
|
|
||||||
case 0x1BU: d[0] = !d[0]; return true;
|
|
||||||
case 0x1FU: d[1] = !d[1]; return true;
|
|
||||||
case 0x17U: d[2] = !d[2]; return true;
|
|
||||||
case 0x07U: d[3] = !d[3]; return true;
|
|
||||||
case 0x0EU: d[4] = !d[4]; return true;
|
|
||||||
case 0x1CU: d[5] = !d[5]; return true;
|
|
||||||
case 0x11U: d[6] = !d[6]; return true;
|
|
||||||
case 0x0BU: d[7] = !d[7]; return true;
|
|
||||||
case 0x16U: d[8] = !d[8]; return true;
|
|
||||||
case 0x05U: d[9] = !d[9]; return true;
|
|
||||||
case 0x0AU: d[10] = !d[10]; return true;
|
|
||||||
case 0x14U: d[11] = !d[11]; return true;
|
|
||||||
|
|
||||||
// No bit errors
|
|
||||||
case 0x00U: return true;
|
|
||||||
|
|
||||||
// Unrecoverable errors
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t crc8(uint8_t bits[], unsigned int len)
|
|
||||||
{
|
|
||||||
uint8_t crc=0;
|
|
||||||
unsigned int K = 8;
|
|
||||||
uint8_t poly[9] = {1,0,0,0,0,0,1,1,1}; // crc8 poly
|
|
||||||
uint8_t buf[256];
|
|
||||||
if (len+K > sizeof(buf)) {
|
|
||||||
//fprintf (stderr, "crc8: buffer length %u exceeds maximum %lu\n", len+K, sizeof(buf));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
memset (buf, 0, sizeof(buf));
|
|
||||||
for (unsigned int i=0; i<len; i++){
|
|
||||||
buf[i] = bits[i];
|
|
||||||
}
|
|
||||||
for (unsigned int i=0; i<len; i++)
|
|
||||||
if (buf[i])
|
|
||||||
for (unsigned int j=0; j<K+1; j++)
|
|
||||||
buf[i+j] ^= poly[j];
|
|
||||||
for (unsigned int i=0; i<K; i++){
|
|
||||||
crc = (crc << 1) + buf[len + i];
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool crc8_ok(uint8_t bits[], unsigned int len)
|
|
||||||
{
|
|
||||||
uint16_t crc = 0;
|
|
||||||
for (unsigned int i=0; i < 8; i++) {
|
|
||||||
crc = (crc << 1) + bits[len+i];
|
|
||||||
}
|
|
||||||
return (crc == crc8(bits,len));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @brief : This function compute the CRC-CCITT of the DMR data
|
|
||||||
* by using the polynomial x^16 + x^12 + x^5 + 1
|
|
||||||
*
|
|
||||||
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
|
||||||
*
|
|
||||||
* @return The 16 bit CRC
|
|
||||||
*/
|
|
||||||
|
|
||||||
uint16_t ComputeCrcCCITT(uint8_t * DMRData)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
|
||||||
/* Polynomial x^16 + x^12 + x^5 + 1
|
|
||||||
* Normal = 0x1021
|
|
||||||
* Reciprocal = 0x0811
|
|
||||||
* Reversed = 0x8408
|
|
||||||
* Reversed reciprocal = 0x8810 */
|
|
||||||
uint16_t Polynome = 0x1021;
|
|
||||||
for(i = 0; i < 80; i++)
|
|
||||||
{
|
|
||||||
if(((CRC >> 15) & 1) ^ (DMRData[i] & 1))
|
|
||||||
{
|
|
||||||
CRC = (CRC << 1) ^ Polynome;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CRC <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invert the CRC */
|
|
||||||
CRC ^= 0xFFFF;
|
|
||||||
|
|
||||||
/* Return the CRC */
|
|
||||||
return CRC;
|
|
||||||
} /* End ComputeCrcCCITT() */
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @brief : This function compute the CRC-24 bit of the full
|
|
||||||
* link control by using the Reed-Solomon(12,9) FEC
|
|
||||||
*
|
|
||||||
* @param FullLinkControlDataBytes : A buffer pointer of the DMR data bytes (12 bytes)
|
|
||||||
*
|
|
||||||
* @param CRCComputed : A 32 bit pointer where the computed CRC 24-bit will be stored
|
|
||||||
*
|
|
||||||
* @param CRCMask : The 24 bit CRC mask to apply
|
|
||||||
*
|
|
||||||
* @return 0 = CRC error
|
|
||||||
* 1 = CRC is correct
|
|
||||||
*/
|
|
||||||
|
|
||||||
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
rs_12_9_codeword_t VoiceLCHeaderStr;
|
|
||||||
rs_12_9_poly_t syndrome;
|
|
||||||
uint8_t errors_found = 0;
|
|
||||||
rs_12_9_correct_errors_result_t result = RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND;
|
|
||||||
uint32_t CrcIsCorrect = 0;
|
|
||||||
|
|
||||||
for(i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
VoiceLCHeaderStr.data[i] = FullLinkControlDataBytes[i];
|
|
||||||
|
|
||||||
/* Apply CRC mask on each 3 last bytes
|
|
||||||
* of the full link control */
|
|
||||||
if(i == 9)
|
|
||||||
{
|
|
||||||
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 16);
|
|
||||||
}
|
|
||||||
else if(i == 10)
|
|
||||||
{
|
|
||||||
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 8);
|
|
||||||
}
|
|
||||||
else if(i == 11)
|
|
||||||
{
|
|
||||||
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Nothing to do */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check and correct the full link LC control with Reed Solomon (12,9) FEC */
|
|
||||||
rs_12_9_calc_syndrome(&VoiceLCHeaderStr, &syndrome);
|
|
||||||
if(rs_12_9_check_syndrome(&syndrome) != 0) result = rs_12_9_correct_errors(&VoiceLCHeaderStr, &syndrome, &errors_found);
|
|
||||||
|
|
||||||
/* Reconstitue the CRC */
|
|
||||||
*CRCComputed = (uint32_t)((VoiceLCHeaderStr.data[9] << 16) & 0xFF0000);
|
|
||||||
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[10] << 8) & 0x00FF00);
|
|
||||||
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[11] << 0) & 0x0000FF);
|
|
||||||
|
|
||||||
if((result == RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND) ||
|
|
||||||
(result == RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED))
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "CRC OK : 0x%06X\n", *CRCComputed);
|
|
||||||
CrcIsCorrect = 1;
|
|
||||||
|
|
||||||
/* Reconstitue full link control data after FEC correction */
|
|
||||||
for(i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
FullLinkControlDataBytes[i] = VoiceLCHeaderStr.data[i];
|
|
||||||
|
|
||||||
/* Apply CRC mask on each 3 last bytes
|
|
||||||
* of the full link control */
|
|
||||||
if(i == 9)
|
|
||||||
{
|
|
||||||
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 16);
|
|
||||||
}
|
|
||||||
else if(i == 10)
|
|
||||||
{
|
|
||||||
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 8);
|
|
||||||
}
|
|
||||||
else if(i == 11)
|
|
||||||
{
|
|
||||||
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Nothing to do */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "CRC ERROR : 0x%06X\n", *CRCComputed);
|
|
||||||
CrcIsCorrect = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the CRC status */
|
|
||||||
return CrcIsCorrect;
|
|
||||||
} /* End ComputeAndCorrectFullLinkControlCrc() */
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @brief : This function compute the 5 bit CRC of the DMR voice burst data
|
|
||||||
* See ETSI TS 102 361-1 chapter B.3.11
|
|
||||||
*
|
|
||||||
* @param Input : A buffer pointer of the DMR data (72 bytes)
|
|
||||||
*
|
|
||||||
* @return The 5 bit CRC
|
|
||||||
*/
|
|
||||||
|
|
||||||
uint8_t ComputeCrc5Bit(uint8_t * DMRData)
|
|
||||||
{
|
|
||||||
uint32_t i, j, k;
|
|
||||||
uint8_t Buffer[9];
|
|
||||||
uint32_t Sum;
|
|
||||||
uint8_t CRC = 0;
|
|
||||||
|
|
||||||
/* Convert the 72 bit into 9 bytes */
|
|
||||||
k = 0;
|
|
||||||
for(i = 0; i < 9; i++)
|
|
||||||
{
|
|
||||||
Buffer[i] = 0;
|
|
||||||
for(j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
Buffer[i] = Buffer[i] << 1;
|
|
||||||
Buffer[i] = Buffer[i] | DMRData[k++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add all 9 bytes */
|
|
||||||
Sum = 0;
|
|
||||||
for(i = 0; i < 9; i++)
|
|
||||||
{
|
|
||||||
Sum += (uint32_t)Buffer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sum MOD 31 = CRC */
|
|
||||||
CRC = (uint8_t)(Sum % 31);
|
|
||||||
|
|
||||||
/* Return the CRC */
|
|
||||||
return CRC;
|
|
||||||
} /* End ComputeCrc5Bit() */
|
|
||||||
|
|
||||||
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
|
||||||
{
|
|
||||||
uint32_t Output = 0;
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
for(i = 0; i < BitLength; i++)
|
|
||||||
{
|
|
||||||
Output <<= 1;
|
|
||||||
Output |= (uint32_t)(BufferIn[i] & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Output;
|
|
||||||
} /* End ConvertBitIntoBytes() */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @brief : This function compute the CRC-9 of the DMR data
|
|
||||||
* by using the polynomial x^9 + x^6 + x^4 + x^3 + 1
|
|
||||||
*
|
|
||||||
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
|
||||||
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
|
||||||
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
|
||||||
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
|
||||||
*
|
|
||||||
* @param NbData : The number of bit to compute
|
|
||||||
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
|
||||||
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
|
||||||
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
|
||||||
*
|
|
||||||
* @return The 9 bit CRC
|
|
||||||
*/
|
|
||||||
660
src/dmr_voice.c
Normal file
660
src/dmr_voice.c
Normal file
@@ -0,0 +1,660 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 DSD Author
|
||||||
|
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
#include "dmr_const.h"
|
||||||
|
|
||||||
|
void processDMRvoice (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
// extracts AMBE frames from DMR frame
|
||||||
|
int i, j, k, l, dibit;
|
||||||
|
int *dibit_p;
|
||||||
|
char ambe_fr[4][24] = {0};
|
||||||
|
char ambe_fr2[4][24] = {0};
|
||||||
|
char ambe_fr3[4][24] = {0};
|
||||||
|
const int *w, *x, *y, *z;
|
||||||
|
char sync[25];
|
||||||
|
char syncdata[48];
|
||||||
|
char cachdata[13] = {0};
|
||||||
|
int mutecurrentslot;
|
||||||
|
int msMode;
|
||||||
|
char cc[4];
|
||||||
|
unsigned char EmbeddedSignalling[16];
|
||||||
|
unsigned int EmbeddedSignallingOk;
|
||||||
|
|
||||||
|
#ifdef DMR_DUMP
|
||||||
|
int k;
|
||||||
|
char syncbits[49];
|
||||||
|
char cachbits[25];12
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Init the superframe buffers */
|
||||||
|
memset(&state->TS1SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t));
|
||||||
|
memset(&state->TS2SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t));
|
||||||
|
|
||||||
|
/* Init the color code status */
|
||||||
|
state->color_code_ok = 0;
|
||||||
|
|
||||||
|
mutecurrentslot = 0;
|
||||||
|
msMode = 0;
|
||||||
|
|
||||||
|
dibit_p = state->dibit_buf_p - 144;
|
||||||
|
for(j = 0; j < 6; j++)
|
||||||
|
{
|
||||||
|
// 2nd half of previous slot
|
||||||
|
for(i = 0; i < 54; i++)
|
||||||
|
{
|
||||||
|
if(j > 0)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dibit = *dibit_p;
|
||||||
|
dibit_p++;
|
||||||
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CACH
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
if(j > 0)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dibit = *dibit_p;
|
||||||
|
dibit_p++;
|
||||||
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachdata[i] = dibit;
|
||||||
|
if(i == 2)
|
||||||
|
{
|
||||||
|
/* Change the current slot only if we are in relayed mode (not in direct mode) */
|
||||||
|
if(state->directmode == 0) state->currentslot = (1 & (dibit >> 1)); // bit 1
|
||||||
|
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
state->slot1light[0] = '[';
|
||||||
|
state->slot1light[6] = ']';
|
||||||
|
state->slot2light[0] = ' ';
|
||||||
|
state->slot2light[6] = ' ';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->slot2light[0] = '[';
|
||||||
|
state->slot2light[6] = ']';
|
||||||
|
state->slot1light[0] = ' ';
|
||||||
|
state->slot1light[6] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachdata[12] = 0;
|
||||||
|
|
||||||
|
//set state-dmrburstL and R here to a value for ncurses
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
state->dmrburstL = 16; //use 16 for Voice?
|
||||||
|
}
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
state->dmrburstR = 16; //Use 16 for Voice?
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DMR_DUMP
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
dibit = cachdata[i];
|
||||||
|
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||||
|
k++;
|
||||||
|
cachbits[k] = (1 & dibit) + 48; // bit 0
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
cachbits[24] = 0;
|
||||||
|
fprintf(stderr, "%s ", cachbits);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// current slot frame 1
|
||||||
|
w = rW;
|
||||||
|
x = rX;
|
||||||
|
y = rY;
|
||||||
|
z = rZ;
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 36; i++)
|
||||||
|
{
|
||||||
|
if(j > 0)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dibit = *dibit_p;
|
||||||
|
dibit_p++;
|
||||||
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Save the current frame in the appropriate superframe buffer */
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
/* Time slot 1 superframe buffer filling => RAW voice bit */
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
|
||||||
|
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Time slot 2 superframe buffer filling => RAW voice bit */
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
|
||||||
|
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
z++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// current slot frame 2 first half
|
||||||
|
w = rW;
|
||||||
|
x = rX;
|
||||||
|
y = rY;
|
||||||
|
z = rZ;
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 18; i++)
|
||||||
|
{
|
||||||
|
if(j > 0)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dibit = *dibit_p;
|
||||||
|
dibit_p++;
|
||||||
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Save the current frame in the appropriate superframe buffer */
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
/* Time slot 1 superframe buffer filling => RAW voice bit */
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
|
||||||
|
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Time slot 2 superframe buffer filling => RAW voice bit */
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
|
||||||
|
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
z++;
|
||||||
|
}
|
||||||
|
|
||||||
|
l = 0;
|
||||||
|
// signaling data or sync
|
||||||
|
for(i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
if(j > 0)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dibit = *dibit_p;
|
||||||
|
dibit_p++;
|
||||||
|
if(opts->inverted_dmr == 1)
|
||||||
|
{
|
||||||
|
dibit = (dibit ^ 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
sync[i] = (dibit | 1) + 48;
|
||||||
|
|
||||||
|
/* Save the current frame in the appropriate superframe buffer */
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
/* Time slot 1 superframe buffer filling => SYNC data */
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Time slot 2 superframe buffer filling => SYNC data */
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync[24] = 0;
|
||||||
|
|
||||||
|
/* Copy the Embedded signaling (1st part) */
|
||||||
|
for(i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
EmbeddedSignalling[i] = syncdata[i];
|
||||||
|
}
|
||||||
|
/* Copy the Embedded signaling (2nd part) */
|
||||||
|
for(i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
EmbeddedSignalling[i + 8] = syncdata[i + 40];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
|
||||||
|
if(QR_16_7_6_decode(EmbeddedSignalling)) EmbeddedSignallingOk = 1;
|
||||||
|
else EmbeddedSignallingOk = 0;
|
||||||
|
|
||||||
|
/* Get the color code */
|
||||||
|
cc[0] = EmbeddedSignalling[0];
|
||||||
|
cc[1] = EmbeddedSignalling[1];
|
||||||
|
cc[2] = EmbeddedSignalling[2];
|
||||||
|
cc[3] = EmbeddedSignalling[3];
|
||||||
|
|
||||||
|
/* The color code is normally the same for all
|
||||||
|
* 6 received voice frame, so in case of error
|
||||||
|
* do not store it except if the last SYNC frame
|
||||||
|
* has been received. */
|
||||||
|
if(EmbeddedSignallingOk || ((state->color_code_ok == 0) && (j == 5)))
|
||||||
|
{
|
||||||
|
/* Save the color code */
|
||||||
|
state->color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]);
|
||||||
|
state->color_code_ok = EmbeddedSignallingOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save the Power Control (PI) indicator */
|
||||||
|
state->PI = (unsigned int)EmbeddedSignalling[4];
|
||||||
|
state->PI_ok = EmbeddedSignallingOk;
|
||||||
|
|
||||||
|
/* Save the Link Control Start Stop (LCSS) indicator */
|
||||||
|
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
||||||
|
state->LCSS_ok = EmbeddedSignallingOk;
|
||||||
|
|
||||||
|
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
||||||
|
{
|
||||||
|
mutecurrentslot = 1;
|
||||||
|
state->directmode = 0;
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->slot1light, "[slot1]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(state->slot2light, "[slot2]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
|
||||||
|
{
|
||||||
|
mutecurrentslot = 1;
|
||||||
|
state->currentslot = 0;
|
||||||
|
state->directmode = 1; /* Direct mode */
|
||||||
|
sprintf(state->slot1light, "[sLoT1]");
|
||||||
|
}
|
||||||
|
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
||||||
|
{
|
||||||
|
mutecurrentslot = 1;
|
||||||
|
state->currentslot = 1;
|
||||||
|
state->directmode = 1; /* Direct mode */
|
||||||
|
sprintf(state->slot1light, "[sLoT2]");
|
||||||
|
}
|
||||||
|
else if((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) ) //|| (strcmp (sync, DMR_MS_VOICE_SYNC) == 0))
|
||||||
|
{
|
||||||
|
mutecurrentslot = 0;
|
||||||
|
state->directmode = 0;
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->slot1light, "[SLOT1]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(state->slot2light, "[SLOT2]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0)
|
||||||
|
{
|
||||||
|
mutecurrentslot = 0;
|
||||||
|
state->currentslot = 0;
|
||||||
|
state->directmode = 1; /* Direct mode */
|
||||||
|
sprintf(state->slot1light, "[sLoT1]");
|
||||||
|
}
|
||||||
|
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0)
|
||||||
|
{
|
||||||
|
mutecurrentslot = 0;
|
||||||
|
state->currentslot = 1;
|
||||||
|
state->directmode = 1; /* Direct mode */
|
||||||
|
sprintf(state->slot2light, "[sLoT2]");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DMR_DUMP
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
dibit = syncdata[i];
|
||||||
|
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||||
|
k++;
|
||||||
|
syncbits[k] = (1 & dibit) + 48; // bit 0
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
syncbits[48] = 0;
|
||||||
|
fprintf(stderr, "%s ", syncbits);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// current slot frame 2 second half
|
||||||
|
for(i = 0; i < 18; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Save the current frame in the appropriate superframe buffer */
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
/* Time slot 1 superframe buffer filling => RAW voice bit */
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
|
||||||
|
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Time slot 2 superframe buffer filling */
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
|
||||||
|
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
z++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mutecurrentslot == 0)
|
||||||
|
{
|
||||||
|
if(state->firstframe == 1)
|
||||||
|
{ // we don't know if anything received before the first sync after no carrier is valid
|
||||||
|
state->firstframe = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
//processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// current slot frame 3
|
||||||
|
w = rW;
|
||||||
|
x = rX;
|
||||||
|
y = rY;
|
||||||
|
z = rZ;
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 36; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Save the current frame in the appropriate superframe buffer */
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
/* Time slot 1 superframe buffer filling => RAW voice bit */
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
|
||||||
|
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Time slot 2 superframe buffer filling => RAW voice bit */
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
|
||||||
|
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
z++;
|
||||||
|
}
|
||||||
|
if(mutecurrentslot == 0)
|
||||||
|
{
|
||||||
|
//processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CACH
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
cachdata[i] = dibit;
|
||||||
|
}
|
||||||
|
cachdata[12] = 0;
|
||||||
|
|
||||||
|
#ifdef DMR_DUMP
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
dibit = cachdata[i];
|
||||||
|
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||||
|
k++;
|
||||||
|
cachbits[k] = (1 & dibit) + 48; // bit 0
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
cachbits[24] = 0;
|
||||||
|
fprintf(stderr, "%s ", cachbits);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// next slot
|
||||||
|
skipDibit (opts, state, 54);
|
||||||
|
|
||||||
|
// signaling data or sync
|
||||||
|
for(i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
syncdata[i] = dibit;
|
||||||
|
sync[i] = (dibit | 1) + 48;
|
||||||
|
}
|
||||||
|
sync[24] = 0;
|
||||||
|
syncdata[24] = 0;
|
||||||
|
|
||||||
|
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (msMode == 1))
|
||||||
|
{
|
||||||
|
state->directmode = 0;
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->slot2light, " slot2 ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(state->slot1light, " slot1 ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
|
||||||
|
{
|
||||||
|
state->directmode = 0;
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->slot2light, " SLOT2 ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(state->slot1light, " SLOT1 ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if((strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0))
|
||||||
|
{
|
||||||
|
state->currentslot = 0;
|
||||||
|
state->directmode = 1; /* Direct mode */
|
||||||
|
sprintf(state->slot1light, " sLoT1 ");
|
||||||
|
}
|
||||||
|
else if((strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0))
|
||||||
|
{
|
||||||
|
state->currentslot = 1;
|
||||||
|
state->directmode = 1; /* Direct mode */
|
||||||
|
sprintf(state->slot1light, " sLoT2 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DMR_DUMP
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
dibit = syncdata[i];
|
||||||
|
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||||
|
k++;
|
||||||
|
syncbits[k] = (1 & dibit) + 48; // bit 0
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
syncbits[48] = 0;
|
||||||
|
fprintf(stderr, "%s ", syncbits);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(j == 5) //j == 5
|
||||||
|
{
|
||||||
|
|
||||||
|
// 2nd half next slot
|
||||||
|
skipDibit (opts, state, 54);
|
||||||
|
|
||||||
|
// CACH
|
||||||
|
skipDibit (opts, state, 12);
|
||||||
|
|
||||||
|
// first half current slot
|
||||||
|
skipDibit (opts, state, 54);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract the 49 bits AMBE of the voice frame */
|
||||||
|
if(state->currentslot == 0)
|
||||||
|
{
|
||||||
|
/* 3 x 49 bit of AMBE sample per voice frame => Slot 1buffer here */
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
|
||||||
|
|
||||||
|
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i];
|
||||||
|
|
||||||
|
mbe_demodulateAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
|
||||||
|
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i],
|
||||||
|
&(state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* 3 x 49 bit of AMBE sample per voice frame => Slot 2 buffer here */
|
||||||
|
for(i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
|
||||||
|
|
||||||
|
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i];
|
||||||
|
|
||||||
|
mbe_demodulateAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
|
||||||
|
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i],
|
||||||
|
&(state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* End for(j = 0; j < 6; j++) */
|
||||||
|
|
||||||
|
if(opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
||||||
|
|
||||||
|
/* Print the color code */
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf(stderr, "| Color Code=%02d ", (int)state->color_code);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//if(state->color_code_ok) fprintf(stderr, "(OK) |");
|
||||||
|
if(state->color_code_ok)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "| (CRC OK ) |"); //add line break
|
||||||
|
state->dmr_color_code = state->color_code; //set here when voice? otherwise is set off of idle.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf(stderr, "| (CRC ERR) |"); //add line break
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, " VOICE ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform the SYNC DMR data embedded decoding */
|
||||||
|
ProcessVoiceBurstSync(opts, state);
|
||||||
|
|
||||||
|
/* Perform the DMR voice decoding */
|
||||||
|
ProcessDMR(opts, state);
|
||||||
|
|
||||||
|
//LFSR if required, change condition?
|
||||||
|
if ( (state->currentslot == 0 && state->payload_algid == 0x21 ) || //&& opts->payload == 1
|
||||||
|
(state->currentslot == 1 && state->payload_algidR == 0x21 ) ) //&& opts->payload == 1
|
||||||
|
{
|
||||||
|
LFSR(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* End processDMRvoice() */
|
||||||
@@ -177,8 +177,10 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
x++;
|
x++;
|
||||||
y++;
|
y++;
|
||||||
z++;
|
z++;
|
||||||
//should be fixed now with the frame_sync t_max setup
|
if (j == 3 && i == 28) //find sweet spot to cut off early
|
||||||
//no more early breaking required
|
{
|
||||||
|
i = 40; //break early so FS2 sync can resync
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
139
src/dsd_audio.c
139
src/dsd_audio.c
@@ -92,7 +92,11 @@ processAudio (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
// detect max level
|
// detect max level
|
||||||
max = 0;
|
max = 0;
|
||||||
|
//attempt to reduce crackle by overriding the max value when not using pulse input
|
||||||
|
if (opts->audio_in_type != 0)
|
||||||
|
{
|
||||||
|
max = 3000;
|
||||||
|
}
|
||||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||||
for (n = 0; n < 160; n++)
|
for (n = 0; n < 160; n++)
|
||||||
{
|
{
|
||||||
@@ -234,7 +238,11 @@ processAudioR (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
// detect max level
|
// detect max level
|
||||||
max = 0;
|
max = 0;
|
||||||
|
//attempt to reduce crackle by overriding the max value when not using pulse input
|
||||||
|
if (opts->audio_in_type != 0)
|
||||||
|
{
|
||||||
|
max = 3000;
|
||||||
|
}
|
||||||
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||||
for (n = 0; n < 160; n++)
|
for (n = 0; n < 160; n++)
|
||||||
{
|
{
|
||||||
@@ -537,59 +545,31 @@ openAudioOutDevice (dsd_opts * opts, int speed)
|
|||||||
void
|
void
|
||||||
openAudioInDevice (dsd_opts * opts)
|
openAudioInDevice (dsd_opts * opts)
|
||||||
{
|
{
|
||||||
char * extension;
|
|
||||||
const char ch = '.';
|
|
||||||
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
|
||||||
|
|
||||||
// get info of device/file
|
// get info of device/file
|
||||||
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
if(strncmp(opts->audio_in_dev, "-", 1) == 0)
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 1;
|
opts->audio_in_type = 1;
|
||||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
opts->audio_in_file_info->samplerate=48000;
|
||||||
opts->audio_in_file_info->channels=1;
|
opts->pulse_digi_rate_out = 8000; //set out rate to 8000 for stdin input
|
||||||
opts->audio_in_file_info->seekable=0;
|
opts->audio_in_file_info->channels=1;
|
||||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
opts->audio_in_file_info->seekable=0;
|
||||||
opts->audio_in_file = sf_open_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0);
|
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||||
|
opts->audio_in_file = sf_open_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
|
||||||
if(opts->audio_in_file == NULL)
|
if(opts->audio_in_file == NULL) {
|
||||||
{
|
fprintf (stderr,"Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
fprintf(stderr, "Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL));
|
exit(1); //had this one disabled, re-enabling it now
|
||||||
exit(1);
|
}
|
||||||
}
|
}
|
||||||
|
//converted to handle any calls to use portaudio
|
||||||
|
else if(strncmp(opts->audio_in_dev, "pa:", 2) == 0)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 0;
|
||||||
|
fprintf (stderr,"Error, Port Audio is not supported by FME!\n");
|
||||||
|
fprintf (stderr,"Using Pulse Audio Input Stream Instead! \n");
|
||||||
|
sprintf (opts->audio_in_dev, "pulse");
|
||||||
}
|
}
|
||||||
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
|
||||||
{
|
|
||||||
opts->audio_in_type = 8;
|
|
||||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
|
||||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
|
||||||
opts->audio_in_file_info->channels=1;
|
|
||||||
opts->audio_in_file_info->seekable=0;
|
|
||||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
|
||||||
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
|
||||||
|
|
||||||
if(opts->tcp_file_in == NULL)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
|
|
||||||
{
|
|
||||||
opts->audio_in_type = 6;
|
|
||||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
|
||||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
|
||||||
opts->audio_in_file_info->channels=1;
|
|
||||||
opts->audio_in_file_info->seekable=0;
|
|
||||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
|
||||||
opts->udp_file_in = sf_open_fd(opts->udp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
|
||||||
|
|
||||||
if(opts->udp_file_in == NULL)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error, couldn't open UDP with libsndfile: %s\n", sf_strerror(NULL));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 3;
|
opts->audio_in_type = 3;
|
||||||
@@ -598,58 +578,41 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
{
|
{
|
||||||
opts->audio_in_type = 0;
|
opts->audio_in_type = 0;
|
||||||
}
|
}
|
||||||
else if (strncmp(extension, ".bin", 3) == 0)
|
//convert from opening wav files to OP25 symbol capture files since opening wav files is busted
|
||||||
{
|
|
||||||
struct stat stat_buf;
|
|
||||||
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr,"Error, couldn't open bin file %s\n", opts->audio_in_dev);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (S_ISREG(stat_buf.st_mode))
|
|
||||||
{
|
|
||||||
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
|
||||||
opts->audio_in_type = 4; //symbol capture bin files
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
opts->audio_in_type = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//open as wav file as last resort, wav files subseptible to sample rate issues if not 48000
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct stat stat_buf;
|
struct stat stat_buf;
|
||||||
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (S_ISREG(stat_buf.st_mode))
|
if (S_ISREG(stat_buf.st_mode))
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 2; //two now, seperating STDIN and wav files
|
// is this a regular file? then process with libsndfile.
|
||||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
//opts->pulse_digi_rate_out = 8000; //this for wav files input?
|
||||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
// opts->audio_in_type = 1;
|
||||||
opts->audio_in_file_info->channels=1;
|
// opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
opts->audio_in_file_info->channels = 1;
|
// opts->audio_in_file_info->channels = 1;
|
||||||
opts->audio_in_file_info->seekable=0;
|
// opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||||
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)
|
||||||
|
// {
|
||||||
if(opts->audio_in_file == NULL)
|
// fprintf (stderr,"Error, couldn't open file %s\n", opts->audio_in_dev);
|
||||||
{
|
// exit(1);
|
||||||
fprintf(stderr, "Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
// }
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
||||||
|
opts->audio_in_type = 4; //symbol capture bin files
|
||||||
}
|
}
|
||||||
//open pulse audio if no bin or wav
|
else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 0; //not sure if this works or needs to openPulse here
|
// this is a device, use old handling, pulse audio now
|
||||||
|
opts->audio_in_type = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->split == 1)
|
if (opts->split == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Audio In Device: %s\n", opts->audio_in_dev);
|
fprintf (stderr,"Audio In Device: %s\n", opts->audio_in_dev);
|
||||||
|
|||||||
@@ -221,13 +221,12 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
//int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
//int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||||
{
|
{
|
||||||
// determine dibit state
|
// determine dibit state
|
||||||
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37))
|
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18))
|
||||||
|
|
||||||
{
|
{
|
||||||
// 6 +D-STAR
|
// 6 +D-STAR
|
||||||
// 14 +ProVoice
|
// 14 +ProVoice
|
||||||
// 18 +D-STAR_HD
|
// 18 +D-STAR_HD
|
||||||
// 37 +EDACS
|
|
||||||
|
|
||||||
if (symbol > state->center)
|
if (symbol > state->center)
|
||||||
{
|
{
|
||||||
@@ -242,12 +241,11 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
return (1); // +3
|
return (1); // +3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19) || (state->synctype == 38))
|
else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19))
|
||||||
{
|
{
|
||||||
// 7 -D-STAR
|
// 7 -D-STAR
|
||||||
// 15 -ProVoice
|
// 15 -ProVoice
|
||||||
// 19 -D-STAR_HD
|
// 19 -D-STAR_HD
|
||||||
// 38 -EDACS
|
|
||||||
|
|
||||||
if (symbol > state->center)
|
if (symbol > state->center)
|
||||||
{
|
{
|
||||||
@@ -265,7 +263,7 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
|
|
||||||
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
|
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
|
||||||
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
|
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
|
||||||
(state->synctype == 17) || (state->synctype == 29) || (state->synctype == 36) )
|
(state->synctype == 17) || (state->synctype == 36) )
|
||||||
|
|
||||||
{
|
{
|
||||||
// 1 -P25p1
|
// 1 -P25p1
|
||||||
@@ -275,20 +273,16 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
// 11 -DMR (inverted signal voice frame)
|
// 11 -DMR (inverted signal voice frame)
|
||||||
// 13 -DMR (inverted signal data frame)
|
// 13 -DMR (inverted signal data frame)
|
||||||
// 17 -NXDN (inverted data frame)
|
// 17 -NXDN (inverted data frame)
|
||||||
// 29 -NXDN (inverted FSW)
|
|
||||||
// 36 -P25p2
|
|
||||||
|
|
||||||
int valid;
|
int valid;
|
||||||
int dibit;
|
int dibit;
|
||||||
|
|
||||||
valid = 0;
|
valid = 0;
|
||||||
|
|
||||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
if (state->synctype == 1)
|
||||||
//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)
|
|
||||||
{
|
{
|
||||||
// Use the P25 heuristics if available
|
// Use the P25 heuristics if available
|
||||||
//valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid == 0)
|
if (valid == 0)
|
||||||
@@ -340,20 +334,16 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
// 10 +DMR (non inverted signal data frame)
|
// 10 +DMR (non inverted signal data frame)
|
||||||
// 12 +DMR (non inverted signal voice frame)
|
// 12 +DMR (non inverted signal voice frame)
|
||||||
// 16 +NXDN (non inverted data frame)
|
// 16 +NXDN (non inverted data frame)
|
||||||
// 28 +NXND (FSW)
|
|
||||||
// 35 +p25p2
|
|
||||||
|
|
||||||
int valid;
|
int valid;
|
||||||
int dibit;
|
int dibit;
|
||||||
|
|
||||||
valid = 0;
|
valid = 0;
|
||||||
|
|
||||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
if (state->synctype == 0)
|
||||||
//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)
|
|
||||||
{
|
{
|
||||||
// Use the P25 heuristics if available
|
// Use the P25 heuristics if available
|
||||||
//valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid == 0)
|
if (valid == 0)
|
||||||
|
|||||||
140
src/dsd_file.c
140
src/dsd_file.c
@@ -166,7 +166,7 @@ PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d) //for P25P1 and
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stereo, may use this for NXDN too
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
@@ -174,7 +174,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
|
|
||||||
err = (unsigned char) state->errs2;
|
err = (unsigned char) state->errs2;
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->dmr_stereo == 0 && opts->dmr_mono == 0)
|
if (opts->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
fprintf(stderr, " AMBE ");
|
fprintf(stderr, " AMBE ");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
for (i = 0; i < 7; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
|
||||||
{
|
{
|
||||||
b = 0;
|
b = 0;
|
||||||
for (j = 0; j < 8; j++)
|
for (j = 0; j < 8; j++)
|
||||||
@@ -192,13 +192,13 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
b = b + ambe_d[k];
|
b = b + ambe_d[k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
if (opts->payload == 1 && i < 6)
|
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", b);
|
fprintf (stderr, "%02X", b);
|
||||||
}
|
}
|
||||||
if (opts->payload == 1 && i == 6)
|
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit, value will be either 0x00 or 0x80?
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", b & 0x80);
|
fprintf (stderr, "%02X", b & 0x80); //7th octet should only contain 1 bit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
@@ -207,7 +207,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
}
|
}
|
||||||
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
|
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
|
||||||
b = ambe_d[48];
|
b = ambe_d[48];
|
||||||
if (opts->dmr_stereo == 1 || opts->dmr_mono == 1) //need to fix the printouts again
|
if (opts->dmr_stereo == 1) //need to fix the printouts again
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|||||||
|
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1) //make opt variable later on to toggle this
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
@@ -248,12 +248,12 @@ readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|||||||
b = b & 255;
|
b = b & 255;
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
|
//new printout on reading MBE files back in
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1) //make opt variable later on to toggle this
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", x);
|
fprintf (stderr, "%02X", x);
|
||||||
}
|
}
|
||||||
|
//
|
||||||
}
|
}
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
@@ -273,11 +273,11 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
state->errs = state->errs2;
|
state->errs = state->errs2;
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1) //make opt variable later on to toggle this
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
//for (i = 0; i < 6; i++)
|
||||||
for (i = 0; i < 6; i++) //breaks backwards compatablilty with 6 files
|
for (i = 0; i < 6; i++) //breaks backwards compatablilty with 6 files
|
||||||
{
|
{
|
||||||
b = fgetc (opts->mbe_in_f);
|
b = fgetc (opts->mbe_in_f);
|
||||||
@@ -296,13 +296,13 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
b = b & 255;
|
b = b & 255;
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
if (opts->payload == 1 && i < 6)
|
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", x);
|
fprintf (stderr, "%02X", x);
|
||||||
}
|
}
|
||||||
if (opts->payload == 1 && i == 6)
|
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit? value will be either 0x00 or 0x80?
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%02X", x & 0x80);
|
fprintf (stderr, "%02X", x & 0x80); //7th octet should only contain 1 bit?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
@@ -349,6 +349,82 @@ openMbeInFile (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//temporarily disabled until the compiler warnings can be fixed, use symbol capture or per call instead!
|
||||||
|
//probably the easiest thing is to just have this run fclose, and have name assignment on open instead.
|
||||||
|
// void
|
||||||
|
// closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
|
// {
|
||||||
|
//
|
||||||
|
// char shell[255], newfilename[64], ext[5], datestr[32], new_path[1024];
|
||||||
|
// char tgid[17];
|
||||||
|
// int sum, i, j;
|
||||||
|
// int talkgroup;
|
||||||
|
// struct tm timep;
|
||||||
|
// int result;
|
||||||
|
//
|
||||||
|
// if (opts->mbe_out_f != NULL)
|
||||||
|
// {
|
||||||
|
// if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
||||||
|
// {
|
||||||
|
// sprintf (ext, ".imb");
|
||||||
|
// //strptime (opts->mbe_out_file, "%s.imb", &timep);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// sprintf (ext, ".amb");
|
||||||
|
// //strptime (opts->mbe_out_file, "%s.amb", &timep);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (state->tgcount > 0)
|
||||||
|
// {
|
||||||
|
// for (i = 0; i < 16; i++)
|
||||||
|
// {
|
||||||
|
// sum = 0;
|
||||||
|
// for (j = 0; j < state->tgcount; j++)
|
||||||
|
// {
|
||||||
|
// sum = sum + state->tg[j][i] - 48;
|
||||||
|
// }
|
||||||
|
// tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
|
||||||
|
// }
|
||||||
|
// tgid[16] = 0;
|
||||||
|
// talkgroup = (int) strtol (tgid, NULL, 2);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// talkgroup = 0;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fflush (opts->mbe_out_f);
|
||||||
|
// fclose (opts->mbe_out_f);
|
||||||
|
// opts->mbe_out_f = NULL;
|
||||||
|
// strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
|
||||||
|
//
|
||||||
|
// sprintf (newfilename, "%s%s", datestr, ext); //this one
|
||||||
|
//
|
||||||
|
// if (state->lastsynctype == 0 || state->lastsynctype == 1)
|
||||||
|
// {
|
||||||
|
// sprintf (newfilename, "%s-nac%X-tg%i%s", datestr, state->nac, talkgroup, ext);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //sprintf (new_path, "%s%s", opts->mbe_out_dir, newfilename);
|
||||||
|
// #ifdef _WIN32
|
||||||
|
// //sprintf (shell, "move %s %s", opts->mbe_out_path, new_path);
|
||||||
|
// #else
|
||||||
|
// //sprintf (shell, "mv %s %s", opts->mbe_out_path, new_path);
|
||||||
|
// #endif
|
||||||
|
// //result = system (shell);
|
||||||
|
//
|
||||||
|
// state->tgcount = 0;
|
||||||
|
// for (i = 0; i < 25; i++)
|
||||||
|
// {
|
||||||
|
// for (j = 0; j < 16; j++)
|
||||||
|
// {
|
||||||
|
// state->tg[i][j] = 48;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
//much simpler version
|
//much simpler version
|
||||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
@@ -371,6 +447,7 @@ void
|
|||||||
openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//struct timeval tv;
|
||||||
int i, j;
|
int i, j;
|
||||||
char ext[5];
|
char ext[5];
|
||||||
|
|
||||||
@@ -394,6 +471,10 @@ openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
state->tgcount = 0;
|
state->tgcount = 0;
|
||||||
|
|
||||||
|
//gettimeofday (&tv, NULL);
|
||||||
|
//rewrite below line to take getTime function instead?
|
||||||
|
// sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
|
||||||
|
|
||||||
sprintf (opts->mbe_out_file, "%s %s%s", getDateF(), getTimeF(), ext);
|
sprintf (opts->mbe_out_file, "%s %s%s", getDateF(), getTimeF(), ext);
|
||||||
|
|
||||||
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
|
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
|
||||||
@@ -419,6 +500,7 @@ openWavOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
info.samplerate = 8000; //8000
|
info.samplerate = 8000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
|
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
|
||||||
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||||
|
|
||||||
if (opts->wav_out_f == NULL)
|
if (opts->wav_out_f == NULL)
|
||||||
@@ -426,15 +508,18 @@ openWavOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// state->wav_out_bytes = 0;
|
||||||
|
|
||||||
|
}
|
||||||
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
SF_INFO info;
|
SF_INFO info;
|
||||||
info.samplerate = 8000;
|
info.samplerate = 8000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
|
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
|
||||||
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||||
|
|
||||||
if (opts->wav_out_f == NULL)
|
if (opts->wav_out_f == NULL)
|
||||||
@@ -442,6 +527,9 @@ void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// state->wav_out_bytes = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
@@ -451,6 +539,7 @@ void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
|||||||
info.samplerate = 8000; //8000
|
info.samplerate = 8000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
|
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
|
||||||
opts->wav_out_fR = sf_open (opts->wav_out_fileR, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
opts->wav_out_fR = sf_open (opts->wav_out_fileR, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||||
|
|
||||||
if (opts->wav_out_f == NULL)
|
if (opts->wav_out_f == NULL)
|
||||||
@@ -458,6 +547,9 @@ void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_fileR);
|
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_fileR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// state->wav_out_bytes = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
||||||
@@ -467,12 +559,17 @@ void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
|||||||
info.samplerate = 48000; //8000
|
info.samplerate = 48000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
|
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
|
||||||
|
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||||
opts->wav_out_raw = sf_open (opts->wav_out_file_raw, SFM_RDWR, &info);
|
opts->wav_out_raw = sf_open (opts->wav_out_file_raw, SFM_RDWR, &info);
|
||||||
if (opts->wav_out_raw == NULL)
|
if (opts->wav_out_raw == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
|
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// state->wav_out_bytes = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
@@ -512,12 +609,15 @@ void closeSymbolOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
//Do something
|
//Do something
|
||||||
if (opts->symbol_out == 1)
|
if (opts->symbol_out == 1)
|
||||||
{
|
{
|
||||||
if (opts->symbol_out_file != NULL)
|
if (opts->symbol_out_file != NULL) //check first, or issuing a second fclose will crash the SOFTWARE
|
||||||
{
|
{
|
||||||
fclose(opts->symbol_out_f);
|
fclose(opts->symbol_out_f); //free(): double free detected in tcache 2 (this is a new one) happens when closing more than once
|
||||||
|
//noCarrier(opts, state); //free(): double free detected in tcache 2 (this is a new one)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts->symbol_out = 0; //set flag to 1
|
opts->symbol_out = 0; //set flag to 1
|
||||||
|
//sprintf (opts->symbol_out_file, "fme-capture.bin"); //use fme for the symbol bin ext
|
||||||
|
//openSymbolOutFile (opts, state);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Original DMR filter
|
// DMR filter
|
||||||
#define NZEROS 60
|
#define NZEROS 60
|
||||||
float ngain = 7.423339364f;
|
float ngain = 7.423339364f;
|
||||||
static float xv[NZEROS+1];
|
static float xv[NZEROS+1];
|
||||||
@@ -21,48 +21,70 @@ float xcoeffs[] =
|
|||||||
-0.0083649323f,
|
-0.0083649323f,
|
||||||
};
|
};
|
||||||
|
|
||||||
//reverted to original NXDN filters, do better with LICH/Data handling
|
// NXDN48 filter
|
||||||
// NXDN48 filter - Original Version
|
//#define NXZEROS 134
|
||||||
#define NXZEROS 134
|
//float nxgain = 15.95930463f;
|
||||||
float nxgain = 15.95930463f;
|
|
||||||
|
#define NXZEROS 91 //Osmocom OP25
|
||||||
|
float nxgain = 1.0f; //Osmocom OP25
|
||||||
|
|
||||||
static float nxv[NXZEROS+1];
|
static float nxv[NXZEROS+1];
|
||||||
|
|
||||||
|
//OP25 Osmocom NXDN48 Coeffs seem to do well, will want to do some VS testing
|
||||||
float nxcoeffs[] =
|
float nxcoeffs[] =
|
||||||
{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
{ 0.00203767f, 0.00491622f, 0.00748189f, 0.00934561f, 0.01017049f, 0.00972167f,
|
||||||
+0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
0.00790748f, 0.00480597f, 0.00067202f, -0.00407743f, -0.00890019f, -0.01318478f,
|
||||||
-0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
-0.01632047f, -0.01777371f, -0.01716241f, -0.01431852f, -0.00933066f, -0.00255979f,
|
||||||
-0.045828927f, -0.052119261f, -0.056372283f, -0.058221106f,
|
0.00537624f, 0.01364992f, 0.02130038f, 0.02733235f, 0.03082824f, 0.03106179f,
|
||||||
-0.057387924f, -0.053703443f, -0.047122444f, -0.037734535f,
|
0.02760061f, 0.02038545f, 0.00977572f, -0.0034462f, -0.01811514f, -0.03276675f,
|
||||||
-0.025769308f, -0.011595336f, +0.004287292f, +0.021260954f,
|
-0.04575844f, -0.05541762f, -0.06020353f, -0.0588669f, -0.05059129f, -0.03510097f,
|
||||||
+0.038610717f, +0.055550276f, +0.071252765f, +0.084885375f,
|
-0.01272309f, 0.01560398f, 0.04837926f, 0.08364189f, 0.11912012f, 0.15241359f,
|
||||||
+0.095646450f, +0.102803611f, +0.105731303f, +0.103946126f,
|
0.18119246f, 0.20339503f, 0.21740525f, 0.2221932f, 0.21740525f, 0.20339503f,
|
||||||
+0.097138329f, +0.085197939f, +0.068234131f, +0.046586711f,
|
0.18119246f, 0.15241359f, 0.11912012f, 0.08364189f, 0.04837926f, 0.01560398f,
|
||||||
+0.020828821f, -0.008239664f, -0.039608255f, -0.072081234f,
|
-0.01272309f, -0.03510097f, -0.05059129f, -0.0588669f, -0.06020353f, -0.05541762f,
|
||||||
-0.104311776f, -0.134843790f, -0.162160200f, -0.184736015f,
|
-0.04575844f, -0.03276675f, -0.01811514f, -0.0034462f, 0.00977572f, 0.02038545f,
|
||||||
-0.201094346f, -0.209863285f, -0.209831516f, -0.200000470f,
|
0.02760061f, 0.03106179f, 0.03082824f, 0.02733235f, 0.02130038f, 0.01364992f,
|
||||||
-0.179630919f, -0.148282051f, -0.105841323f, -0.052543664f,
|
0.00537624f, -0.00255979f, -0.00933066f, -0.01431852f, -0.01716241f, -0.01777371f,
|
||||||
+0.011020985f, +0.083912428f, +0.164857408f, +0.252278939f,
|
-0.01632047f, -0.01318478f, -0.00890019f, -0.00407743f, 0.00067202f, 0.00480597f,
|
||||||
+0.344336996f, +0.438979335f, +0.534000832f, +0.627109358f,
|
0.00790748f, 0.00972167f, 0.01017049f, 0.00934561f, 0.00748189f, 0.00491622f,
|
||||||
+0.715995947f, +0.798406824f, +0.872214756f, +0.935487176f,
|
0.00203767f};
|
||||||
+0.986548646f, +1.024035395f, +1.046939951f, +1.054644241f,
|
|
||||||
+1.046939951f, +1.024035395f, +0.986548646f, +0.935487176f,
|
//OLD nxCOEFFS
|
||||||
+0.872214756f, +0.798406824f, +0.715995947f, +0.627109358f,
|
// { +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
||||||
+0.534000832f, +0.438979335f, +0.344336996f, +0.252278939f,
|
// +0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
||||||
+0.164857408f, +0.083912428f, +0.011020985f, -0.052543664f,
|
// -0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
||||||
-0.105841323f, -0.148282051f, -0.179630919f, -0.200000470f,
|
// -0.045828927f, -0.052119261f, -0.056372283f, -0.058221106f,
|
||||||
-0.209831516f, -0.209863285f, -0.201094346f, -0.184736015f,
|
// -0.057387924f, -0.053703443f, -0.047122444f, -0.037734535f,
|
||||||
-0.162160200f, -0.134843790f, -0.104311776f, -0.072081234f,
|
// -0.025769308f, -0.011595336f, +0.004287292f, +0.021260954f,
|
||||||
-0.039608255f, -0.008239664f, +0.020828821f, +0.046586711f,
|
// +0.038610717f, +0.055550276f, +0.071252765f, +0.084885375f,
|
||||||
+0.068234131f, +0.085197939f, +0.097138329f, +0.103946126f,
|
// +0.095646450f, +0.102803611f, +0.105731303f, +0.103946126f,
|
||||||
+0.105731303f, +0.102803611f, +0.095646450f, +0.084885375f,
|
// +0.097138329f, +0.085197939f, +0.068234131f, +0.046586711f,
|
||||||
+0.071252765f, +0.055550276f, +0.038610717f, +0.021260954f,
|
// +0.020828821f, -0.008239664f, -0.039608255f, -0.072081234f,
|
||||||
+0.004287292f, -0.011595336f, -0.025769308f, -0.037734535f,
|
// -0.104311776f, -0.134843790f, -0.162160200f, -0.184736015f,
|
||||||
-0.047122444f, -0.053703443f, -0.057387924f, -0.058221106f,
|
// -0.201094346f, -0.209863285f, -0.209831516f, -0.200000470f,
|
||||||
-0.056372283f, -0.052119261f, -0.045828927f, -0.037935027f,
|
// -0.179630919f, -0.148282051f, -0.105841323f, -0.052543664f,
|
||||||
-0.028914291f, -0.019262057f, -0.009468531f, +0.000003302f,
|
// +0.011020985f, +0.083912428f, +0.164857408f, +0.252278939f,
|
||||||
+0.008737200f, +0.016379869f, +0.022653298f, +0.027362877f,
|
// +0.344336996f, +0.438979335f, +0.534000832f, +0.627109358f,
|
||||||
+0.030401148f, +0.031747267f, +0.031462429f,
|
// +0.715995947f, +0.798406824f, +0.872214756f, +0.935487176f,
|
||||||
};
|
// +0.986548646f, +1.024035395f, +1.046939951f, +1.054644241f,
|
||||||
|
// +1.046939951f, +1.024035395f, +0.986548646f, +0.935487176f,
|
||||||
|
// +0.872214756f, +0.798406824f, +0.715995947f, +0.627109358f,
|
||||||
|
// +0.534000832f, +0.438979335f, +0.344336996f, +0.252278939f,
|
||||||
|
// +0.164857408f, +0.083912428f, +0.011020985f, -0.052543664f,
|
||||||
|
// -0.105841323f, -0.148282051f, -0.179630919f, -0.200000470f,
|
||||||
|
// -0.209831516f, -0.209863285f, -0.201094346f, -0.184736015f,
|
||||||
|
// -0.162160200f, -0.134843790f, -0.104311776f, -0.072081234f,
|
||||||
|
// -0.039608255f, -0.008239664f, +0.020828821f, +0.046586711f,
|
||||||
|
// +0.068234131f, +0.085197939f, +0.097138329f, +0.103946126f,
|
||||||
|
// +0.105731303f, +0.102803611f, +0.095646450f, +0.084885375f,
|
||||||
|
// +0.071252765f, +0.055550276f, +0.038610717f, +0.021260954f,
|
||||||
|
// +0.004287292f, -0.011595336f, -0.025769308f, -0.037734535f,
|
||||||
|
// -0.047122444f, -0.053703443f, -0.057387924f, -0.058221106f,
|
||||||
|
// -0.056372283f, -0.052119261f, -0.045828927f, -0.037935027f,
|
||||||
|
// -0.028914291f, -0.019262057f, -0.009468531f, +0.000003302f,
|
||||||
|
// +0.008737200f, +0.016379869f, +0.022653298f, +0.027362877f,
|
||||||
|
// +0.030401148f, +0.031747267f, +0.031462429f,
|
||||||
|
// };
|
||||||
|
|
||||||
// DMR filter F4EXB
|
// DMR filter F4EXB
|
||||||
#define DMRNZEROS 60
|
#define DMRNZEROS 60
|
||||||
@@ -88,52 +110,6 @@ const float dmrcoeffs[] =
|
|||||||
0.0147202645, -0.0186176083, -0.0428325003, -0.0543747957, -0.0528141756,
|
0.0147202645, -0.0186176083, -0.0428325003, -0.0543747957, -0.0528141756,
|
||||||
-0.0404938748, -0.0216605133, -0.0013114705, 0.0159662432, 0.0269200615, 0.0301506278};
|
-0.0404938748, -0.0216605133, -0.0013114705, 0.0159662432, 0.0269200615, 0.0301506278};
|
||||||
|
|
||||||
// dPMR filter, version of F4EXB
|
|
||||||
static float dpmrxv[NXZEROS+1];
|
|
||||||
|
|
||||||
// dPMR filter, version of F4EXB
|
|
||||||
#define DPMRNZEROS 134
|
|
||||||
|
|
||||||
// dPMR filter, version of F4EXB
|
|
||||||
const float dpmrgain = 14.6083498224f;
|
|
||||||
|
|
||||||
// dPMR filter - root raised cosine alpha=0.2 Ts = 3325 S/s Fc = 48 kHz - zero at boundaries appears to be slightly better
|
|
||||||
const float dpmrcoeffs[] =
|
|
||||||
{-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816,
|
|
||||||
0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307,
|
|
||||||
0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763,
|
|
||||||
-0.0013831396, -0.0119878087, -0.0228442151, -0.0333082708,
|
|
||||||
-0.0427067804, -0.0503756642, -0.0557003599, -0.0581561791,
|
|
||||||
-0.0573462646, -0.0530347941, -0.0451732069, -0.0339174991,
|
|
||||||
-0.0196350217, -0.0028997157, 0.0155246961, 0.0347134030,
|
|
||||||
0.0536202583, 0.0711271166, 0.0861006725, 0.0974542022,
|
|
||||||
0.1042112035, 0.1055676660, 0.1009496091, 0.0900625944,
|
|
||||||
0.0729301774, 0.0499186839, 0.0217462748, -0.0105250265,
|
|
||||||
-0.0455148664, -0.0815673067, -0.1168095612, -0.1492246435,
|
|
||||||
-0.1767350726, -0.1972941202, -0.2089805758, -0.2100926829,
|
|
||||||
-0.1992367833, -0.1754063031, -0.1380470370, -0.0871052089,
|
|
||||||
-0.0230554989, 0.0530929052, 0.1398131936, 0.2351006721,
|
|
||||||
0.3365341927, 0.4413570929, 0.5465745033, 0.6490630781,
|
|
||||||
0.7456885564, 0.8334261381, 0.9094784589, 0.9713859928,
|
|
||||||
1.0171250045, 1.0451886943, 1.0546479089, 1.0451886943,
|
|
||||||
1.0171250045, 0.9713859928, 0.9094784589, 0.8334261381,
|
|
||||||
0.7456885564, 0.6490630781, 0.5465745033, 0.4413570929,
|
|
||||||
0.3365341927, 0.2351006721, 0.1398131936, 0.0530929052,
|
|
||||||
-0.0230554989, -0.0871052089, -0.1380470370, -0.1754063031,
|
|
||||||
-0.1992367833, -0.2100926829, -0.2089805758, -0.1972941202,
|
|
||||||
-0.1767350726, -0.1492246435, -0.1168095612, -0.0815673067,
|
|
||||||
-0.0455148664, -0.0105250265, 0.0217462748, 0.0499186839,
|
|
||||||
0.0729301774, 0.0900625944, 0.1009496091, 0.1055676660,
|
|
||||||
0.1042112035, 0.0974542022, 0.0861006725, 0.0711271166,
|
|
||||||
0.0536202583, 0.0347134030, 0.0155246961, -0.0028997157,
|
|
||||||
-0.0196350217, -0.0339174991, -0.0451732069, -0.0530347941,
|
|
||||||
-0.0573462646, -0.0581561791, -0.0557003599, -0.0503756642,
|
|
||||||
-0.0427067804, -0.0333082708, -0.0228442151, -0.0119878087,
|
|
||||||
-0.0013831396, 0.0083888763, 0.0168387650, 0.0235897433,
|
|
||||||
0.0283911865, 0.0311240307, 0.0317982965, 0.0305433586,
|
|
||||||
0.0275919612, 0.0232592816, 0.0179185547, 0.0119748846,
|
|
||||||
0.0058388841, -0.0000983004};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
short dsd_input_filter(short sample, int mode);
|
short dsd_input_filter(short sample, int mode);
|
||||||
@@ -147,13 +123,7 @@ dmr_filter(short sample)
|
|||||||
short
|
short
|
||||||
nxdn_filter(short sample)
|
nxdn_filter(short sample)
|
||||||
{
|
{
|
||||||
return dsd_input_filter(sample, 2);
|
return dsd_input_filter(sample, 2);
|
||||||
}
|
|
||||||
|
|
||||||
short
|
|
||||||
dpmr_filter(short sample)
|
|
||||||
{
|
|
||||||
return dsd_input_filter(sample, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -174,7 +144,7 @@ dsd_input_filter(short sample, int mode)
|
|||||||
case 2:
|
case 2:
|
||||||
gain = nxgain;
|
gain = nxgain;
|
||||||
v = nxv;
|
v = nxv;
|
||||||
coeffs = (float *)nxcoeffs;
|
coeffs = nxcoeffs;
|
||||||
zeros = NXZEROS;
|
zeros = NXZEROS;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
@@ -183,12 +153,6 @@ dsd_input_filter(short sample, int mode)
|
|||||||
coeffs = (float *)dmrcoeffs;
|
coeffs = (float *)dmrcoeffs;
|
||||||
zeros = DMRNZEROS;
|
zeros = DMRNZEROS;
|
||||||
break;
|
break;
|
||||||
case 4:
|
|
||||||
gain = dpmrgain;
|
|
||||||
v = dpmrxv;
|
|
||||||
coeffs = (float *)dpmrcoeffs;
|
|
||||||
zeros = DPMRNZEROS;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
@@ -196,7 +160,7 @@ dsd_input_filter(short sample, int mode)
|
|||||||
for (i = 0; i < zeros; i++)
|
for (i = 0; i < zeros; i++)
|
||||||
v[i] = v[i+1];
|
v[i] = v[i+1];
|
||||||
|
|
||||||
v[zeros] = (float)sample; // unfiltered sample in
|
v[zeros] = sample; // unfiltered sample in
|
||||||
sum = 0.0f;
|
sum = 0.0f;
|
||||||
|
|
||||||
for (i = 0; i <= zeros; i++)
|
for (i = 0; i <= zeros; i++)
|
||||||
|
|||||||
131
src/dsd_frame.c
131
src/dsd_frame.c
@@ -84,13 +84,54 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
state->minref = state->min;
|
state->minref = state->min;
|
||||||
}
|
}
|
||||||
|
|
||||||
//NXDN FSW
|
if ((state->synctype == 8) || (state->synctype == 9)) //NXDN Voice
|
||||||
if ((state->synctype == 28) || (state->synctype == 29))
|
{
|
||||||
{
|
//state->rf_mod = 2; //wrong type of modulation HERE HERE
|
||||||
nxdn_frame (opts, state);
|
state->nac = 0;
|
||||||
//no MBEout...use symbol capture bin instead!
|
state->lastsrc = 0;
|
||||||
return;
|
state->lasttg = 0;
|
||||||
}
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
if (opts->verbose > 0)
|
||||||
|
{
|
||||||
|
level = (int) state->max / 164;
|
||||||
|
//fprintf (stderr, "inlvl: %2i%% ", level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state->nac = 0;
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
|
{
|
||||||
|
openMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
|
processNXDNVoice (opts, state);
|
||||||
|
//ProcessNXDNFrame (opts, state, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ((state->synctype == 16) || (state->synctype == 17)) //NXDN Data
|
||||||
|
{
|
||||||
|
//state->rf_mod = 2;
|
||||||
|
state->nac = 0;
|
||||||
|
state->lastsrc = 0;
|
||||||
|
state->lasttg = 0;
|
||||||
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
if (opts->verbose > 0)
|
||||||
|
{
|
||||||
|
level = (int) state->max / 164;
|
||||||
|
//fprintf (stderr, "inlvl: %2i%% ", level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state->nac = 0;
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
|
{
|
||||||
|
openMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
sprintf (state->fsubtype, " DATA ");
|
||||||
|
processNXDNData (opts, state);
|
||||||
|
//ProcessNXDNFrame (opts, state, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
else if ((state->synctype == 6) || (state->synctype == 7))
|
else if ((state->synctype == 6) || (state->synctype == 7))
|
||||||
{
|
{
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
@@ -138,30 +179,6 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
//Start DMR Types
|
//Start DMR Types
|
||||||
else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC
|
else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC
|
||||||
{
|
{
|
||||||
|
|
||||||
//print manufacturer strings to branding, disabled 0x10 moto other systems can use that fid set
|
|
||||||
//0x06 is trident, but when searching, apparently, they developed con+, but was bought by moto?
|
|
||||||
if (state->dmr_mfid == 0x10) ; //sprintf (state->dmr_branding, "%s", "Motorola");
|
|
||||||
else if (state->dmr_mfid == 0x68) sprintf (state->dmr_branding, "%s", "Hytera");
|
|
||||||
else if (state->dmr_mfid == 0x58) sprintf (state->dmr_branding, "%s", "Tait");
|
|
||||||
|
|
||||||
//disabling these due to random data decodes setting an odd mfid, could be legit, but only for that one packet?
|
|
||||||
//or, its just a decode error somewhere
|
|
||||||
// else if (state->dmr_mfid == 0x20) sprintf (state->dmr_branding, "%s", "JVC Kenwood");
|
|
||||||
// else if (state->dmr_mfid == 0x04) sprintf (state->dmr_branding, "%s", "Flyde Micro");
|
|
||||||
// else if (state->dmr_mfid == 0x05) sprintf (state->dmr_branding, "%s", "PROD-EL SPA");
|
|
||||||
// else if (state->dmr_mfid == 0x06) sprintf (state->dmr_branding, "%s", "Motorola"); //trident/moto con+
|
|
||||||
// else if (state->dmr_mfid == 0x07) sprintf (state->dmr_branding, "%s", "RADIODATA");
|
|
||||||
// else if (state->dmr_mfid == 0x08) sprintf (state->dmr_branding, "%s", "Hytera");
|
|
||||||
// else if (state->dmr_mfid == 0x09) sprintf (state->dmr_branding, "%s", "ASELSAN");
|
|
||||||
// else if (state->dmr_mfid == 0x0A) sprintf (state->dmr_branding, "%s", "Kirisun");
|
|
||||||
// else if (state->dmr_mfid == 0x0B) sprintf (state->dmr_branding, "%s", "DMR Association");
|
|
||||||
// else if (state->dmr_mfid == 0x13) sprintf (state->dmr_branding, "%s", "EMC S.P.A.");
|
|
||||||
// else if (state->dmr_mfid == 0x1C) sprintf (state->dmr_branding, "%s", "EMC S.P.A.");
|
|
||||||
// else if (state->dmr_mfid == 0x33) sprintf (state->dmr_branding, "%s", "Radio Activity");
|
|
||||||
// else if (state->dmr_mfid == 0x3C) sprintf (state->dmr_branding, "%s", "Radio Activity");
|
|
||||||
// else if (state->dmr_mfid == 0x77) sprintf (state->dmr_branding, "%s", "Vertex Standard");
|
|
||||||
|
|
||||||
//disable so radio id doesn't blink in and out during ncurses and aggressive_framesync
|
//disable so radio id doesn't blink in and out during ncurses and aggressive_framesync
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
//state->lastsrc = 0;
|
//state->lastsrc = 0;
|
||||||
@@ -187,14 +204,13 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
sprintf (state->slot1light, " slot1 ");
|
sprintf (state->slot1light, " slot1 ");
|
||||||
sprintf (state->slot2light, " slot2 ");
|
sprintf (state->slot2light, " slot2 ");
|
||||||
dmrMSBootstrap (opts, state);
|
processDMRvoice (opts, state);
|
||||||
}
|
}
|
||||||
if (opts->dmr_mono == 1 && state->synctype == 32)
|
if (opts->dmr_stereo == 0 && state->synctype == 32)
|
||||||
{
|
{
|
||||||
// fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
||||||
// fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
dmrMSBootstrap (opts, state);
|
|
||||||
}
|
}
|
||||||
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
|
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
|
||||||
{
|
{
|
||||||
@@ -208,20 +224,19 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
|
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
|
||||||
{
|
{
|
||||||
dmrMSData (opts, state);
|
if (opts->dmr_stereo == 0)
|
||||||
// if (opts->dmr_stereo == 0)
|
{
|
||||||
// {
|
closeMbeOutFile (opts, state);
|
||||||
// closeMbeOutFile (opts, state);
|
state->err_str[0] = 0;
|
||||||
// state->err_str[0] = 0;
|
fprintf (stderr, "%s", KRED);
|
||||||
// fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
||||||
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
fprintf (stderr, "%s", KNRM);
|
||||||
// fprintf (stderr, "%s", KNRM);
|
}
|
||||||
// }
|
if (opts->dmr_stereo == 1)
|
||||||
// if (opts->dmr_stereo == 1)
|
{
|
||||||
// {
|
dmrMSData (opts, state);
|
||||||
// dmrMSData (opts, state);
|
|
||||||
|
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -232,7 +247,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
sprintf (state->slot1light, " slot1 ");
|
sprintf (state->slot1light, " slot1 ");
|
||||||
sprintf (state->slot2light, " slot2 ");
|
sprintf (state->slot2light, " slot2 ");
|
||||||
state->payload_miP = 0; //zero out when switching back to data sync
|
state->payload_miP = 0; //zero out when switching back to data sync
|
||||||
dmr_data_sync (opts, state);
|
processDMRdata (opts, state);
|
||||||
}
|
}
|
||||||
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
|
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
|
||||||
if (opts->dmr_stereo == 1)
|
if (opts->dmr_stereo == 1)
|
||||||
@@ -241,7 +256,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
sprintf (state->slot1light, " slot1 ");
|
sprintf (state->slot1light, " slot1 ");
|
||||||
sprintf (state->slot2light, " slot2 ");
|
sprintf (state->slot2light, " slot2 ");
|
||||||
state->payload_miP = 0; //zero out when switching back to data sync
|
state->payload_miP = 0; //zero out when switching back to data sync
|
||||||
dmr_data_sync (opts, state);
|
processDMRdata (opts, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -272,9 +287,9 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
else if ((state->synctype == 14) || (state->synctype == 15))
|
||||||
{
|
{
|
||||||
//state->nac = 0;
|
state->nac = 0;
|
||||||
//state->lastsrc = 0;
|
state->lastsrc = 0;
|
||||||
//state->lasttg = 0;
|
state->lasttg = 0;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
if (opts->verbose > 0)
|
if (opts->verbose > 0)
|
||||||
@@ -291,12 +306,6 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
processProVoice (opts, state);
|
processProVoice (opts, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//edacs
|
|
||||||
else if ((state->synctype == 37) || (state->synctype == 38))
|
|
||||||
{
|
|
||||||
edacs (opts, state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//ysf
|
//ysf
|
||||||
else if ((state->synctype == 30) || (state->synctype == 31))
|
else if ((state->synctype == 30) || (state->synctype == 31))
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
147
src/dsd_import.c
147
src/dsd_import.c
@@ -1,147 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#define BSIZE 999
|
|
||||||
|
|
||||||
int csvGroupImport(dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
char filename[1024] = "filename.csv";
|
|
||||||
sprintf (filename, "%s", opts->group_in_file);
|
|
||||||
//filename[1023] = '\0'; //necessary?
|
|
||||||
char buffer[BSIZE];
|
|
||||||
FILE * fp;
|
|
||||||
fp = fopen(filename, "r");
|
|
||||||
if (fp == NULL) {
|
|
||||||
printf("Unable to open group file '%s'\n", filename);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
int row_count = 0;
|
|
||||||
int field_count = 0;
|
|
||||||
long int group_number = 0; //local group number for array index value
|
|
||||||
int i = 0;
|
|
||||||
while (fgets(buffer, BSIZE, fp)) {
|
|
||||||
field_count = 0;
|
|
||||||
row_count++;
|
|
||||||
if (row_count == 1)
|
|
||||||
continue; //don't want labels
|
|
||||||
char * field = strtok(buffer, ","); //seperate by comma
|
|
||||||
while (field) {
|
|
||||||
|
|
||||||
if (field_count == 0)
|
|
||||||
{
|
|
||||||
//group_number = atol(field);
|
|
||||||
state->group_array[i].groupNumber = atol(field);
|
|
||||||
fprintf (stderr, "%ld, ", state->group_array[i].groupNumber);
|
|
||||||
}
|
|
||||||
if (field_count == 1)
|
|
||||||
{
|
|
||||||
strcpy(state->group_array[i].groupMode, field);
|
|
||||||
fprintf (stderr, "%s, ", state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
if (field_count == 2)
|
|
||||||
{
|
|
||||||
strcpy(state->group_array[i].groupName, field);
|
|
||||||
fprintf (stderr, "%s ", state->group_array[i].groupName);
|
|
||||||
}
|
|
||||||
|
|
||||||
field = strtok(NULL, ",");
|
|
||||||
field_count++;
|
|
||||||
}
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
i++;
|
|
||||||
state->group_tally++;
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//LCN import for EDACS, migrated to channel map (channel map does both)
|
|
||||||
int csvLCNImport(dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
char filename[1024] = "filename.csv";
|
|
||||||
sprintf (filename, "%s", opts->lcn_in_file);
|
|
||||||
//filename[1023] = '\0'; //necessary?
|
|
||||||
char buffer[BSIZE];
|
|
||||||
FILE * fp;
|
|
||||||
fp = fopen(filename, "r");
|
|
||||||
if (fp == NULL) {
|
|
||||||
printf("Unable to open lcn file '%s'\n", filename);
|
|
||||||
//have this return -1 and handle it inside of main
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
int row_count = 0;
|
|
||||||
int field_count = 0;
|
|
||||||
|
|
||||||
while (fgets(buffer, BSIZE, fp)) {
|
|
||||||
field_count = 0;
|
|
||||||
row_count++;
|
|
||||||
if (row_count == 1)
|
|
||||||
continue; //don't want labels
|
|
||||||
char * field = strtok(buffer, ","); //seperate by comma
|
|
||||||
while (field) {
|
|
||||||
|
|
||||||
state->trunk_lcn_freq[field_count] = atol (field);
|
|
||||||
state->lcn_freq_count++; //keep tally of number of Frequencies imported
|
|
||||||
fprintf (stderr, "LCN [%d] [%ld]", field_count+1, state->trunk_lcn_freq[field_count]);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
|
|
||||||
|
|
||||||
field = strtok(NULL, ",");
|
|
||||||
field_count++;
|
|
||||||
}
|
|
||||||
fprintf (stderr, "LCN Count %d\n", state->lcn_freq_count);
|
|
||||||
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import
|
|
||||||
{
|
|
||||||
char filename[1024] = "filename.csv";
|
|
||||||
sprintf (filename, "%s", opts->chan_in_file);
|
|
||||||
|
|
||||||
char buffer[BSIZE];
|
|
||||||
FILE * fp;
|
|
||||||
fp = fopen(filename, "r");
|
|
||||||
if (fp == NULL) {
|
|
||||||
printf("Unable to open channel map file '%s'\n", filename);
|
|
||||||
//have this return -1 and handle it inside of main
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
int row_count = 0;
|
|
||||||
int field_count = 0;
|
|
||||||
|
|
||||||
long int chan_number;
|
|
||||||
|
|
||||||
while (fgets(buffer, BSIZE, fp)) {
|
|
||||||
field_count = 0;
|
|
||||||
row_count++;
|
|
||||||
if (row_count == 1)
|
|
||||||
continue; //don't want labels
|
|
||||||
char * field = strtok(buffer, ","); //seperate by comma
|
|
||||||
while (field) {
|
|
||||||
|
|
||||||
if (field_count == 0)
|
|
||||||
{
|
|
||||||
sscanf (field, "%ld", &chan_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field_count == 1)
|
|
||||||
{
|
|
||||||
sscanf (field, "%ld", &state->trunk_chan_map[chan_number]);
|
|
||||||
//adding this should be compatible with EDACS, test and obsolete the LCN Import function if desired
|
|
||||||
sscanf (field, "%ld", &state->trunk_lcn_freq[state->lcn_freq_count]);
|
|
||||||
state->lcn_freq_count++; //keep tally of number of Frequencies imported
|
|
||||||
}
|
|
||||||
|
|
||||||
field = strtok(NULL, ",");
|
|
||||||
field_count++;
|
|
||||||
}
|
|
||||||
fprintf (stderr, "Channel [%05ld] [%09ld]", chan_number, state->trunk_chan_map[chan_number]);
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
1105
src/dsd_main.c
1105
src/dsd_main.c
File diff suppressed because it is too large
Load Diff
113
src/dsd_mbe.c
113
src/dsd_mbe.c
@@ -128,39 +128,15 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
|
|
||||||
|
|
||||||
for (i = 0; i < 88; i++)
|
for (i = 0; i < 88; i++)
|
||||||
{
|
|
||||||
imbe_d[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 49; i++)
|
|
||||||
{
|
|
||||||
ambe_d[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//set playback mode for this frame
|
|
||||||
char mode[8];
|
|
||||||
|
|
||||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
|
||||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
|
||||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
|
||||||
|
|
||||||
int groupNumber = 0;
|
|
||||||
|
|
||||||
if (state->currentslot == 0) groupNumber = state->lasttg;
|
|
||||||
else groupNumber = state->lasttgR;
|
|
||||||
|
|
||||||
for (i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == groupNumber)
|
|
||||||
{
|
{
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
imbe_d[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//set flag to not play audio this time, but won't prevent writing to wav files
|
for (i = 0; i < 49; i++)
|
||||||
if (strcmp(mode, "B") == 0) opts->audio_out = 0;
|
{
|
||||||
|
ambe_d[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
//end set playback mode for this frame
|
|
||||||
|
|
||||||
if ((state->synctype == 0) || (state->synctype == 1))
|
if ((state->synctype == 0) || (state->synctype == 1))
|
||||||
{
|
{
|
||||||
@@ -225,7 +201,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
saveAmbe2450Data (opts, state, ambe_d);
|
saveAmbe2450Data (opts, state, ambe_d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 28) || (state->synctype == 29)) //was 8 and 9
|
else if ((state->synctype == 8) || (state->synctype == 9))
|
||||||
{
|
{
|
||||||
|
|
||||||
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||||
@@ -269,7 +245,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//stereo slots and slot 0 (left slot)
|
//stereo slots and slot 0 (left slot)
|
||||||
if (state->currentslot == 0) //&& opts->dmr_stereo == 1
|
if (state->currentslot == 0 && opts->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
|
|
||||||
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||||
@@ -364,7 +340,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//stereo slots and slot 1 (right slot)
|
//stereo slots and slot 1 (right slot)
|
||||||
if (state->currentslot == 1) //&& opts->dmr_stereo == 1
|
if (state->currentslot == 1 && opts->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
|
|
||||||
state->errsR = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
state->errsR = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||||
@@ -459,19 +435,19 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
|
|
||||||
//X2-TDMA? Not sure what still makes it this far to run under Framef
|
//X2-TDMA? Not sure what still makes it this far to run under Framef
|
||||||
// if (opts->dmr_stereo == 0)
|
if (opts->dmr_stereo == 0)
|
||||||
// {
|
{
|
||||||
// mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
// if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
// {
|
{
|
||||||
// PrintAMBEData (opts, state, ambe_d);
|
PrintAMBEData (opts, state, ambe_d);
|
||||||
// }
|
}
|
||||||
// //only save MBE files if not enc or unmuted, THIS does not seem to work for some reason
|
//only save MBE files if not enc or unmuted, THIS does not seem to work for some reason
|
||||||
// if (opts->mbe_out_f != NULL && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
if (opts->mbe_out_f != NULL && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
||||||
// {
|
{
|
||||||
// saveAmbe2450Data (opts, state, ambe_d);
|
saveAmbe2450Data (opts, state, ambe_d);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -480,7 +456,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
int enc_bit = 0;
|
int enc_bit = 0;
|
||||||
//end enc check
|
//end enc check
|
||||||
|
|
||||||
if ( (opts->dmr_mono == 1 || opts->dmr_stereo == 1) && state->currentslot == 0) //all mono traffic routed through 'left'
|
if (opts->dmr_stereo == 1 && state->currentslot == 0)
|
||||||
{
|
{
|
||||||
enc_bit = (state->dmr_so >> 6) & 0x1;
|
enc_bit = (state->dmr_so >> 6) & 0x1;
|
||||||
if (enc_bit == 1)
|
if (enc_bit == 1)
|
||||||
@@ -504,24 +480,6 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
|
|
||||||
if (opts->reverse_mute == 1)
|
|
||||||
{
|
|
||||||
if (state->dmr_encL == 0)
|
|
||||||
{
|
|
||||||
state->dmr_encL = 1;
|
|
||||||
opts->unmute_encrypted_p25 = 0;
|
|
||||||
opts->dmr_mute_encL = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->dmr_encL = 0;
|
|
||||||
opts->unmute_encrypted_p25 = 1;
|
|
||||||
opts->dmr_mute_encL = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//end reverse mute test
|
|
||||||
|
|
||||||
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
||||||
{
|
{
|
||||||
state->debug_audio_errors += state->errs2;
|
state->debug_audio_errors += state->errs2;
|
||||||
@@ -536,7 +494,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->dmr_stereo == 1 && state->currentslot == 1)
|
if (opts->dmr_stereo == 1 && state->currentslot == 1)
|
||||||
{
|
{
|
||||||
enc_bit = (state->dmr_soR >> 6) & 0x1;
|
enc_bit = (state->dmr_soR >> 6) & 0x1;
|
||||||
if (enc_bit == 0x1)
|
if (enc_bit == 0x1)
|
||||||
@@ -560,24 +518,6 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
|
|
||||||
if (opts->reverse_mute == 1)
|
|
||||||
{
|
|
||||||
if (state->dmr_encR == 0)
|
|
||||||
{
|
|
||||||
state->dmr_encR = 1;
|
|
||||||
opts->unmute_encrypted_p25 = 0;
|
|
||||||
opts->dmr_mute_encR = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->dmr_encR = 0;
|
|
||||||
opts->unmute_encrypted_p25 = 1;
|
|
||||||
opts->dmr_mute_encR = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//end reverse mute test
|
|
||||||
|
|
||||||
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
||||||
{
|
{
|
||||||
state->debug_audio_errorsR += state->errs2R;
|
state->debug_audio_errorsR += state->errs2R;
|
||||||
@@ -593,7 +533,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
|
|
||||||
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
|
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
|
||||||
if (opts->dmr_mono == 0 && opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
if (opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
||||||
{
|
{
|
||||||
state->debug_audio_errors += state->errs2;
|
state->debug_audio_errors += state->errs2;
|
||||||
if (opts->audio_out == 1)
|
if (opts->audio_out == 1)
|
||||||
@@ -632,8 +572,5 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//reset audio out flag for next repitition
|
|
||||||
if (strcmp(mode, "B") == 0) opts->audio_out = 1;
|
|
||||||
//restore flag for null output type
|
|
||||||
if (opts->audio_out_type == 9) opts->audio_out = 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
1175
src/dsd_ncurses.c
1175
src/dsd_ncurses.c
File diff suppressed because it is too large
Load Diff
276
src/dsd_rigctl.c
276
src/dsd_rigctl.c
@@ -1,276 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* dsd_rigctl.c
|
|
||||||
* Simple RIGCTL Client for DSD (remote control of GQRX, SDR++, etc)
|
|
||||||
*
|
|
||||||
* Portions from https://github.com/neural75/gqrx-scanner
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-10 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
|
|
||||||
//UDP Specific
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#define BUFSIZE 1024
|
|
||||||
#define FREQ_MAX 4096
|
|
||||||
#define SAVED_FREQ_MAX 1000
|
|
||||||
#define TAG_MAX 100
|
|
||||||
|
|
||||||
//
|
|
||||||
// error - wrapper for perror
|
|
||||||
//
|
|
||||||
void error(char *msg) {
|
|
||||||
perror(msg);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Connect
|
|
||||||
//
|
|
||||||
int Connect (char *hostname, int portno)
|
|
||||||
{
|
|
||||||
int sockfd, n;
|
|
||||||
struct sockaddr_in serveraddr;
|
|
||||||
struct hostent *server;
|
|
||||||
|
|
||||||
|
|
||||||
/* socket: create the socket */
|
|
||||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (sockfd < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"ERROR opening socket\n");
|
|
||||||
error("ERROR opening socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* gethostbyname: get the server's DNS entry */
|
|
||||||
server = gethostbyname(hostname);
|
|
||||||
if (server == NULL) {
|
|
||||||
fprintf(stderr,"ERROR, no such host as %s\n", hostname);
|
|
||||||
//exit(0);
|
|
||||||
return (0); //return 0, check on other end and configure pulse input
|
|
||||||
}
|
|
||||||
|
|
||||||
/* build the server's Internet address */
|
|
||||||
bzero((char *) &serveraddr, sizeof(serveraddr));
|
|
||||||
serveraddr.sin_family = AF_INET;
|
|
||||||
bcopy((char *)server->h_addr,
|
|
||||||
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
|
|
||||||
serveraddr.sin_port = htons(portno);
|
|
||||||
|
|
||||||
/* connect: create a connection with the server */
|
|
||||||
if (connect(sockfd, (const struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"ERROR opening socket\n");
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sockfd;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Send
|
|
||||||
//
|
|
||||||
bool Send(int sockfd, char *buf)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
|
|
||||||
n = write(sockfd, buf, strlen(buf));
|
|
||||||
if (n < 0)
|
|
||||||
error("ERROR writing to socket");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Recv
|
|
||||||
//
|
|
||||||
bool Recv(int sockfd, char *buf)
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
|
|
||||||
n = read(sockfd, buf, BUFSIZE);
|
|
||||||
if (n < 0)
|
|
||||||
error("ERROR reading from socket");
|
|
||||||
buf[n]= '\0';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// GQRX Protocol
|
|
||||||
//
|
|
||||||
long int GetCurrentFreq(int sockfd)
|
|
||||||
{
|
|
||||||
long int freq = 0;
|
|
||||||
char buf[BUFSIZE];
|
|
||||||
char * ptr;
|
|
||||||
char * token;
|
|
||||||
|
|
||||||
Send(sockfd, "f\n");
|
|
||||||
Recv(sockfd, buf);
|
|
||||||
|
|
||||||
if (strcmp(buf, "RPRT 1") == 0 )
|
|
||||||
return freq;
|
|
||||||
|
|
||||||
token = strtok (buf, "\n");
|
|
||||||
freq = strtol (token, &ptr, 10);
|
|
||||||
// fprintf (stderr, "\nRIGCTL VFO Freq: [%ld]\n", freq);
|
|
||||||
return freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SetFreq(int sockfd, long int freq)
|
|
||||||
{
|
|
||||||
char buf[BUFSIZE];
|
|
||||||
|
|
||||||
sprintf (buf, "F %ld\n", freq);
|
|
||||||
Send(sockfd, buf);
|
|
||||||
Recv(sockfd, buf);
|
|
||||||
|
|
||||||
if (strcmp(buf, "RPRT 1") == 0 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SetModulation(int sockfd, int bandwidth)
|
|
||||||
{
|
|
||||||
char buf[BUFSIZE];
|
|
||||||
//the bandwidth is now a user/system based configurable variable
|
|
||||||
sprintf (buf, "M FM %d\n", bandwidth);
|
|
||||||
Send(sockfd, buf);
|
|
||||||
Recv(sockfd, buf);
|
|
||||||
|
|
||||||
if (strcmp(buf, "RPRT 1") == 0 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetSignalLevel(int sockfd, double *dBFS)
|
|
||||||
{
|
|
||||||
char buf[BUFSIZE];
|
|
||||||
|
|
||||||
Send(sockfd, "l\n");
|
|
||||||
Recv(sockfd, buf);
|
|
||||||
|
|
||||||
if (strcmp(buf, "RPRT 1") == 0 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
sscanf(buf, "%lf", dBFS);
|
|
||||||
*dBFS = round((*dBFS) * 10)/10;
|
|
||||||
|
|
||||||
if (*dBFS == 0.0)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetSquelchLevel(int sockfd, double *dBFS)
|
|
||||||
{
|
|
||||||
char buf[BUFSIZE];
|
|
||||||
|
|
||||||
Send(sockfd, "l SQL\n");
|
|
||||||
Recv(sockfd, buf);
|
|
||||||
|
|
||||||
if (strcmp(buf, "RPRT 1") == 0 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
sscanf(buf, "%lf", dBFS);
|
|
||||||
*dBFS = round((*dBFS) * 10)/10;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SetSquelchLevel(int sockfd, double dBFS)
|
|
||||||
{
|
|
||||||
char buf[BUFSIZE];
|
|
||||||
|
|
||||||
sprintf (buf, "L SQL %f\n", dBFS);
|
|
||||||
Send(sockfd, buf);
|
|
||||||
Recv(sockfd, buf);
|
|
||||||
|
|
||||||
if (strcmp(buf, "RPRT 1") == 0 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// GetSignalLevelEx
|
|
||||||
// Get a bunch of sample with some delay and calculate the mean value
|
|
||||||
//
|
|
||||||
bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp)
|
|
||||||
{
|
|
||||||
double temp_level;
|
|
||||||
*dBFS = 0;
|
|
||||||
int errors = 0;
|
|
||||||
for (int i = 0; i < n_samp; i++)
|
|
||||||
{
|
|
||||||
if ( GetSignalLevel(sockfd, &temp_level) )
|
|
||||||
*dBFS = *dBFS + temp_level;
|
|
||||||
else
|
|
||||||
errors++;
|
|
||||||
usleep(1000);
|
|
||||||
}
|
|
||||||
*dBFS = *dBFS / (n_samp - errors);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//shoe in UDP input connection here...still having issues that I don't know how to resolve
|
|
||||||
int UDPBind (char *hostname, int portno)
|
|
||||||
{
|
|
||||||
int sockfd, n;
|
|
||||||
struct sockaddr_in serveraddr, client_addr;
|
|
||||||
struct hostent *server;
|
|
||||||
|
|
||||||
/* socket: create the socket */
|
|
||||||
//UDP socket
|
|
||||||
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
||||||
|
|
||||||
if (sockfd < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"ERROR opening UDP socket\n");
|
|
||||||
error("ERROR opening UDP socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* build the server's Internet address */
|
|
||||||
bzero((char *) &serveraddr, sizeof(serveraddr));
|
|
||||||
serveraddr.sin_family = AF_INET;
|
|
||||||
serveraddr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY
|
|
||||||
serveraddr.sin_port = htons(portno);
|
|
||||||
|
|
||||||
//Bind socket to listening
|
|
||||||
if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
|
|
||||||
perror("ERROR on binding UDP Port");
|
|
||||||
}
|
|
||||||
|
|
||||||
return sockfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
|
||||||
{
|
|
||||||
int handle;
|
|
||||||
unsigned short udp_port = opts->rtl_udp_port;
|
|
||||||
char data[5] = {0}; //data buffer size is 5 for UDP frequency tuning
|
|
||||||
struct sockaddr_in address;
|
|
||||||
|
|
||||||
uint32_t new_freq = frequency;
|
|
||||||
opts->rtlsdr_center_freq = new_freq; //for ncurses terminal display after rtl is started up
|
|
||||||
|
|
||||||
handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
||||||
|
|
||||||
data[0] = 0;
|
|
||||||
data[1] = new_freq & 0xFF;
|
|
||||||
data[2] = (new_freq >> 8) & 0xFF;
|
|
||||||
data[3] = (new_freq >> 16) & 0xFF;
|
|
||||||
data[4] = (new_freq >> 24) & 0xFF;
|
|
||||||
|
|
||||||
memset((char * ) & address, 0, sizeof(address));
|
|
||||||
address.sin_family = AF_INET;
|
|
||||||
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later
|
|
||||||
address.sin_port = htons(udp_port);
|
|
||||||
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
|
||||||
}
|
|
||||||
@@ -100,34 +100,32 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
//stdin only, wav files moving to new number
|
else if (opts->audio_in_type == 1) {
|
||||||
else if (opts->audio_in_type == 1)
|
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||||
{
|
//fprintf (stderr, "..");
|
||||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
|
||||||
if(result == 0)
|
{
|
||||||
{
|
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
|
||||||
//cleanupAndExit (opts, state);
|
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
|
||||||
sf_close(opts->audio_in_file);
|
}
|
||||||
opts->audio_in_type = 0; //set input type
|
if(result == 0) {
|
||||||
openPulseInput(opts); //open pulse inpput
|
cleanupAndExit (opts, state);
|
||||||
sample = 0; //send zero sample
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//wav files, same but using seperate value so we can still manipulate ncurses menu
|
|
||||||
//since we can not worry about getch/stdin conflict
|
|
||||||
else if (opts->audio_in_type == 2)
|
else if (opts->audio_in_type == 2)
|
||||||
{
|
{
|
||||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
#ifdef USE_PORTAUDIO
|
||||||
if(result == 0)
|
PaError err = Pa_ReadStream( opts->audio_in_pa_stream, &sample, 1 );
|
||||||
|
if( err != paNoError )
|
||||||
{
|
{
|
||||||
|
fprintf( stderr, "An error occured while using the portaudio input stream\n" );
|
||||||
sf_close(opts->audio_in_file);
|
fprintf( stderr, "Error number: %d\n", err );
|
||||||
opts->audio_in_type = 0; //set input type
|
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||||
openPulseInput(opts); //open pulse inpput
|
|
||||||
sample = 0; //send zero sample
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
#ifdef USE_RTLSDR
|
#ifdef USE_RTLSDR
|
||||||
@@ -142,30 +140,12 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
//tcp socket input from SDR++
|
|
||||||
else if (opts->audio_in_type == 8)
|
|
||||||
{
|
|
||||||
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
|
||||||
if(result == 0) {
|
|
||||||
sf_close(opts->tcp_file_in);
|
|
||||||
opts->audio_in_type = 0; //set input type
|
|
||||||
openPulseInput(opts); //open pulse inpput
|
|
||||||
sample = 0; //zero sample on bad result, keep the ball rolling
|
|
||||||
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//UDP Socket input...not working correct. Reads samples, but no sync
|
|
||||||
else if (opts->audio_in_type == 6)
|
|
||||||
{
|
|
||||||
//I think this doesn't get the entire dgram when we run sf_read_short on the udp dgram
|
|
||||||
result = sf_read_short(opts->udp_file_in, &sample, 1);
|
|
||||||
// if (sample != 0)
|
|
||||||
// fprintf (stderr, "Result = %d Sample = %d \n", result, sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef TRACE_DSD
|
||||||
|
state->debug_sample_index++;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// printf ("res: %zd\n, offset: %lld", result, sf_seek(opts->audio_in_file, 0, SEEK_CUR));
|
||||||
if (opts->use_cosine_filter)
|
if (opts->use_cosine_filter)
|
||||||
{
|
{
|
||||||
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34)
|
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34)
|
||||||
@@ -178,25 +158,18 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
state->lastsynctype == 20 || state->lastsynctype == 21 ||
|
state->lastsynctype == 20 || state->lastsynctype == 21 ||
|
||||||
state->lastsynctype == 22 || state->lastsynctype == 23 ||
|
state->lastsynctype == 22 || state->lastsynctype == 23 ||
|
||||||
state->lastsynctype == 24 || state->lastsynctype == 25 ||
|
state->lastsynctype == 24 || state->lastsynctype == 25 ||
|
||||||
state->lastsynctype == 26 || state->lastsynctype == 27 ||
|
state->lastsynctype == 26 || state->lastsynctype == 27 ) //||
|
||||||
state->lastsynctype == 28 || state->lastsynctype == 29 ) //||
|
//state->lastsynctype == 35 || state->lastsynctype == 36) //phase 2
|
||||||
//state->lastsynctype == 35 || state->lastsynctype == 36) //phase 2 C4FM disc tap input
|
|
||||||
{
|
{
|
||||||
//if(state->samplesPerSymbol == 20)
|
if(state->samplesPerSymbol == 20)
|
||||||
if(opts->frame_nxdn48 == 1)
|
|
||||||
{
|
{
|
||||||
sample = nxdn_filter(sample);
|
sample = nxdn_filter(sample);
|
||||||
}
|
}
|
||||||
//else if (state->lastsynctype >= 20 && state->lastsynctype <=27) //this the right range?
|
|
||||||
else if (opts->frame_dpmr == 1)
|
|
||||||
{
|
|
||||||
sample = dpmr_filter(sample);
|
|
||||||
}
|
|
||||||
else if (state->samplesPerSymbol == 8) //phase 2 cqpsk
|
else if (state->samplesPerSymbol == 8) //phase 2 cqpsk
|
||||||
{
|
{
|
||||||
//sample = dmr_filter(sample); //work on filter later
|
//sample = dmr_filter(sample); //work on filter later
|
||||||
}
|
}
|
||||||
else // the 12.5KHz NXDN filter is the same as the DMR filter...hopefully
|
else // the 12.5KHz NXDN filter is the same as the DMR filter
|
||||||
{
|
{
|
||||||
sample = dmr_filter(sample);
|
sample = dmr_filter(sample);
|
||||||
}
|
}
|
||||||
@@ -413,11 +386,11 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
if (state->rf_mod == 2) //GFSK
|
if (state->rf_mod == 2) //GFSK
|
||||||
{
|
{
|
||||||
symbol = state->symbolc;
|
symbol = state->symbolc;
|
||||||
if (state->symbolc == 0 )
|
if (state->symbolc == 0 && state->synctype >= 0)
|
||||||
{
|
{
|
||||||
symbol = -3; //-1
|
symbol = -3; //-1
|
||||||
}
|
}
|
||||||
if (state->symbolc == 1 )
|
if (state->symbolc == 1 && state->synctype >= 0)
|
||||||
{
|
{
|
||||||
symbol = -1; //-3
|
symbol = -1; //-3
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,19 +54,65 @@ upsample (dsd_state * state, float invalue)
|
|||||||
*outbuf1 = d;
|
*outbuf1 = d;
|
||||||
outbuf1++;
|
outbuf1++;
|
||||||
|
|
||||||
//only if enabled, produces crackling/buzzing sounds when input type is not pulse audio (reason still unknown)
|
if (state->currentslot == 1 && state->dmr_stereo == 1)
|
||||||
//also, could be improved to provide better audio in the future
|
|
||||||
if (state->audio_smoothing == 1)
|
|
||||||
{
|
{
|
||||||
if (state->currentslot == 1 && state->dmr_stereo == 1)
|
if (state->audio_out_idx2R > 24)
|
||||||
{
|
{
|
||||||
if (state->audio_out_idx2R > 24)
|
// smoothing
|
||||||
|
outbuf1 -= 16;
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
// smoothing
|
for (i = 0; i < 6; i++)
|
||||||
outbuf1 -= 16;
|
{
|
||||||
for (j = 0; j < 4; j++)
|
sum = 0;
|
||||||
|
outbuf1 -= 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 += 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 += 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 -= 2;
|
||||||
|
*outbuf1 = (sum / (float) 3);
|
||||||
|
outbuf1++;
|
||||||
|
}
|
||||||
|
outbuf1 -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state->currentslot == 0 && state->dmr_stereo == 1)
|
||||||
|
{
|
||||||
|
if (state->audio_out_idx2 > 24)
|
||||||
|
{
|
||||||
|
// smoothing
|
||||||
|
outbuf1 -= 16;
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
for (i = 0; i < 6; i++)
|
for (i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
sum = 0;
|
||||||
|
outbuf1 -= 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 += 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 += 2;
|
||||||
|
sum += (int)*outbuf1;
|
||||||
|
outbuf1 -= 2;
|
||||||
|
*outbuf1 = (sum / (float) 3);
|
||||||
|
outbuf1++;
|
||||||
|
}
|
||||||
|
outbuf1 -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state->dmr_stereo == 0)
|
||||||
|
{
|
||||||
|
if (state->audio_out_idx2 > 24)
|
||||||
|
{
|
||||||
|
// smoothing
|
||||||
|
outbuf1 -= 16;
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
sum = 0;
|
sum = 0;
|
||||||
outbuf1 -= 2;
|
outbuf1 -= 2;
|
||||||
@@ -81,58 +127,6 @@ upsample (dsd_state * state, float invalue)
|
|||||||
}
|
}
|
||||||
outbuf1 -= 8;
|
outbuf1 -= 8;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state->currentslot == 0 && state->dmr_stereo == 1)
|
|
||||||
{
|
|
||||||
if (state->audio_out_idx2 > 24)
|
|
||||||
{
|
|
||||||
// smoothing
|
|
||||||
outbuf1 -= 16;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
*outbuf1 = (sum / (float) 3);
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
outbuf1 -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state->dmr_stereo == 0)
|
|
||||||
{
|
|
||||||
if (state->audio_out_idx2 > 24)
|
|
||||||
{
|
|
||||||
// smoothing
|
|
||||||
outbuf1 -= 16;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
*outbuf1 = (sum / (float) 3);
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
outbuf1 -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
517
src/edacs-bch3.c
517
src/edacs-bch3.c
@@ -1,517 +0,0 @@
|
|||||||
/*
|
|
||||||
* File: bch3.h
|
|
||||||
* Title: Encoder/decoder for binary BCH codes in C (Version 3.1)
|
|
||||||
* Author: Robert Morelos-Zaragoza
|
|
||||||
* Date: August 1994-June 13, 1997
|
|
||||||
* Mod: December 14, 2021, for EDACS-FM BCH Polynomial Error Generation and Detection
|
|
||||||
* Mod2: October 2022, for DSD-FME port of EDACS-FM
|
|
||||||
* Source: www.eccpage.com
|
|
||||||
*
|
|
||||||
* =============== Encoder/Decoder for binary BCH codes in C =================
|
|
||||||
*
|
|
||||||
* Version 1: Original program. The user provides the generator polynomial
|
|
||||||
* of the code (cumbersome!).
|
|
||||||
* Version 2: Computes the generator polynomial of the code.
|
|
||||||
* Version 3: No need to input the coefficients of a primitive polynomial of
|
|
||||||
* degree m, used to construct the Galois Field GF(2**m). The
|
|
||||||
* program now works for any binary BCH code of length such that:
|
|
||||||
* 2**(m-1) - 1 < length <= 2**m - 1
|
|
||||||
*
|
|
||||||
* Note: You may have to change the size of the arrays to make it work.
|
|
||||||
*
|
|
||||||
* The encoding and decoding methods used in this program are based on the
|
|
||||||
* book "Error Control Coding: Fundamentals and Applications", by Lin and
|
|
||||||
* Costello, Prentice Hall, 1983.
|
|
||||||
*
|
|
||||||
* Thanks to Patrick Boyle (pboyle@era.com) for his observation that 'bch2.c'
|
|
||||||
* did not work for lengths other than 2**m-1 which led to this new version.
|
|
||||||
* Portions of this program are from 'rs.c', a Reed-Solomon encoder/decoder
|
|
||||||
* in C, written by Simon Rockliff (simon@augean.ua.oz.au) on 21/9/89. The
|
|
||||||
* previous version of the BCH encoder/decoder in C, 'bch2.c', was written by
|
|
||||||
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) on 5/19/92.
|
|
||||||
*
|
|
||||||
* NOTE:
|
|
||||||
* The author is not responsible for any malfunctioning of
|
|
||||||
* this program, nor for any damage caused by it. Please include the
|
|
||||||
* original program along with these comments in any redistribution.
|
|
||||||
*
|
|
||||||
* For more information, suggestions, or other ideas on implementing error
|
|
||||||
* correcting codes, please contact me at:
|
|
||||||
*
|
|
||||||
* Robert Morelos-Zaragoza
|
|
||||||
* 5120 Woodway, Suite 7036
|
|
||||||
* Houston, Texas 77056
|
|
||||||
*
|
|
||||||
* email: r.morelos-zaragoza@ieee.org
|
|
||||||
*
|
|
||||||
* COPYRIGHT NOTICE: This computer program is free for non-commercial purposes.
|
|
||||||
* You may implement this program for any non-commercial application. You may
|
|
||||||
* also implement this program for commercial purposes, provided that you
|
|
||||||
* obtain my written permission. Any modification of this program is covered
|
|
||||||
* by this copyright.
|
|
||||||
*
|
|
||||||
* == Copyright (c) 1994-7, Robert Morelos-Zaragoza. All rights reserved. ==
|
|
||||||
*
|
|
||||||
* m = order of the Galois field GF(2**m)
|
|
||||||
* n = 2**m - 1 = size of the multiplicative group of GF(2**m)
|
|
||||||
* length = length of the BCH code
|
|
||||||
* t = error correcting capability (max. no. of errors the code corrects)
|
|
||||||
* d = 2*t + 1 = designed min. distance = no. of consecutive roots of g(x) + 1
|
|
||||||
* k = n - deg(g(x)) = dimension (no. of information bits/codeword) of the code
|
|
||||||
* p[] = coefficients of a primitive polynomial used to generate GF(2**m)
|
|
||||||
* g[] = coefficients of the generator polynomial, g(x)
|
|
||||||
* alpha_to [] = log table of GF(2**m)
|
|
||||||
* index_of[] = antilog table of GF(2**m)
|
|
||||||
* ddata[] = information bits = coefficients of data polynomial, i(x)
|
|
||||||
* bb[] = coefficients of redundancy polynomial x^(length-k) i(x) modulo g(x)
|
|
||||||
* numerr = number of errors
|
|
||||||
* errpos[] = error positions
|
|
||||||
* recd[] = coefficients of the received polynomial
|
|
||||||
* decerror = number of decoding errors (in _message_ positions)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
int m, n, length, k, t, d;
|
|
||||||
int p[21];
|
|
||||||
int alpha_to[1048576], index_of[1048576], g[548576];
|
|
||||||
int recd[1048576], ddata[1048576], bb[548576];
|
|
||||||
int seed;
|
|
||||||
int numerr, errpos[1024], decerror = 0;
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
read_p()
|
|
||||||
/*
|
|
||||||
* Read m, the degree of a primitive polynomial p(x) used to compute the
|
|
||||||
* Galois field GF(2**m). Get precomputed coefficients p[] of p(x). Read
|
|
||||||
* the code length.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
int i, ninf;
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
m = 6;
|
|
||||||
} while ( !(m>1) || !(m<21) );
|
|
||||||
for (i=1; i<m; i++)
|
|
||||||
p[i] = 0;
|
|
||||||
p[0] = p[m] = 1;
|
|
||||||
if (m == 2) p[1] = 1;
|
|
||||||
else if (m == 3) p[1] = 1;
|
|
||||||
else if (m == 4) p[1] = 1;
|
|
||||||
else if (m == 5) p[2] = 1;
|
|
||||||
else if (m == 6) p[1] = 1;
|
|
||||||
else if (m == 7) p[1] = 1;
|
|
||||||
else if (m == 8) p[4] = p[5] = p[6] = 1;
|
|
||||||
else if (m == 9) p[4] = 1;
|
|
||||||
else if (m == 10) p[3] = 1;
|
|
||||||
else if (m == 11) p[2] = 1;
|
|
||||||
else if (m == 12) p[3] = p[4] = p[7] = 1;
|
|
||||||
else if (m == 13) p[1] = p[3] = p[4] = 1;
|
|
||||||
else if (m == 14) p[1] = p[11] = p[12] = 1;
|
|
||||||
else if (m == 15) p[1] = 1;
|
|
||||||
else if (m == 16) p[2] = p[3] = p[5] = 1;
|
|
||||||
else if (m == 17) p[3] = 1;
|
|
||||||
else if (m == 18) p[7] = 1;
|
|
||||||
else if (m == 19) p[1] = p[5] = p[6] = 1;
|
|
||||||
else if (m == 20) p[3] = 1;
|
|
||||||
|
|
||||||
n = 1;
|
|
||||||
for (i = 0; i <= m; i++) {
|
|
||||||
n *= 2;
|
|
||||||
}
|
|
||||||
n = n / 2 - 1;
|
|
||||||
ninf = (n + 1) / 2 - 1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
|
|
||||||
length = 40;
|
|
||||||
} while ( !((length <= n)&&(length>ninf)) );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
generate_gf()
|
|
||||||
/*
|
|
||||||
* Generate field GF(2**m) from the irreducible polynomial p(X) with
|
|
||||||
* coefficients in p[0]..p[m].
|
|
||||||
*
|
|
||||||
* Lookup tables:
|
|
||||||
* index->polynomial form: alpha_to[] contains j=alpha^i;
|
|
||||||
* polynomial form -> index form: index_of[j=alpha^i] = i
|
|
||||||
*
|
|
||||||
* alpha=2 is the primitive element of GF(2**m)
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
register int i, mask;
|
|
||||||
|
|
||||||
mask = 1;
|
|
||||||
alpha_to[m] = 0;
|
|
||||||
for (i = 0; i < m; i++) {
|
|
||||||
alpha_to[i] = mask;
|
|
||||||
index_of[alpha_to[i]] = i;
|
|
||||||
if (p[i] != 0)
|
|
||||||
alpha_to[m] ^= mask;
|
|
||||||
mask <<= 1;
|
|
||||||
}
|
|
||||||
index_of[alpha_to[m]] = m;
|
|
||||||
mask >>= 1;
|
|
||||||
for (i = m + 1; i < n; i++) {
|
|
||||||
if (alpha_to[i - 1] >= mask)
|
|
||||||
alpha_to[i] = alpha_to[m] ^ ((alpha_to[i - 1] ^ mask) << 1);
|
|
||||||
else
|
|
||||||
alpha_to[i] = alpha_to[i - 1] << 1;
|
|
||||||
index_of[alpha_to[i]] = i;
|
|
||||||
}
|
|
||||||
index_of[0] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
gen_poly()
|
|
||||||
/*
|
|
||||||
* Compute the generator polynomial of a binary BCH code. Fist generate the
|
|
||||||
* cycle sets modulo 2**m - 1, cycle[][] = (i, 2*i, 4*i, ..., 2^l*i). Then
|
|
||||||
* determine those cycle sets that contain integers in the set of (d-1)
|
|
||||||
* consecutive integers {1..(d-1)}. The generator polynomial is calculated
|
|
||||||
* as the product of linear factors of the form (x+alpha^i), for every i in
|
|
||||||
* the above cycle sets.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
register int ii, jj, ll, kaux;
|
|
||||||
register int test, aux, nocycles, root, noterms, rdncy;
|
|
||||||
int cycle[1024][21], size[1024], min[1024], zeros[1024];
|
|
||||||
|
|
||||||
/* Generate cycle sets modulo n, n = 2**m - 1 */
|
|
||||||
cycle[0][0] = 0;
|
|
||||||
size[0] = 1;
|
|
||||||
cycle[1][0] = 1;
|
|
||||||
size[1] = 1;
|
|
||||||
jj = 1; /* cycle set index */
|
|
||||||
if (m > 9) {
|
|
||||||
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
/* Generate the jj-th cycle set */
|
|
||||||
ii = 0;
|
|
||||||
do {
|
|
||||||
ii++;
|
|
||||||
cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % n;
|
|
||||||
size[jj]++;
|
|
||||||
aux = (cycle[jj][ii] * 2) % n;
|
|
||||||
} while (aux != cycle[jj][0]);
|
|
||||||
/* Next cycle set representative */
|
|
||||||
ll = 0;
|
|
||||||
do {
|
|
||||||
ll++;
|
|
||||||
test = 0;
|
|
||||||
for (ii = 1; ((ii <= jj) && (!test)); ii++)
|
|
||||||
/* Examine previous cycle sets */
|
|
||||||
for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++)
|
|
||||||
if (ll == cycle[ii][kaux])
|
|
||||||
test = 1;
|
|
||||||
} while ((test) && (ll < (n - 1)));
|
|
||||||
if (!(test)) {
|
|
||||||
jj++; /* next cycle set index */
|
|
||||||
cycle[jj][0] = ll;
|
|
||||||
size[jj] = 1;
|
|
||||||
}
|
|
||||||
} while (ll < (n - 1));
|
|
||||||
nocycles = jj; /* number of cycle sets modulo n */
|
|
||||||
|
|
||||||
t = 2;
|
|
||||||
|
|
||||||
d = 2 * t + 1;
|
|
||||||
|
|
||||||
/* Search for roots 1, 2, ..., d-1 in cycle sets */
|
|
||||||
kaux = 0;
|
|
||||||
rdncy = 0;
|
|
||||||
for (ii = 1; ii <= nocycles; ii++) {
|
|
||||||
min[kaux] = 0;
|
|
||||||
test = 0;
|
|
||||||
for (jj = 0; ((jj < size[ii]) && (!test)); jj++)
|
|
||||||
for (root = 1; ((root < d) && (!test)); root++)
|
|
||||||
if (root == cycle[ii][jj]) {
|
|
||||||
test = 1;
|
|
||||||
min[kaux] = ii;
|
|
||||||
}
|
|
||||||
if (min[kaux]) {
|
|
||||||
rdncy += size[min[kaux]];
|
|
||||||
kaux++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
noterms = kaux;
|
|
||||||
kaux = 1;
|
|
||||||
for (ii = 0; ii < noterms; ii++)
|
|
||||||
for (jj = 0; jj < size[min[ii]]; jj++) {
|
|
||||||
zeros[kaux] = cycle[min[ii]][jj];
|
|
||||||
kaux++;
|
|
||||||
}
|
|
||||||
|
|
||||||
k = length - rdncy;
|
|
||||||
|
|
||||||
if (k<0)
|
|
||||||
{
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute the generator polynomial */
|
|
||||||
g[0] = alpha_to[zeros[1]];
|
|
||||||
g[1] = 1; /* g(x) = (X + zeros[1]) initially */
|
|
||||||
for (ii = 2; ii <= rdncy; ii++) {
|
|
||||||
g[ii] = 1;
|
|
||||||
for (jj = ii - 1; jj > 0; jj--)
|
|
||||||
if (g[jj] != 0)
|
|
||||||
g[jj] = g[jj - 1] ^ alpha_to[(index_of[g[jj]] + zeros[ii]) % n];
|
|
||||||
else
|
|
||||||
g[jj] = g[jj - 1];
|
|
||||||
g[0] = alpha_to[(index_of[g[0]] + zeros[ii]) % n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
encode_bch()
|
|
||||||
/*
|
|
||||||
* Compute redundacy bb[], the coefficients of b(x). The redundancy
|
|
||||||
* polynomial b(x) is the remainder after dividing x^(length-k)*ddata(x)
|
|
||||||
* by the generator polynomial g(x).
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
register int i, j;
|
|
||||||
register int feedback;
|
|
||||||
|
|
||||||
for (i = 0; i < length - k; i++)
|
|
||||||
bb[i] = 0;
|
|
||||||
for (i = k - 1; i >= 0; i--) {
|
|
||||||
feedback = ddata[i] ^ bb[length - k - 1];
|
|
||||||
if (feedback != 0) {
|
|
||||||
for (j = length - k - 1; j > 0; j--)
|
|
||||||
if (g[j] != 0)
|
|
||||||
bb[j] = bb[j - 1] ^ feedback;
|
|
||||||
else
|
|
||||||
bb[j] = bb[j - 1];
|
|
||||||
bb[0] = g[0] && feedback;
|
|
||||||
} else {
|
|
||||||
for (j = length - k - 1; j > 0; j--)
|
|
||||||
bb[j] = bb[j - 1];
|
|
||||||
bb[0] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
decode_bch()
|
|
||||||
/*
|
|
||||||
* Simon Rockliff's implementation of Berlekamp's algorithm.
|
|
||||||
*
|
|
||||||
* Assume we have received bits in recd[i], i=0..(n-1).
|
|
||||||
*
|
|
||||||
* Compute the 2*t syndromes by substituting alpha^i into rec(X) and
|
|
||||||
* evaluating, storing the syndromes in s[i], i=1..2t (leave s[0] zero) .
|
|
||||||
* Then we use the Berlekamp algorithm to find the error location polynomial
|
|
||||||
* elp[i].
|
|
||||||
*
|
|
||||||
* If the degree of the elp is >t, then we cannot correct all the errors, and
|
|
||||||
* we have detected an uncorrectable error pattern. We output the information
|
|
||||||
* bits uncorrected.
|
|
||||||
*
|
|
||||||
* If the degree of elp is <=t, we substitute alpha^i , i=1..n into the elp
|
|
||||||
* to get the roots, hence the inverse roots, the error location numbers.
|
|
||||||
* This step is usually called "Chien's search".
|
|
||||||
*
|
|
||||||
* If the number of errors located is not equal the degree of the elp, then
|
|
||||||
* the decoder assumes that there are more than t errors and cannot correct
|
|
||||||
* them, only detect them. We output the information bits uncorrected.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
register int i, j, u, q, t2, count = 0, syn_error = 0;
|
|
||||||
int elp[1026][1024], d[1026], l[1026], u_lu[1026], s[1025];
|
|
||||||
int root[200], loc[200], err[1024], reg[201];
|
|
||||||
|
|
||||||
t2 = 2 * t;
|
|
||||||
|
|
||||||
/* first form the syndromes */
|
|
||||||
for (i = 1; i <= t2; i++) {
|
|
||||||
s[i] = 0;
|
|
||||||
for (j = 0; j < length; j++)
|
|
||||||
if (recd[j] != 0)
|
|
||||||
s[i] ^= alpha_to[(i * j) % n];
|
|
||||||
if (s[i] != 0)
|
|
||||||
syn_error = 1; /* set error flag if non-zero syndrome */
|
|
||||||
/*
|
|
||||||
* Note: If the code is used only for ERROR DETECTION, then
|
|
||||||
* exit program here indicating the presence of errors.
|
|
||||||
*/
|
|
||||||
/* convert syndrome from polynomial form to index form */
|
|
||||||
s[i] = index_of[s[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syn_error) { /* if there are errors, try to correct them */
|
|
||||||
/*
|
|
||||||
* Compute the error location polynomial via the Berlekamp
|
|
||||||
* iterative algorithm. Following the terminology of Lin and
|
|
||||||
* Costello's book : d[u] is the 'mu'th discrepancy, where
|
|
||||||
* u='mu'+1 and 'mu' (the Greek letter!) is the step number
|
|
||||||
* ranging from -1 to 2*t (see L&C), l[u] is the degree of
|
|
||||||
* the elp at that step, and u_l[u] is the difference between
|
|
||||||
* the step number and the degree of the elp.
|
|
||||||
*/
|
|
||||||
/* initialise table entries */
|
|
||||||
d[0] = 0; /* index form */
|
|
||||||
d[1] = s[1]; /* index form */
|
|
||||||
elp[0][0] = 0; /* index form */
|
|
||||||
elp[1][0] = 1; /* polynomial form */
|
|
||||||
for (i = 1; i < t2; i++) {
|
|
||||||
elp[0][i] = -1; /* index form */
|
|
||||||
elp[1][i] = 0; /* polynomial form */
|
|
||||||
}
|
|
||||||
l[0] = 0;
|
|
||||||
l[1] = 0;
|
|
||||||
u_lu[0] = -1;
|
|
||||||
u_lu[1] = 0;
|
|
||||||
u = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
u++;
|
|
||||||
if (d[u] == -1) {
|
|
||||||
l[u + 1] = l[u];
|
|
||||||
for (i = 0; i <= l[u]; i++) {
|
|
||||||
elp[u + 1][i] = elp[u][i];
|
|
||||||
elp[u][i] = index_of[elp[u][i]];
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
/*
|
|
||||||
* search for words with greatest u_lu[q] for
|
|
||||||
* which d[q]!=0
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
q = u - 1;
|
|
||||||
while ((d[q] == -1) && (q > 0))
|
|
||||||
q--;
|
|
||||||
/* have found first non-zero d[q] */
|
|
||||||
if (q > 0) {
|
|
||||||
j = q;
|
|
||||||
do {
|
|
||||||
j--;
|
|
||||||
if ((d[j] != -1) && (u_lu[q] < u_lu[j]))
|
|
||||||
q = j;
|
|
||||||
} while (j > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* have now found q such that d[u]!=0 and
|
|
||||||
* u_lu[q] is maximum
|
|
||||||
*/
|
|
||||||
/* store degree of new elp polynomial */
|
|
||||||
if (l[u] > l[q] + u - q)
|
|
||||||
l[u + 1] = l[u];
|
|
||||||
else
|
|
||||||
l[u + 1] = l[q] + u - q;
|
|
||||||
|
|
||||||
/* form new elp(x) */
|
|
||||||
for (i = 0; i < t2; i++)
|
|
||||||
elp[u + 1][i] = 0;
|
|
||||||
for (i = 0; i <= l[q]; i++)
|
|
||||||
if (elp[q][i] != -1)
|
|
||||||
elp[u + 1][i + u - q] =
|
|
||||||
alpha_to[(d[u] + n - d[q] + elp[q][i]) % n];
|
|
||||||
for (i = 0; i <= l[u]; i++) {
|
|
||||||
elp[u + 1][i] ^= elp[u][i];
|
|
||||||
elp[u][i] = index_of[elp[u][i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
u_lu[u + 1] = u - l[u + 1];
|
|
||||||
|
|
||||||
/* form (u+1)th discrepancy */
|
|
||||||
if (u < t2) {
|
|
||||||
/* no discrepancy computed on last iteration */
|
|
||||||
if (s[u + 1] != -1)
|
|
||||||
d[u + 1] = alpha_to[s[u + 1]];
|
|
||||||
else
|
|
||||||
d[u + 1] = 0;
|
|
||||||
for (i = 1; i <= l[u + 1]; i++)
|
|
||||||
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
|
|
||||||
d[u + 1] ^= alpha_to[(s[u + 1 - i]
|
|
||||||
+ index_of[elp[u + 1][i]]) % n];
|
|
||||||
/* put d[u+1] into index form */
|
|
||||||
d[u + 1] = index_of[d[u + 1]];
|
|
||||||
}
|
|
||||||
} while ((u < t2) && (l[u + 1] <= t));
|
|
||||||
|
|
||||||
u++;
|
|
||||||
if (l[u] <= t) {/* Can correct errors */
|
|
||||||
/* put elp into index form */
|
|
||||||
for (i = 0; i <= l[u]; i++)
|
|
||||||
elp[u][i] = index_of[elp[u][i]];
|
|
||||||
|
|
||||||
for (i = 0; i <= l[u]; i++)
|
|
||||||
|
|
||||||
/* Chien search: find roots of the error location polynomial */
|
|
||||||
for (i = 1; i <= l[u]; i++)
|
|
||||||
reg[i] = elp[u][i];
|
|
||||||
count = 0;
|
|
||||||
for (i = 1; i <= n; i++) {
|
|
||||||
q = 1;
|
|
||||||
for (j = 1; j <= l[u]; j++)
|
|
||||||
if (reg[j] != -1) {
|
|
||||||
reg[j] = (reg[j] + j) % n;
|
|
||||||
q ^= alpha_to[reg[j]];
|
|
||||||
}
|
|
||||||
if (!q) { /* store root and error
|
|
||||||
* location number indices */
|
|
||||||
root[count] = i;
|
|
||||||
loc[count] = n - i;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == l[u])
|
|
||||||
/* no. roots = degree of elp hence <= t errors */
|
|
||||||
for (i = 0; i < l[u]; i++)
|
|
||||||
recd[loc[i]] ^= 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long int messagepp = 0x0;
|
|
||||||
|
|
||||||
//very simplified version, just to encode, get frame and compare
|
|
||||||
unsigned long long int edacs_bch (unsigned long long int message)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
//not ideal to run these every frame, will want to eventually run once on start of DSD-FME, and store values
|
|
||||||
read_p(); /* Read m */
|
|
||||||
generate_gf(); /* Construct the Galois Field GF(2**m) */
|
|
||||||
gen_poly(); /* Compute the generator polynomial of BCH code */
|
|
||||||
//test shows this works, but output from program is that the poly is backwards, not sure if it corrects okay though
|
|
||||||
for (i = 0; i < k; i++){
|
|
||||||
ddata[i] = ( (message >> i) & 0x1 ); //loaded up backwards? or just outputs backwards?
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
encode_bch(); /* encode data */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* recd[] are the coefficients of c(x) = x**(length-k)*data(x) + b(x)
|
|
||||||
*/
|
|
||||||
for (i = 0; i < length - k; i++)
|
|
||||||
recd[i] = bb[i];
|
|
||||||
for (i = 0; i < k; i++)
|
|
||||||
recd[i + length - k] = ddata[i];
|
|
||||||
|
|
||||||
for (i = 0; i < length; i++) {
|
|
||||||
messagepp = (messagepp << 1) | recd[39-i]; //get it in the correct direction
|
|
||||||
}
|
|
||||||
|
|
||||||
return (messagepp);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
464
src/edacs-fme.c
464
src/edacs-fme.c
@@ -1,464 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* EDACS-FME
|
|
||||||
* A program for decoding EDACS (ported to DSD-FME)
|
|
||||||
* https://github.com/lwvmobile/edacs-fm
|
|
||||||
*
|
|
||||||
* Portions of this software originally from:
|
|
||||||
* https://github.com/sp5wwp/ledacs
|
|
||||||
* XTAL Labs
|
|
||||||
* 30 IV 2016
|
|
||||||
* Many thanks to SP5WWP for permission to use and modify this software
|
|
||||||
*
|
|
||||||
* Encoder/decoder for binary BCH codes in C (Version 3.1)
|
|
||||||
* Robert Morelos-Zaragoza
|
|
||||||
* 1994-7
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-10 Version EDACS-FM Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
char * getDateE(void) {
|
|
||||||
char datename[99]; //might honestly just need a much smaller size, and not a larger one
|
|
||||||
char * curr2;
|
|
||||||
struct tm * to;
|
|
||||||
time_t t;
|
|
||||||
t = time(NULL);
|
|
||||||
to = localtime( & t);
|
|
||||||
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
|
|
||||||
curr2 = strtok(datename, " ");
|
|
||||||
return curr2;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * getTimeE(void) //get pretty hh:mm:ss timestamp
|
|
||||||
{
|
|
||||||
time_t t = time(NULL);
|
|
||||||
|
|
||||||
char * curr;
|
|
||||||
char * stamp = asctime(localtime( & t));
|
|
||||||
|
|
||||||
curr = strtok(stamp, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
|
|
||||||
return curr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void edacs(dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
unsigned long long fr_1 = 0xFFFFFFFFFF; //40-bit frames
|
|
||||||
unsigned long long fr_2 = 0; //each is a 40 bit frame that repeats 3 times
|
|
||||||
unsigned long long fr_3 = 0; //two messages per frame
|
|
||||||
unsigned long long fr_4 = 0xFFFFFFFFFF;
|
|
||||||
unsigned long long fr_5 = 0;
|
|
||||||
unsigned long long fr_6 = 0;
|
|
||||||
|
|
||||||
//BCH stuff
|
|
||||||
unsigned long long int fr_1m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler
|
|
||||||
unsigned long long int fr_1t = 0xFFFFFFFFFF; //40 bit return from BCH with poly attached
|
|
||||||
unsigned long long int fr_4m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler
|
|
||||||
unsigned long long int fr_4t = 0xFFFFFFFFFF; //40 bit return from BCH with poly attached
|
|
||||||
|
|
||||||
unsigned char command = 0xFF;
|
|
||||||
unsigned char mt1 = 0x1F;
|
|
||||||
unsigned char mt2 = 0xF;
|
|
||||||
unsigned char mta = 0;
|
|
||||||
unsigned char lcn = 0;
|
|
||||||
|
|
||||||
//commands; may not use these anymore
|
|
||||||
unsigned int vcmd = 0xEE; //voice command variable
|
|
||||||
unsigned int idcmd = 0xFD;
|
|
||||||
unsigned int peercmd = 0xF88; //using for EA detection test
|
|
||||||
unsigned int netcmd = 0xF3; //using for Networked Test
|
|
||||||
|
|
||||||
state->edacs_vc_lcn = -1; //init on negative for ncurses and tuning
|
|
||||||
|
|
||||||
int i, j;
|
|
||||||
int edacs_bit[241] = {0}; //zero out bit array and collect bits into it.
|
|
||||||
|
|
||||||
for (i = 0; i < 240; i++) //288 bits every transmission minus 48 bit (24 dibit) sync pattern
|
|
||||||
{
|
|
||||||
edacs_bit[i] = getDibit (opts, state); //getDibit returns binary 0 or 1 on GFSK signal (Edacs and PV)
|
|
||||||
}
|
|
||||||
|
|
||||||
//push the edacs_bit array into fr format from EDACS-FM
|
|
||||||
for (i = 0; i < 40; i++)
|
|
||||||
{
|
|
||||||
//only fr_1 and fr4 are going to matter
|
|
||||||
fr_1 = fr_1 << 1;
|
|
||||||
fr_1 = fr_1 | edacs_bit[i];
|
|
||||||
fr_2 = fr_2 << 1;
|
|
||||||
fr_2 = fr_2 | edacs_bit[i+40];
|
|
||||||
fr_3 = fr_3 << 1;
|
|
||||||
fr_3 = fr_3 | edacs_bit[i+80];
|
|
||||||
|
|
||||||
fr_4 = fr_4 << 1;
|
|
||||||
fr_4 = fr_4 | edacs_bit[i+120];
|
|
||||||
fr_5 = fr_5 << 1;
|
|
||||||
fr_5 = fr_5 | edacs_bit[i+160];
|
|
||||||
fr_6 = fr_6 << 1;
|
|
||||||
fr_6 = fr_6 | edacs_bit[i+200];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fr_1 = fr_1 & 0xFFFFFFFFFF;
|
|
||||||
fr_4 = fr_4 & 0xFFFFFFFFFF;
|
|
||||||
|
|
||||||
fr_1m = (fr_1 & 0xFFFFFFF000) >> 12;
|
|
||||||
fr_4m = (fr_4 & 0xFFFFFFF000) >> 12;
|
|
||||||
|
|
||||||
//take our message and create a new crc for it, if it matches the old crc, then we have a good frame
|
|
||||||
fr_1t = edacs_bch (fr_1m);
|
|
||||||
fr_4t = edacs_bch (fr_4m);
|
|
||||||
|
|
||||||
fr_1t = fr_1t & 0xFFFFFFFFFF;
|
|
||||||
fr_4t = fr_4t & 0xFFFFFFFFFF;
|
|
||||||
|
|
||||||
if (fr_1 != fr_1t || fr_4 != fr_4t)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " BCH FAIL ");
|
|
||||||
}
|
|
||||||
else //BCH Pass, continue from here.
|
|
||||||
{
|
|
||||||
|
|
||||||
//ESK on/off detection, I honestly don't remember the logic for this anymore, but it works fine
|
|
||||||
if ( (((fr_1t & 0xF000000000) >> 36) != 0xB) && (((fr_1t & 0xF000000000) >> 36) != 0x1) && (((fr_1t & 0xFF00000000) >> 32) != 0xF3) )
|
|
||||||
{
|
|
||||||
//experimenting with values here, not too high, and not too low
|
|
||||||
if ( (((fr_1t & 0xF000000000) >> 36) <= 0x8 ))
|
|
||||||
{
|
|
||||||
state->esk_mask = 0xA0;
|
|
||||||
}
|
|
||||||
//ideal value would be 5, but some other values exist that don't allow it
|
|
||||||
if ( (((fr_1t & 0xF000000000) >> 36) > 0x8 ) )
|
|
||||||
{
|
|
||||||
state->esk_mask = 0x0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Standard/Networked Auto Detection
|
|
||||||
//if (command == netcmd) //netcmd is F3 Standard/Networked (I think)
|
|
||||||
if ( (fr_1t >> 32 == netcmd) || (fr_1t >> 32 == (netcmd ^ 0xA0)) )
|
|
||||||
{
|
|
||||||
state->ea_mode = 0; //disable extended addressing mode
|
|
||||||
}
|
|
||||||
|
|
||||||
//EA Auto detection //peercmd is 0xF88 peer site relay 0xFF80000000 >> 28
|
|
||||||
if (fr_1t >> 28 == peercmd || fr_1t >> 28 == (peercmd ^ 0xA00) )
|
|
||||||
{
|
|
||||||
state->ea_mode = 1; //enable extended addressing mode
|
|
||||||
}
|
|
||||||
|
|
||||||
//Start Extended Addressing Mode
|
|
||||||
if (state->ea_mode == 1)
|
|
||||||
{
|
|
||||||
command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask;
|
|
||||||
mt1 = (command & 0xF8) >> 3;
|
|
||||||
mt2 = (fr_1t & 0x780000000) >> 31;
|
|
||||||
lcn = (fr_1t & 0x3E0000000) >> 29; //only valid during calls, status
|
|
||||||
|
|
||||||
//Site ID
|
|
||||||
unsigned long long int site_id = 0; //we probably could just make this an int as well as the state variables
|
|
||||||
if (mt1 == 0x1F && mt2 == 0xA)
|
|
||||||
{
|
|
||||||
site_id = ((fr_1 & 0x1F000) >> 12) | ((fr_1 & 0x1F000000) >> 19);
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Site ID [%02llX][%03lld] Extended Addressing", site_id, site_id);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
state->edacs_site_id = site_id;
|
|
||||||
}
|
|
||||||
//Patch Groups
|
|
||||||
else if (mt1 == 0x1F && mt2 == 0xC)
|
|
||||||
{
|
|
||||||
int patch_site = ((fr_4t & 0xFF00000000) >> 32); //is site info valid, 0 for all sites? else patch only good on site listed?
|
|
||||||
int sourcep = ((fr_1t & 0xFFFF000) >> 12);
|
|
||||||
int targetp = ((fr_4t & 0xFFFF000) >> 12);
|
|
||||||
fprintf (stderr, " Patch -- Site [%d] Source [%d] Target [%d] ", patch_site, sourcep, targetp);
|
|
||||||
}
|
|
||||||
//Adjacent Sites
|
|
||||||
else if (mt1 == 0x1F && mt2 == 0x1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " Adjacent Site");
|
|
||||||
if ( ((fr_1t & 0xFF000) >> 12) > 0 )
|
|
||||||
{
|
|
||||||
int adj = (fr_1t & 0xFF000) >> 12;
|
|
||||||
int adj_l = (fr_1t & 0x1F000000) >> 24;
|
|
||||||
fprintf (stderr, " [%02X][%03d] on CC LCN [%02d]", adj, adj, adj_l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Control Channel LCN
|
|
||||||
else if (mt1 == 0x1F && mt2 == 0x8)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " Control Channel LCN");
|
|
||||||
if (((fr_4t >> 12) & 0x1F) != 0)
|
|
||||||
{
|
|
||||||
state->edacs_cc_lcn = ((fr_4t >> 12) & 0x1F);
|
|
||||||
if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26) //26, or 27. shouldn't matter don't think cc lcn will give a status lcn val
|
|
||||||
{
|
|
||||||
state->edacs_lcn_count = state->edacs_cc_lcn;
|
|
||||||
}
|
|
||||||
fprintf (stderr, " [%d]", state->edacs_cc_lcn);
|
|
||||||
|
|
||||||
//check for control channel lcn frequency if not provided in channel map or in the lcn list
|
|
||||||
if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0)
|
|
||||||
{
|
|
||||||
long int lcnfreq = 0;
|
|
||||||
//if using rigctl, we can ask for the currrent frequency
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
lcnfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
|
||||||
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
|
||||||
}
|
|
||||||
//if using rtl input, we can ask for the current frequency tuned
|
|
||||||
if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
lcnfreq = (long int)opts->rtlsdr_center_freq;
|
|
||||||
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//set trunking cc here so we know where to come back to
|
|
||||||
if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0)
|
|
||||||
{
|
|
||||||
state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//disabling kick command, data looks like its just FFFF, no actual values, can't verify accuracy
|
|
||||||
// else if (mt1 == 0x1F && mt2 == 0xB) //KICK LISTING for EA?? Unverified, but probably observed in Unitrunker way back when.
|
|
||||||
// {
|
|
||||||
// int kicked = (fr_4t & 0xFFFFF000) >> 12;
|
|
||||||
// fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
|
||||||
// fprintf (stderr, " FR_3 [%010llX]", fr_3);
|
|
||||||
// fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
|
||||||
// fprintf (stderr, " FR_6 [%010llX]", fr_6);
|
|
||||||
// fprintf (stderr, " Kick Command?");
|
|
||||||
// }
|
|
||||||
//Voice Call Grant Update
|
|
||||||
// mt1 0x3 is Digital group voice call, 0x2 Group Data Channel, 0x1 TDMA call
|
|
||||||
else if (mt1 >= 0x1 && mt1 <= 0x3)
|
|
||||||
{
|
|
||||||
//LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc"
|
|
||||||
if (lcn > state->edacs_lcn_count && lcn < 26)
|
|
||||||
{
|
|
||||||
state->edacs_lcn_count = lcn;
|
|
||||||
}
|
|
||||||
|
|
||||||
int group = (fr_1t & 0xFFFF000) >> 12;
|
|
||||||
int source = (fr_4t & 0xFFFFF000) >> 12;
|
|
||||||
if (group != 0) state->lasttg = group;
|
|
||||||
if (source != 0) state->lastsrc = source;
|
|
||||||
if (lcn != 0) state->edacs_vc_lcn = lcn;
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf (stderr, " Group [%05d] Source [%08d] LCN[%02d]", group, source, lcn);
|
|
||||||
|
|
||||||
char mode[8]; //allow, block, digital enc
|
|
||||||
|
|
||||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
|
||||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
|
||||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == group)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mt1 == 0x1) fprintf (stderr, " TDMA Call"); //never observed, wonder if any EDACS systems ever carried a TDMA signal (X2-TDMA?)
|
|
||||||
if (mt1 == 0x2) fprintf (stderr, " Group Data Call"); //Never Seen this one before
|
|
||||||
if (mt1 == 0x3) fprintf (stderr, " Digital Call"); //ProVoice, this is what we always get on SLERS EA
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
//this is working now with the new import setup
|
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
|
||||||
{
|
|
||||||
if (lcn < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise)
|
|
||||||
{
|
|
||||||
//openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl)
|
|
||||||
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
|
|
||||||
{
|
|
||||||
sprintf (opts->wav_out_file, "./WAV/%s %s pV Site %lld TG %d SRC %d.wav", getDateE(), getTimeE(), state->edacs_site_id, group, source);
|
|
||||||
openWavOutFile (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because the lcn index starts at zero
|
|
||||||
state->edacs_tuned_lcn = lcn;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->audio_in_type == 3) //rtl dongle
|
|
||||||
{
|
|
||||||
rtl_udp_tune(opts, state, state->trunk_lcn_freq[lcn-1]);
|
|
||||||
state->edacs_tuned_lcn = lcn;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else //print frames for debug/analysis
|
|
||||||
{
|
|
||||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
|
||||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
|
||||||
fprintf (stderr, " Unknown Command");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
//Start Standard or Networked Mode
|
|
||||||
else if (state->ea_mode == 0)
|
|
||||||
{
|
|
||||||
//standard or networked
|
|
||||||
command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask;
|
|
||||||
lcn = (fr_1t & 0xF8000000) >> 27;
|
|
||||||
|
|
||||||
//site ID and CC LCN
|
|
||||||
if (command == 0xFD)
|
|
||||||
{
|
|
||||||
int site_id = (fr_1t & 0xFF000) >> 12;
|
|
||||||
int cc_lcn = (fr_1t & 0x1F000000) >> 24;
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, " Site ID [%02X][%03d] on CC LCN [%02d] Standard/Networked", site_id, site_id, cc_lcn);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
state->edacs_site_id = site_id;
|
|
||||||
state->edacs_cc_lcn = cc_lcn;
|
|
||||||
|
|
||||||
if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26)
|
|
||||||
{
|
|
||||||
state->edacs_lcn_count = state->edacs_cc_lcn;
|
|
||||||
}
|
|
||||||
|
|
||||||
//check for control channel lcn frequency if not provided in channel map or in the lcn list
|
|
||||||
if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0)
|
|
||||||
{
|
|
||||||
long int lcnfreq = 0;
|
|
||||||
//if using rigctl, we can ask for the currrent frequency
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
lcnfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
|
||||||
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
|
||||||
}
|
|
||||||
//if using rtl input, we can ask for the current frequency tuned
|
|
||||||
if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
lcnfreq = (long int)opts->rtlsdr_center_freq;
|
|
||||||
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//set trunking cc here so we know where to come back to
|
|
||||||
if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0)
|
|
||||||
{
|
|
||||||
state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//voice call assignment
|
|
||||||
else if (command == 0xEE || command == 0xEF)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (lcn > state->edacs_lcn_count && lcn < 26)
|
|
||||||
{
|
|
||||||
state->edacs_lcn_count = lcn;
|
|
||||||
}
|
|
||||||
state->edacs_vc_lcn = lcn;
|
|
||||||
//just going to use the default 4-4-3 A-FS scheme, making it user configurable would be a pain
|
|
||||||
int afs = (fr_1t & 0x7FF000) >> 12;
|
|
||||||
int a = afs >> 7;
|
|
||||||
int fs = afs & 0x7F;
|
|
||||||
int status = (fr_1t & 0xF00000000) >> 32;
|
|
||||||
if (afs > 0) state->lastsrc = afs;
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf (stderr, " AFS [0x%03X] [%02d-%03d] LCN [%02d]", afs, a, fs, lcn);
|
|
||||||
|
|
||||||
char mode[8]; //allow, block, digital enc
|
|
||||||
|
|
||||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
|
||||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
|
||||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == afs)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command == 0xEE)
|
|
||||||
{
|
|
||||||
//no handling for raw audio yet...that works properly atleast
|
|
||||||
fprintf (stderr, " Analog");
|
|
||||||
}
|
|
||||||
else //Digital Call (ProVoice, hopefully not Aegis in 2022)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " Digital");
|
|
||||||
//this is working now with the new import setup
|
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
|
||||||
{
|
|
||||||
if (lcn < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise)
|
|
||||||
{
|
|
||||||
//openwav file and do per call right here
|
|
||||||
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
|
|
||||||
{
|
|
||||||
sprintf (opts->wav_out_file, "./WAV/%s %s pV Site %lld AFS %X.wav", getDateE(), getTimeE(), state->edacs_site_id, afs);
|
|
||||||
openWavOutFile (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero
|
|
||||||
state->edacs_tuned_lcn = lcn;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->audio_in_type == 3) //rtl dongle
|
|
||||||
{
|
|
||||||
rtl_udp_tune(opts, state, state->trunk_lcn_freq[lcn-1]);
|
|
||||||
state->edacs_tuned_lcn = lcn;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
//Network Command, Not sure of how to handle, but just show frames
|
|
||||||
else if (command == 0xF3)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
|
||||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
|
||||||
fprintf (stderr, " Network Command");
|
|
||||||
}
|
|
||||||
|
|
||||||
else //print frames for debug/analysis
|
|
||||||
{
|
|
||||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
|
||||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
|
||||||
fprintf (stderr, " Unknown Command");
|
|
||||||
}
|
|
||||||
|
|
||||||
} //end Standard or Networked
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
124
src/nxdn96.c
Normal file
124
src/nxdn96.c
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#include "dsd.h"
|
||||||
|
#include "nxdn96_const.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
processNXDN96 (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
int i, j, k, dibit;
|
||||||
|
|
||||||
|
char ambe_fr[4][24];
|
||||||
|
const int *w, *x, *y, *z;
|
||||||
|
|
||||||
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "VOICE e:");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (k = 0; k < 4; k++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 222; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, " ");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (k < 3)
|
||||||
|
{
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
w = nW;
|
||||||
|
x = nX;
|
||||||
|
y = nY;
|
||||||
|
z = nZ;
|
||||||
|
for (i = 0; i < 36; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
z++;
|
||||||
|
}
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, " ");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (j = 0; j < 3; j++) // we skip the last voice frame until frame sync can work with < 24 symbols
|
||||||
|
{
|
||||||
|
w = nW;
|
||||||
|
x = nX;
|
||||||
|
y = nY;
|
||||||
|
z = nZ;
|
||||||
|
for (i = 0; i < 36; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
z++;
|
||||||
|
}
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, " ");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k < 3)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 18; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, " ");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < 30; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
printf ("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
127
src/nxdn_data.c
Normal file
127
src/nxdn_data.c
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#include "dsd.h"
|
||||||
|
#include "nxdn_const.h"
|
||||||
|
|
||||||
|
void processNXDNData (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
int i, dibit = 0;
|
||||||
|
unsigned char sacch_raw[72] = {0};
|
||||||
|
unsigned char sacch_decoded[32] = {0}; /* 26 bit + 6 bit CRC */
|
||||||
|
unsigned char *pr;
|
||||||
|
uint8_t CrcIsGood = 0;
|
||||||
|
uint8_t StructureField = 0;
|
||||||
|
uint8_t RAN = 0;
|
||||||
|
uint8_t PartOfFrame = 0;
|
||||||
|
|
||||||
|
/* Remove compiler warning */
|
||||||
|
//UNUSED_VARIABLE(dibit);
|
||||||
|
|
||||||
|
//if using NXDN Scrambling, run LFSRN (dummy ambe values) to continue seed
|
||||||
|
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||||
|
{
|
||||||
|
char ambe_temp[49] = {0};
|
||||||
|
char ambe_d[49] = {0};
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
LFSRN(ambe_temp, ambe_d, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "DATA - ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start pseudo-random NXDN sequence after
|
||||||
|
* LICH = 16 bit = 8 dibit
|
||||||
|
* ==> Index 8 */
|
||||||
|
pr = (unsigned char *)(&nxdnpr2[8]);
|
||||||
|
for (i = 0; i < 30; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
|
||||||
|
/* Store and de-interleave directly SACCH bits */
|
||||||
|
sacch_raw[(2*i)] = ((*pr) & 1) ^ (1 & (dibit >> 1)); // bit 1
|
||||||
|
sacch_raw[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
pr++;
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf(stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf(stderr, " ");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Decode the SACCH fields */
|
||||||
|
CrcIsGood = NXDN_SACCH_raw_part_decode(sacch_raw, sacch_decoded);
|
||||||
|
StructureField = (sacch_decoded[0]<<1 | sacch_decoded[1]<<0) & 0x03; /* Compute the Structure Field (remove 2 first bits of the SR Information in the SACCH) */
|
||||||
|
RAN = (sacch_decoded[2]<<5 | sacch_decoded[3]<<4 | sacch_decoded[4]<<3 | sacch_decoded[5]<<2 | sacch_decoded[6]<<1 | sacch_decoded[7]<<0) & 0x3F; /* Compute the RAN (6 last bits of the SR Information in the SACCH) */
|
||||||
|
|
||||||
|
/* Compute the Part of Frame according to the structure field */
|
||||||
|
if (StructureField == 3) PartOfFrame = 0;
|
||||||
|
else if(StructureField == 2) PartOfFrame = 1;
|
||||||
|
else if(StructureField == 1) PartOfFrame = 2;
|
||||||
|
else if(StructureField == 0) PartOfFrame = 3;
|
||||||
|
else PartOfFrame = 0; /* Normally we should never go here */
|
||||||
|
|
||||||
|
/* Fill the SACCH structure */
|
||||||
|
state->NxdnSacchRawPart[PartOfFrame].CrcIsGood = CrcIsGood;
|
||||||
|
state->NxdnSacchRawPart[PartOfFrame].StructureField = StructureField;
|
||||||
|
state->NxdnSacchRawPart[PartOfFrame].RAN = RAN;
|
||||||
|
|
||||||
|
memcpy(state->NxdnSacchRawPart[PartOfFrame].Data, &sacch_decoded[8], 18); /* Copy the 18 bits of SACCH content */
|
||||||
|
|
||||||
|
//fprintf(stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
|
||||||
|
if(CrcIsGood)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
//fprintf (stderr, "(CRC OK) ");
|
||||||
|
state->nxdn_last_ran = RAN; //disable, try to grab this in voice instead
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "(CRC ERR) ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Decode the SACCH only when all 4 voice frame
|
||||||
|
* SACCH spare parts have been received */
|
||||||
|
if(PartOfFrame == 3)
|
||||||
|
{
|
||||||
|
/* Decode the entire SACCH content */
|
||||||
|
NXDN_SACCH_Full_decode(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 144; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf(stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset the SACCH CRCs when all 4 voice frame
|
||||||
|
* SACCH spare parts have been received */
|
||||||
|
if(PartOfFrame == 3)
|
||||||
|
{
|
||||||
|
/* Reset all CRCs of the SACCH */
|
||||||
|
for(i = 0; i < 4; i++) state->NxdnSacchRawPart[i].CrcIsGood = 0;
|
||||||
|
|
||||||
|
//reset Scrambler seed
|
||||||
|
if (state->nxdn_cipher_type == 0x1)
|
||||||
|
{
|
||||||
|
state->payload_miN = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} /* End processNXDNData() */
|
||||||
@@ -1,681 +0,0 @@
|
|||||||
//NXDN descramble/deperm/depuncture and crc/utility functions
|
|
||||||
//Reworked portions from Osmocom OP25
|
|
||||||
|
|
||||||
/* -*- c++ -*- */
|
|
||||||
/*
|
|
||||||
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* This is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this software; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include "nxdn_const.h"
|
|
||||||
|
|
||||||
static const uint8_t scramble_t[] = { //values are the position values we need to invert in the descramble
|
|
||||||
2, 5, 6, 7, 10, 12, 14, 16, 17, 22, 23, 25, 26, 27, 28, 30, 33, 34, 36, 37, 38, 41, 45, 47,
|
|
||||||
52, 54, 56, 57, 59, 62, 63, 64, 65, 66, 67, 69, 70, 73, 76, 79, 81, 82, 84, 85, 86, 87, 88,
|
|
||||||
89, 92, 95, 96, 98, 100, 103, 104, 107, 108, 116, 117, 121, 122, 125, 127, 131, 132, 134,
|
|
||||||
137, 139, 140, 141, 142, 143, 144, 145, 147, 151, 153, 154, 158, 159, 160, 162, 164, 165,
|
|
||||||
168, 170, 171, 174, 175, 176, 177, 181
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int PARITY[] = {
|
|
||||||
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0,
|
|
||||||
1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1,
|
|
||||||
1, 0, 0, 1, 1, 0, 1, 0, 0, 1
|
|
||||||
};
|
|
||||||
|
|
||||||
//decoding functions here
|
|
||||||
void nxdn_descramble(uint8_t dibits[], int len)
|
|
||||||
{
|
|
||||||
for (int i=0; i<sizeof(scramble_t); i++) {
|
|
||||||
if (scramble_t[i] >= len)
|
|
||||||
break;
|
|
||||||
dibits[scramble_t[i]] ^= 0x2; // invert sign of scrambled dibits
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
uint16_t crc = 0; //crc calculated by function
|
|
||||||
uint16_t check = 0; //crc from payload for comparison
|
|
||||||
int out;
|
|
||||||
char buf[128];
|
|
||||||
|
|
||||||
for (int i=0; i<144; i++)
|
|
||||||
deperm[PERM_16_9[i]] = bits[i];
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
//switch to the convolutional decoder
|
|
||||||
uint8_t temp[210];
|
|
||||||
uint8_t s0;
|
|
||||||
uint8_t s1;
|
|
||||||
uint8_t m_data[13];
|
|
||||||
memset (temp, 0, sizeof(temp));
|
|
||||||
memset (m_data, 0, sizeof(m_data));
|
|
||||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
//load tail 16 bits into check variable to compare vs computed crc value
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
check = check << 1;
|
|
||||||
check = check + trellis_buf[80+i];
|
|
||||||
}
|
|
||||||
|
|
||||||
crc = crc12f (trellis_buf, 80);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " FACCH Payload ");
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
|
||||||
}
|
|
||||||
fprintf (stderr, " - %03X %03X", crc, check);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//new 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];
|
|
||||||
int o = 0;
|
|
||||||
uint8_t crc = 0; //value computed by crc6 on payload
|
|
||||||
uint8_t check = 0; //value pulled from last 6 bits
|
|
||||||
int sf = 0;
|
|
||||||
int ran = 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[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 < 40U; i++)
|
|
||||||
{
|
|
||||||
s0 = temp[(2*i)];
|
|
||||||
s1 = temp[(2*i)+1];
|
|
||||||
|
|
||||||
CNXDNConvolution_decode(s0, s1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//stored as 5 bytes, will need to convert to trellis_buf after running
|
|
||||||
CNXDNConvolution_chainback(m_data, 36U);
|
|
||||||
|
|
||||||
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 = crc6(trellis_buf, 32);
|
|
||||||
for (int i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
check = check << 1;
|
|
||||||
check = check + trellis_buf[i+26];
|
|
||||||
}
|
|
||||||
|
|
||||||
//FIRST! If part of a non_superframe, and CRC is good, send directly to NXDN_Elements_Content_decode
|
|
||||||
if (state->nxdn_sacch_non_superframe == TRUE)
|
|
||||||
{
|
|
||||||
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
|
||||||
else fprintf (stderr, " ");
|
|
||||||
|
|
||||||
uint8_t nsf_sacch[26];
|
|
||||||
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 (opts->payload == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n SACCH NSF ");
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
|
||||||
}
|
|
||||||
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//If part of superframe, collect the fragments and send to NXDN_SACCH_Full_decode instead
|
|
||||||
else if (state->nxdn_sacch_non_superframe == FALSE)
|
|
||||||
{
|
|
||||||
//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;
|
|
||||||
else if (sf == 0) part_of_frame = 3;
|
|
||||||
else part_of_frame = 0;
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KCYN);
|
|
||||||
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
|
||||||
else fprintf (stderr, " ");
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
state->nxdn_ran = state->nxdn_last_ran = ran;
|
|
||||||
state->nxdn_sf = sf;
|
|
||||||
state->nxdn_part_of_frame = part_of_frame;
|
|
||||||
state->nxdn_sacch_frame_segcrc[part_of_frame] = 0; //zero indicates good check
|
|
||||||
}
|
|
||||||
else state->nxdn_sacch_frame_segcrc[part_of_frame] = 1; //1 indicates bad check
|
|
||||||
|
|
||||||
int sacch_segment = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
sacch_segment = sacch_segment << 1;
|
|
||||||
sacch_segment = sacch_segment + trellis_buf[i+8];
|
|
||||||
state->nxdn_sacch_frame_segment[part_of_frame][i] = trellis_buf[i+8];
|
|
||||||
}
|
|
||||||
|
|
||||||
//Hand off to LEH NXDN_SACCH_Full_decode
|
|
||||||
if (part_of_frame == 3)
|
|
||||||
{
|
|
||||||
NXDN_SACCH_Full_decode (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " SACCH SF Segment #%d ", part_of_frame+1);
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
|
||||||
}
|
|
||||||
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348])
|
|
||||||
{
|
|
||||||
|
|
||||||
uint8_t deperm[348];
|
|
||||||
uint8_t depunc[406];
|
|
||||||
uint8_t trellis_buf[199];
|
|
||||||
int id = 0;
|
|
||||||
uint16_t crc = 0;
|
|
||||||
|
|
||||||
for (int i=0; i<348; i++) {
|
|
||||||
deperm[PERM_12_29[i]] = bits[i];
|
|
||||||
}
|
|
||||||
for (int i=0; i<29; i++) {
|
|
||||||
depunc[id++] = deperm[i*12];
|
|
||||||
depunc[id++] = deperm[i*12+1];
|
|
||||||
depunc[id++] = deperm[i*12+2];
|
|
||||||
depunc[id++] = 0;
|
|
||||||
depunc[id++] = deperm[i*12+3];
|
|
||||||
depunc[id++] = deperm[i*12+4];
|
|
||||||
depunc[id++] = deperm[i*12+5];
|
|
||||||
depunc[id++] = deperm[i*12+6];
|
|
||||||
depunc[id++] = deperm[i*12+7];
|
|
||||||
depunc[id++] = deperm[i*12+8];
|
|
||||||
depunc[id++] = deperm[i*12+9];
|
|
||||||
depunc[id++] = 0;
|
|
||||||
depunc[id++] = deperm[i*12+10];
|
|
||||||
depunc[id++] = deperm[i*12+11];
|
|
||||||
}
|
|
||||||
|
|
||||||
//switch to the convolutional decoder
|
|
||||||
uint8_t temp[220];
|
|
||||||
uint8_t s0;
|
|
||||||
uint8_t s1;
|
|
||||||
uint8_t m_data[26];
|
|
||||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
|
||||||
memset (temp, 0, sizeof (temp));
|
|
||||||
memset (m_data, 0, sizeof (m_data));
|
|
||||||
|
|
||||||
for (int i = 0; i < 406; i++)
|
|
||||||
{
|
|
||||||
temp[i] = depunc[i] << 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
temp[i+203] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNConvolution_start();
|
|
||||||
for (int i = 0U; i < 207U; i++)
|
|
||||||
{
|
|
||||||
s0 = temp[(2*i)];
|
|
||||||
s1 = temp[(2*i)+1];
|
|
||||||
|
|
||||||
CNXDNConvolution_decode(s0, s1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//numerals seem okay now
|
|
||||||
CNXDNConvolution_chainback(m_data, 203U);
|
|
||||||
|
|
||||||
for(int i = 0; i < 26; 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 = crc15(trellis_buf, 199);
|
|
||||||
|
|
||||||
uint8_t f2u_message_buffer[199];
|
|
||||||
memset (f2u_message_buffer, 0, sizeof(f2u_message_buffer));
|
|
||||||
|
|
||||||
//just going to leave this all here in case its needed, like in cac,
|
|
||||||
//don't have any samples with f2 or udch data in it
|
|
||||||
for (int i = 0; i < 199-8; i++)
|
|
||||||
{
|
|
||||||
f2u_message_buffer[i] = trellis_buf[i+8];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crc == 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 (opts->payload == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " F2/U Payload\n ");
|
|
||||||
for (int i = 0; i < 26; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
|
||||||
if (i == 12) fprintf (stderr, "\n ");
|
|
||||||
}
|
|
||||||
if (crc != 0) fprintf (stderr, " CRC ERR ");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
int id = 0;
|
|
||||||
uint16_t crc = 0;
|
|
||||||
|
|
||||||
for (int i=0; i<300; i++) {
|
|
||||||
deperm[PERM_12_25[i]] = bits[i];
|
|
||||||
}
|
|
||||||
for (int i=0; i<25; i++) {
|
|
||||||
depunc[id++] = deperm[i*12];
|
|
||||||
depunc[id++] = deperm[i*12+1];
|
|
||||||
depunc[id++] = deperm[i*12+2];
|
|
||||||
depunc[id++] = 0;
|
|
||||||
depunc[id++] = deperm[i*12+3];
|
|
||||||
depunc[id++] = deperm[i*12+4];
|
|
||||||
depunc[id++] = deperm[i*12+5];
|
|
||||||
depunc[id++] = deperm[i*12+6];
|
|
||||||
depunc[id++] = deperm[i*12+7];
|
|
||||||
depunc[id++] = deperm[i*12+8];
|
|
||||||
depunc[id++] = deperm[i*12+9];
|
|
||||||
depunc[id++] = 0;
|
|
||||||
depunc[id++] = deperm[i*12+10];
|
|
||||||
depunc[id++] = deperm[i*12+11];
|
|
||||||
}
|
|
||||||
|
|
||||||
//switch to the convolutional decoder
|
|
||||||
uint8_t temp[360];
|
|
||||||
uint8_t s0;
|
|
||||||
uint8_t s1;
|
|
||||||
uint8_t m_data[26];
|
|
||||||
memset (trellis_buf, 0, sizeof(trellis_buf));
|
|
||||||
memset (temp, 0, sizeof (temp));
|
|
||||||
memset (m_data, 0, sizeof (m_data));
|
|
||||||
|
|
||||||
for (int i = 0; i < 350; i++)
|
|
||||||
{
|
|
||||||
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++)
|
|
||||||
{
|
|
||||||
s0 = temp[(2*i)];
|
|
||||||
s1 = temp[(2*i)+1];
|
|
||||||
|
|
||||||
CNXDNConvolution_decode(s0, s1);
|
|
||||||
}
|
|
||||||
|
|
||||||
CNXDNConvolution_chainback(m_data, 175U);
|
|
||||||
|
|
||||||
for(int i = 0; i < 22; i++)
|
|
||||||
{
|
|
||||||
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
|
||||||
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
|
||||||
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
|
||||||
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
|
||||||
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
|
||||||
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
|
||||||
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
|
||||||
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
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
|
|
||||||
{
|
|
||||||
cac_message_buffer[i] = trellis_buf[i+8];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crc == 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " CAC ");
|
|
||||||
NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
fprintf (stderr, " CAC Payload\n ");
|
|
||||||
for (int i = 0; i < 22; i++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
|
||||||
if (i == 10) fprintf (stderr, "\n ");
|
|
||||||
}
|
|
||||||
if (crc != 0) fprintf (stderr, " CRC ERR ");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
|
||||||
{
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
if (MessageType == 0x10) fprintf(stderr, " IDLE");
|
|
||||||
else if (MessageType == 0x11) fprintf(stderr, " DISC"); //disconnect
|
|
||||||
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 == 0x04) fprintf(stderr, " VCALL_ASSGN");
|
|
||||||
else if (MessageType == 0x05) fprintf(stderr, " VCALL_ASSGN_DUP");
|
|
||||||
else if (MessageType == 0x0E) fprintf(stderr, " DCALL_ASSGN");
|
|
||||||
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 == 0x1A) fprintf(stderr, " CCH_INFO");
|
|
||||||
else if (MessageType == 0x1B) fprintf(stderr, " ADJ_SITE_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");
|
|
||||||
else if (MessageType == 0x32) fprintf(stderr, " STAT_REQ");
|
|
||||||
else if (MessageType == 0x33) fprintf(stderr, " STAT_RESP");
|
|
||||||
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
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
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));
|
|
||||||
sprintf (state->nxdn_call_type, "%s", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//voice descrambler
|
|
||||||
void LFSRN(char * BufferIn, char * BufferOut, dsd_state * state)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int lfsr;
|
|
||||||
int pN[49] = {0};
|
|
||||||
int bit = 0;
|
|
||||||
|
|
||||||
lfsr = state->payload_miN & 0x7FFF;
|
|
||||||
|
|
||||||
for (i = 0; i < 49; i++)
|
|
||||||
{
|
|
||||||
pN[i] = lfsr & 0x1;
|
|
||||||
bit = ( (lfsr >> 1) ^ (lfsr >> 0) ) & 1;
|
|
||||||
lfsr = ( (lfsr >> 1 ) | (bit << 14) );
|
|
||||||
BufferOut[i] = BufferIn[i] ^ pN[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
state->payload_miN = lfsr & 0x7FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int load_i(const uint8_t val[], int len) {
|
|
||||||
int acc = 0;
|
|
||||||
for (int i=0; i<len; i++){
|
|
||||||
acc = (acc << 1) + (val[i] & 1);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t crc6(const uint8_t buf[], int len)
|
|
||||||
{
|
|
||||||
uint8_t s[6];
|
|
||||||
uint8_t a;
|
|
||||||
for (int i=0;i<6;i++)
|
|
||||||
s[i] = 1;
|
|
||||||
for (int i=0;i<len;i++) {
|
|
||||||
a = buf[i] ^ s[0];
|
|
||||||
s[0] = a ^ s[1];
|
|
||||||
s[1] = s[2];
|
|
||||||
s[2] = s[3];
|
|
||||||
s[3] = a ^ s[4];
|
|
||||||
s[4] = a ^ s[5];
|
|
||||||
s[5] = a;
|
|
||||||
}
|
|
||||||
return load_i(s, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t crc12f(const uint8_t buf[], int len)
|
|
||||||
{
|
|
||||||
uint8_t s[12];
|
|
||||||
uint8_t a;
|
|
||||||
for (int i=0;i<12;i++)
|
|
||||||
s[i] = 1;
|
|
||||||
for (int i=0;i<len;i++) {
|
|
||||||
a = buf[i] ^ s[0];
|
|
||||||
s[0] = a ^ s[1];
|
|
||||||
s[1] = s[2];
|
|
||||||
s[2] = s[3];
|
|
||||||
s[3] = s[4];
|
|
||||||
s[4] = s[5];
|
|
||||||
s[5] = s[6];
|
|
||||||
s[6] = s[7];
|
|
||||||
s[7] = s[8];
|
|
||||||
s[8] = a ^ s[9];
|
|
||||||
s[9] = a ^ s[10];
|
|
||||||
s[10] = a ^ s[11];
|
|
||||||
s[11] = a;
|
|
||||||
}
|
|
||||||
return load_i(s, 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t crc15(const uint8_t buf[], int len)
|
|
||||||
{
|
|
||||||
uint8_t s[15];
|
|
||||||
uint8_t a;
|
|
||||||
for (int i=0;i<15;i++)
|
|
||||||
s[i] = 1;
|
|
||||||
for (int i=0;i<len;i++) {
|
|
||||||
a = buf[i] ^ s[0];
|
|
||||||
s[0] = a ^ s[1];
|
|
||||||
s[1] = s[2];
|
|
||||||
s[2] = s[3];
|
|
||||||
s[3] = a ^ s[4];
|
|
||||||
s[4] = a ^ s[5];
|
|
||||||
s[5] = s[6];
|
|
||||||
s[6] = s[7];
|
|
||||||
s[7] = a ^ s[8];
|
|
||||||
s[8] = a ^ s[9];
|
|
||||||
s[9] = s[10];
|
|
||||||
s[10] = s[11];
|
|
||||||
s[11] = s[12];
|
|
||||||
s[12] = a ^ s[13];
|
|
||||||
s[13] = s[14];
|
|
||||||
s[14] = a;
|
|
||||||
}
|
|
||||||
return load_i(s, 15);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
crc = crc ^ 0xffff;
|
|
||||||
return crc & 0xffff;
|
|
||||||
}
|
|
||||||
@@ -1,766 +0,0 @@
|
|||||||
/*
|
|
||||||
============================================================================
|
|
||||||
Name : nxdn_element.c (formerly nxdn_lib)
|
|
||||||
Author :
|
|
||||||
Version : 1.0
|
|
||||||
Date : 2018 December 26
|
|
||||||
Copyright : No copyright
|
|
||||||
Description : NXDN decoding source lib - modified from nxdn_lib
|
|
||||||
Origin : Originally found at - https://github.com/LouisErigHerve/dsd
|
|
||||||
============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
uint8_t SACCH[72];
|
|
||||||
uint32_t i;
|
|
||||||
uint8_t CrcCorrect = 1;
|
|
||||||
|
|
||||||
/* Consider all SACCH CRC parts as corrects */
|
|
||||||
CrcCorrect = 1;
|
|
||||||
|
|
||||||
/* Reconstitute the full 72 bits SACCH */
|
|
||||||
for(i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
memcpy(&SACCH[i * 18], state->nxdn_sacch_frame_segment[i], 18);
|
|
||||||
|
|
||||||
/* Check CRC */
|
|
||||||
if (state->nxdn_sacch_frame_segcrc[i] != 0) CrcCorrect = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Decodes the element content */
|
|
||||||
// currently only going to run this if all four CRCs are good
|
|
||||||
if (CrcCorrect == 1) NXDN_Elements_Content_decode(opts, state, CrcCorrect, SACCH);
|
|
||||||
|
|
||||||
} /* End NXDN_SACCH_Full_decode() */
|
|
||||||
|
|
||||||
|
|
||||||
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
|
||||||
uint8_t CrcCorrect, uint8_t * ElementsContent)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
uint8_t MessageType;
|
|
||||||
uint64_t CurrentIV = 0;
|
|
||||||
unsigned long long int FullMessage = 0;
|
|
||||||
/* Get the "Message Type" field */
|
|
||||||
MessageType = (ElementsContent[2] & 1) << 5;
|
|
||||||
MessageType |= (ElementsContent[3] & 1) << 4;
|
|
||||||
MessageType |= (ElementsContent[4] & 1) << 3;
|
|
||||||
MessageType |= (ElementsContent[5] & 1) << 2;
|
|
||||||
MessageType |= (ElementsContent[6] & 1) << 1;
|
|
||||||
MessageType |= (ElementsContent[7] & 1) << 0;
|
|
||||||
|
|
||||||
//only run message type if good CRC?
|
|
||||||
if (CrcCorrect == 1) nxdn_message_type (opts, state, MessageType);
|
|
||||||
|
|
||||||
/* Save the "F1" and "F2" flags */
|
|
||||||
state->NxdnElementsContent.F1 = ElementsContent[0];
|
|
||||||
state->NxdnElementsContent.F2 = ElementsContent[1];
|
|
||||||
|
|
||||||
/* Save the "Message Type" field */
|
|
||||||
state->NxdnElementsContent.MessageType = MessageType;
|
|
||||||
|
|
||||||
/* Decode the right "Message Type" */
|
|
||||||
switch(MessageType)
|
|
||||||
{
|
|
||||||
//VCALL_ASSGN
|
|
||||||
case 0x04:
|
|
||||||
//continue flow to DUP, both use same message format
|
|
||||||
|
|
||||||
//VCALL_ASSGN_DUP
|
|
||||||
case 0x05:
|
|
||||||
NXDN_decode_VCALL_ASSGN(opts, state, ElementsContent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//Alias 0x3F
|
|
||||||
case 0x3F:
|
|
||||||
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
|
|
||||||
NXDN_decode_Alias(opts, state, ElementsContent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//SRV_INFO
|
|
||||||
case 0x19:
|
|
||||||
NXDN_decode_srv_info(opts, state, ElementsContent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//CCH_INFO
|
|
||||||
case 0x1A:
|
|
||||||
NXDN_decode_cch_info(opts, state, ElementsContent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//SITE_INFO
|
|
||||||
case 0x18:
|
|
||||||
NXDN_decode_site_info(opts, state, ElementsContent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//DISC
|
|
||||||
case 0x11:
|
|
||||||
//NXDN_decode_VCALL(opts, state, ElementsContent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//Idle
|
|
||||||
case 0x10:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VCALL */
|
|
||||||
case 0x01:
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Set the CRC state */
|
|
||||||
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
|
|
||||||
|
|
||||||
/* Decode the "VCALL" message */
|
|
||||||
NXDN_decode_VCALL(opts, state, ElementsContent);
|
|
||||||
|
|
||||||
/* Check the "Cipher Type" and the "Key ID" validity */
|
|
||||||
if(CrcCorrect)
|
|
||||||
{
|
|
||||||
state->NxdnElementsContent.CipherParameterValidity = 1;
|
|
||||||
}
|
|
||||||
else state->NxdnElementsContent.CipherParameterValidity = 0;
|
|
||||||
break;
|
|
||||||
} /* End case NXDN_VCALL: */
|
|
||||||
|
|
||||||
/* VCALL_IV */
|
|
||||||
case 0x03:
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Set the CRC state */
|
|
||||||
state->NxdnElementsContent.VCallIvCrcIsGood = CrcCorrect;
|
|
||||||
|
|
||||||
/* Decode the "VCALL_IV" message */
|
|
||||||
NXDN_decode_VCALL_IV(opts, state, ElementsContent);
|
|
||||||
|
|
||||||
if(CrcCorrect)
|
|
||||||
{
|
|
||||||
/* CRC is correct, copy the next theorical IV to use directly from the
|
|
||||||
* received VCALL_IV */
|
|
||||||
memcpy(state->NxdnElementsContent.NextIVComputed, state->NxdnElementsContent.IV, 8);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* CRC is incorrect, compute the next IV to use */
|
|
||||||
CurrentIV = 0;
|
|
||||||
|
|
||||||
/* Convert the 8 bytes buffer into a 64 bits integer */
|
|
||||||
for(i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
CurrentIV |= state->NxdnElementsContent.NextIVComputed[i];
|
|
||||||
CurrentIV = CurrentIV << 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} /* End case NXDN_VCALL_IV: */
|
|
||||||
|
|
||||||
/* Unknown Message Type */
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} /* End switch(MessageType) */
|
|
||||||
|
|
||||||
} /* End NXDN_Elements_Content_decode() */
|
|
||||||
|
|
||||||
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|
||||||
{
|
|
||||||
//just using 'short form' M only data, not the optional data
|
|
||||||
uint8_t CCOption = 0;
|
|
||||||
uint8_t CallType = 0;
|
|
||||||
uint8_t VoiceCallOption = 0;
|
|
||||||
uint16_t SourceUnitID = 0;
|
|
||||||
uint16_t DestinationID = 0;
|
|
||||||
uint8_t CallTimer = 0;
|
|
||||||
uint16_t Channel = 0;
|
|
||||||
uint8_t LocationIDOption = 0;
|
|
||||||
|
|
||||||
uint8_t DuplexMode[32] = {0};
|
|
||||||
uint8_t TransmissionMode[32] = {0};
|
|
||||||
|
|
||||||
/* Decode "CC Option" */
|
|
||||||
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
|
|
||||||
state->NxdnElementsContent.CCOption = CCOption;
|
|
||||||
|
|
||||||
/* Decode "Call Type" */
|
|
||||||
CallType = (uint8_t)ConvertBitIntoBytes(&Message[16], 3);
|
|
||||||
state->NxdnElementsContent.CallType = CallType;
|
|
||||||
|
|
||||||
/* Decode "Voice Call Option" */
|
|
||||||
VoiceCallOption = (uint8_t)ConvertBitIntoBytes(&Message[19], 5);
|
|
||||||
state->NxdnElementsContent.VoiceCallOption = VoiceCallOption;
|
|
||||||
|
|
||||||
/* Decode "Source Unit ID" */
|
|
||||||
SourceUnitID = (uint16_t)ConvertBitIntoBytes(&Message[24], 16);
|
|
||||||
state->NxdnElementsContent.SourceUnitID = SourceUnitID;
|
|
||||||
|
|
||||||
/* Decode "Destination ID" */
|
|
||||||
DestinationID = (uint16_t)ConvertBitIntoBytes(&Message[40], 16);
|
|
||||||
state->NxdnElementsContent.DestinationID = DestinationID;
|
|
||||||
|
|
||||||
/* Decode "Call Timer" */ //unsure of format of call timer, not required info for trunking
|
|
||||||
CallTimer = (uint8_t)ConvertBitIntoBytes(&Message[56], 6);
|
|
||||||
|
|
||||||
/* Decode "Channel" */
|
|
||||||
Channel = (uint16_t)ConvertBitIntoBytes(&Message[62], 10);
|
|
||||||
|
|
||||||
/* Decode "Location ID Option" */ //is in the optional field, probably best not to run this one
|
|
||||||
//LocationIDOption = (uint8_t)ConvertBitIntoBytes(&Message[72], 5);
|
|
||||||
|
|
||||||
/* Print the "CC Option" */
|
|
||||||
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
|
|
||||||
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
|
|
||||||
if(CCOption & 0x20) fprintf(stderr, "Priority Paging ");
|
|
||||||
|
|
||||||
/* Print the "Call Type" */
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType));
|
|
||||||
|
|
||||||
/* Print the "Voice Call Option" */
|
|
||||||
NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
|
|
||||||
fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
|
|
||||||
|
|
||||||
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
|
|
||||||
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
|
|
||||||
|
|
||||||
/* Print Channel (Skipping Call Timer)*/
|
|
||||||
fprintf(stderr, "- Channel [%03X][%04d] ", Channel & 0x3FF, Channel & 0x3FF);
|
|
||||||
|
|
||||||
//run process to figure out frequency value from the channel, or from lcn import array
|
|
||||||
long int freq = 0;
|
|
||||||
freq = nxdn_channel_to_frequency(opts, state, Channel);
|
|
||||||
|
|
||||||
//check for control channel frequency in the channel map if not available
|
|
||||||
if (opts->p25_trunk == 1 && state->p25_cc_freq == 0)
|
|
||||||
{
|
|
||||||
long int ccfreq = 0;
|
|
||||||
if (state->trunk_chan_map[0] != 0) state->p25_cc_freq = state->trunk_chan_map[0];
|
|
||||||
|
|
||||||
//we are assuming that the current channel is the control channel
|
|
||||||
//unsure whether or not control signalling is available on NXDN voice channels
|
|
||||||
|
|
||||||
//if not available from the import file, then poll rigctl if its available
|
|
||||||
else if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
ccfreq = (long int)opts->rtlsdr_center_freq;
|
|
||||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//run group/source analysis and tune if available/desired
|
|
||||||
//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");
|
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == DestinationID) //source, or destination?
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
//might not be ideal if both source and group/target are both in the array
|
|
||||||
else if (state->group_array[i].groupNumber == SourceUnitID) //source, or destination?
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//check to see if the source/target candidate is blocked first
|
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0)) //DE is digital encrypted, B is block
|
|
||||||
{
|
|
||||||
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 already
|
|
||||||
{
|
|
||||||
//rigctl
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, freq);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
//set rid and tg when we actually tune to it
|
|
||||||
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
|
|
||||||
state->nxdn_last_tg = (DestinationID & 0xFFFF);
|
|
||||||
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
} /* End NXDN_decode_VCALL_ASSGN() */
|
|
||||||
|
|
||||||
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|
||||||
{
|
|
||||||
uint8_t Alias1 = 0x0; //value of an ascii 'NULL' character
|
|
||||||
uint8_t Alias2 = 0x0;
|
|
||||||
uint8_t Alias3 = 0x0;
|
|
||||||
uint8_t Alias4 = 0x0;
|
|
||||||
uint8_t blocknumber = 0;
|
|
||||||
uint8_t CrcCorrect = 0;
|
|
||||||
|
|
||||||
//alias can also hit on a facch1 so that would be with a non_sf_sacch attached
|
|
||||||
if (state->nxdn_sacch_non_superframe == FALSE)
|
|
||||||
{
|
|
||||||
CrcCorrect = state->NxdnElementsContent.VCallCrcIsGood;
|
|
||||||
}
|
|
||||||
else CrcCorrect = 1; //FACCH1 with bad CRC won't make it this far anyways, so set as 1
|
|
||||||
|
|
||||||
|
|
||||||
//FACCH Payload [3F][68][82][04][2 <- block number4] "[69][6F][6E][20]" <- 4 alias octets [00][7F][1C]
|
|
||||||
blocknumber = (uint8_t)ConvertBitIntoBytes(&Message[32], 4) & 0x7; // & 0x7, might just be three bits, unsure
|
|
||||||
Alias1 = (uint8_t)ConvertBitIntoBytes(&Message[40], 8);
|
|
||||||
Alias2 = (uint8_t)ConvertBitIntoBytes(&Message[48], 8);
|
|
||||||
Alias3 = (uint8_t)ConvertBitIntoBytes(&Message[56], 8);
|
|
||||||
Alias4 = (uint8_t)ConvertBitIntoBytes(&Message[64], 8);
|
|
||||||
//sanity check to prevent OOB array assignment
|
|
||||||
if (blocknumber > 0 && blocknumber < 5)
|
|
||||||
{
|
|
||||||
//assign to index -1, since block number conveyed here is 1,2,3,4, and index values are 0,1,2,3
|
|
||||||
//only assign if within valid range of ascii characters (not including diacritical extended alphabet)
|
|
||||||
//else assign "null" ascii character
|
|
||||||
|
|
||||||
//since we are zeroing out the blocks on tx_rel and other conditions, better to just set nothing to bad Alias bytes
|
|
||||||
//tends to zero out otherwise already good blocks set in a previous repitition.
|
|
||||||
if (Alias1 > 0x19 && Alias1 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][0], "%c", Alias1);
|
|
||||||
else ;// sprintf (state->nxdn_alias_block_segment[blocknumber-1][0], "%c", 0);
|
|
||||||
|
|
||||||
if (Alias2 > 0x19 && Alias2 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][1], "%c", Alias2);
|
|
||||||
else ; //sprintf (state->nxdn_alias_block_segment[blocknumber-1][1], "%c", 0);
|
|
||||||
|
|
||||||
if (Alias3 > 0x19 && Alias3 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][2], "%c", Alias3);
|
|
||||||
else ; //sprintf (state->nxdn_alias_block_segment[blocknumber-1][2], "%c", 0);
|
|
||||||
|
|
||||||
if (Alias4 > 0x19 && Alias4 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][3], "%c", Alias4);
|
|
||||||
else ; //sprintf (state->nxdn_alias_block_segment[blocknumber-1][3], "%c", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//crc errs in one repitition may occlude an otherwise good alias, so test and change if needed
|
|
||||||
//completed alias should still appear in ncurses terminal regardless, so this may be okay
|
|
||||||
if (CrcCorrect)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " "); //spacer
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", state->nxdn_alias_block_segment[i][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf (stderr, " ");
|
|
||||||
}
|
|
||||||
else fprintf (stderr, " CRC ERR "); //redundant print? or okay?
|
|
||||||
}
|
|
||||||
|
|
||||||
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|
||||||
{
|
|
||||||
uint32_t location_id = 0;
|
|
||||||
uint8_t channel1sts = 0;
|
|
||||||
uint16_t channel1 = 0;
|
|
||||||
uint8_t channel2sts = 0;
|
|
||||||
uint16_t channel2 = 0;
|
|
||||||
long int freq1 = 0;
|
|
||||||
long int freq2 = 0;
|
|
||||||
|
|
||||||
location_id = (uint32_t)ConvertBitIntoBytes(&Message[8], 24);
|
|
||||||
channel1sts = (uint8_t)ConvertBitIntoBytes(&Message[32], 6);
|
|
||||||
channel1 = (uint16_t)ConvertBitIntoBytes(&Message[38], 10);
|
|
||||||
channel2sts = (uint8_t)ConvertBitIntoBytes(&Message[48], 6);
|
|
||||||
channel2 = (uint16_t)ConvertBitIntoBytes(&Message[54], 10);
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
nxdn_location_id_handler (state, location_id);
|
|
||||||
fprintf (stderr, "\n Control Channel Information \n");
|
|
||||||
fprintf (stderr, " Location ID [%06X] CC1 [%03X][%04d] CC2 [%03X][%04d] Status: ", location_id, channel1, channel1, channel2, channel2);
|
|
||||||
|
|
||||||
//check the sts bits to determine if current, new, add, or delete
|
|
||||||
if (channel1sts & 0x20) fprintf (stderr, "Current ");
|
|
||||||
else if (channel1sts & 0x10) fprintf (stderr, "New ");
|
|
||||||
else if (channel1sts & 0x8) fprintf (stderr, "Candidate Added ");
|
|
||||||
else if (channel1sts & 0x4) fprintf (stderr, "Candidate Deleted ");
|
|
||||||
freq1 = nxdn_channel_to_frequency (opts, state, channel1);
|
|
||||||
freq2 = nxdn_channel_to_frequency (opts, state, channel2);
|
|
||||||
|
|
||||||
//add handling for adding (or deleting?) frequencies to CC list
|
|
||||||
if (channel1sts & 0x20 || channel1sts & 0x8) //current or new only
|
|
||||||
{
|
|
||||||
if (freq1 != 0) state->p25_cc_freq = freq1;
|
|
||||||
//let's assume the frequency we are tuned to is the current control channel
|
|
||||||
else if (opts->use_rigctl == 1 && state->p25_cc_freq == 0) //if not set from channel map 0 or nxdn_channel_to_frequency and rigctl is available
|
|
||||||
{
|
|
||||||
freq1 = GetCurrentFreq (opts->rigctl_sockfd);
|
|
||||||
if (freq1 != 0) state->p25_cc_freq = freq1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channel1sts & 0x8) //Candidate Added
|
|
||||||
{
|
|
||||||
if (freq1 != 0)
|
|
||||||
{
|
|
||||||
//add to trunk_lcn_freq list?
|
|
||||||
for (int i = 0; i <= state->lcn_freq_count; i++) //<= pretty sure is correct here since we need to go through atleast one rep
|
|
||||||
{
|
|
||||||
if (freq1 == state->trunk_lcn_freq[i]) goto SKIP;
|
|
||||||
}
|
|
||||||
//assign if freq1 not found in array
|
|
||||||
state->trunk_lcn_freq[state->lcn_freq_count] = freq1;
|
|
||||||
state->lcn_freq_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if a delete, let's not bother trying to remove it from the list for now
|
|
||||||
SKIP: ; //do nothing
|
|
||||||
//channel 2 should only be valid on 'current' status, but not sure what value it would contain vs channel 1
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|
||||||
{
|
|
||||||
uint32_t location_id = 0;
|
|
||||||
uint16_t svc_info = 0; //service information
|
|
||||||
uint32_t rst_info = 0; //restriction information
|
|
||||||
|
|
||||||
location_id = (uint32_t)ConvertBitIntoBytes(&Message[8], 24);
|
|
||||||
svc_info = (uint16_t)ConvertBitIntoBytes(&Message[32], 16);
|
|
||||||
rst_info = (uint32_t)ConvertBitIntoBytes(&Message[48], 24);
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, "\n Service Information - ");
|
|
||||||
fprintf (stderr, "Location ID [%06X] SVC [%04X] RST [%06X] ", location_id, svc_info, rst_info);
|
|
||||||
nxdn_location_id_handler (state, location_id);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//externalize the location handler to its own function
|
|
||||||
void nxdn_location_id_handler (dsd_state * state, uint32_t location_id)
|
|
||||||
{
|
|
||||||
//6.5.2 Location ID
|
|
||||||
uint8_t category_bit = location_id >> 22;
|
|
||||||
uint32_t sys_code = 0;
|
|
||||||
uint16_t site_code = 0;
|
|
||||||
|
|
||||||
char category[14]; //G, R, or L
|
|
||||||
|
|
||||||
if (category_bit == 0)
|
|
||||||
{
|
|
||||||
sys_code = ( (location_id & 0x3FFFFF) >> 12); //10 bits
|
|
||||||
site_code = location_id & 0x3FF; //12 bits
|
|
||||||
sprintf (category, "%s", "Global");
|
|
||||||
}
|
|
||||||
else if (category_bit == 2)
|
|
||||||
{
|
|
||||||
sys_code = ( (location_id & 0x3FFFFF) >> 8); //14 bits
|
|
||||||
site_code = location_id & 0xFF; //8 bits
|
|
||||||
sprintf (category, "%s", "Regional");
|
|
||||||
}
|
|
||||||
else if (category_bit == 1)
|
|
||||||
{
|
|
||||||
sys_code = ( (location_id & 0x3FFFFF) >> 5); //17 bits
|
|
||||||
site_code = location_id & 0x1F; //5 bits
|
|
||||||
sprintf (category, "%s", "Local");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//err, or we shouldn't ever get here
|
|
||||||
sprintf (category, "%s", "Reserved/Err");
|
|
||||||
}
|
|
||||||
|
|
||||||
//not entirely convinced that site code and ran are the exact same thing due to bit len differences
|
|
||||||
//site code is meant to be a color code (but this could also be like the P25 p1 nac and p2 cc being the same)
|
|
||||||
state->nxdn_last_ran = site_code; //this one may drop on nocarrier
|
|
||||||
if (site_code != 0) state->nxdn_location_site_code = site_code; //this one won't
|
|
||||||
if (sys_code != 0) state->nxdn_location_sys_code = sys_code;
|
|
||||||
sprintf (state->nxdn_location_category, "%s", category);
|
|
||||||
|
|
||||||
fprintf (stderr, "\n Location Information - Cat: %s - Sys Code: %d - Site Code %d ", category, sys_code, site_code);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|
||||||
{
|
|
||||||
uint32_t location_id = 0;
|
|
||||||
uint16_t cs_info = 0; //channel structure information
|
|
||||||
uint16_t svc_info = 0; //service information
|
|
||||||
uint32_t rst_info = 0; //restriction information
|
|
||||||
uint32_t ca_info = 0; //channel access information
|
|
||||||
uint8_t version_num = 0;
|
|
||||||
uint8_t adj_alloc = 0; //number of adjacent sites (on same system?)
|
|
||||||
|
|
||||||
uint16_t channel1 = 0;
|
|
||||||
uint16_t channel2 = 0;
|
|
||||||
long int freq1 = 0;
|
|
||||||
long int freq2 = 0;
|
|
||||||
|
|
||||||
location_id = (uint32_t)ConvertBitIntoBytes(&Message[8], 24);
|
|
||||||
cs_info = (uint16_t)ConvertBitIntoBytes(&Message[32], 16);
|
|
||||||
svc_info = (uint16_t)ConvertBitIntoBytes(&Message[48], 16);
|
|
||||||
rst_info = (uint32_t)ConvertBitIntoBytes(&Message[64], 24);
|
|
||||||
ca_info = (uint32_t)ConvertBitIntoBytes(&Message[88], 24);
|
|
||||||
version_num = (uint8_t)ConvertBitIntoBytes(&Message[112], 8);
|
|
||||||
adj_alloc = (uint8_t)ConvertBitIntoBytes(&Message[120], 4);
|
|
||||||
channel1 = (uint16_t)ConvertBitIntoBytes(&Message[124], 10);
|
|
||||||
channel2 = (uint16_t)ConvertBitIntoBytes(&Message[134], 10);
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf (stderr, "\n Location ID [%06X] CSC [%04X] SVC [%04X] RST [%06X] \n CA [%06X] V[%X] ADJ [%01X] ",
|
|
||||||
location_id, cs_info, svc_info, rst_info, ca_info, version_num, adj_alloc);
|
|
||||||
nxdn_location_id_handler(state, location_id);
|
|
||||||
if (channel1 != 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n Control Channel 1 [%03X][%04d] ", channel1, channel1 );
|
|
||||||
freq1 = nxdn_channel_to_frequency (opts, state, channel1);
|
|
||||||
}
|
|
||||||
if (channel2 != 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n Control Channel 2 [%03X][%04d] ", channel2, channel2 );
|
|
||||||
freq2 = nxdn_channel_to_frequency (opts, state, channel2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
//place the freqs into the array for hunting CC or
|
|
||||||
//rotating CCs without user LCN list
|
|
||||||
if (freq1 != 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i <= state->lcn_freq_count; i++) //<= pretty sure is correct here since we need to go through atleast one rep
|
|
||||||
{
|
|
||||||
if (freq1 == state->trunk_lcn_freq[i]) goto SKIP;
|
|
||||||
}
|
|
||||||
//assign if freq1 not found in array
|
|
||||||
state->trunk_lcn_freq[state->lcn_freq_count] = freq1;
|
|
||||||
state->lcn_freq_count++;
|
|
||||||
//shim here for main CC freq?
|
|
||||||
state->p25_cc_freq = freq1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (freq2 != 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i <= state->lcn_freq_count; i++) //<= pretty sure is correct here since we need to go through atleast one rep
|
|
||||||
{
|
|
||||||
if (freq2 == state->trunk_lcn_freq[i]) goto SKIP;
|
|
||||||
}
|
|
||||||
//assign if freq2 not found in array
|
|
||||||
state->trunk_lcn_freq[state->lcn_freq_count] = freq2;
|
|
||||||
state->lcn_freq_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
SKIP: ; //do nothing
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|
||||||
{
|
|
||||||
|
|
||||||
uint8_t CCOption = 0;
|
|
||||||
uint8_t CallType = 0;
|
|
||||||
uint8_t VoiceCallOption = 0;
|
|
||||||
uint16_t SourceUnitID = 0;
|
|
||||||
uint16_t DestinationID = 0;
|
|
||||||
uint8_t CipherType = 0;
|
|
||||||
uint8_t KeyID = 0;
|
|
||||||
uint8_t DuplexMode[32] = {0};
|
|
||||||
uint8_t TransmissionMode[32] = {0};
|
|
||||||
unsigned long long int FullMessage = 0;
|
|
||||||
|
|
||||||
/* Decode "CC Option" */
|
|
||||||
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
|
|
||||||
state->NxdnElementsContent.CCOption = CCOption;
|
|
||||||
|
|
||||||
/* Decode "Call Type" */
|
|
||||||
CallType = (uint8_t)ConvertBitIntoBytes(&Message[16], 3);
|
|
||||||
state->NxdnElementsContent.CallType = CallType;
|
|
||||||
|
|
||||||
/* Decode "Voice Call Option" */
|
|
||||||
VoiceCallOption = (uint8_t)ConvertBitIntoBytes(&Message[19], 5);
|
|
||||||
state->NxdnElementsContent.VoiceCallOption = VoiceCallOption;
|
|
||||||
|
|
||||||
/* Decode "Source Unit ID" */
|
|
||||||
SourceUnitID = (uint16_t)ConvertBitIntoBytes(&Message[24], 16);
|
|
||||||
state->NxdnElementsContent.SourceUnitID = SourceUnitID;
|
|
||||||
|
|
||||||
/* Decode "Destination ID" */
|
|
||||||
DestinationID = (uint16_t)ConvertBitIntoBytes(&Message[40], 16);
|
|
||||||
state->NxdnElementsContent.DestinationID = DestinationID;
|
|
||||||
|
|
||||||
/* Decode the "Cipher Type" */
|
|
||||||
CipherType = (uint8_t)ConvertBitIntoBytes(&Message[56], 2);
|
|
||||||
state->NxdnElementsContent.CipherType = CipherType;
|
|
||||||
|
|
||||||
/* Decode the "Key ID" */
|
|
||||||
KeyID = (uint8_t)ConvertBitIntoBytes(&Message[58], 6);
|
|
||||||
state->NxdnElementsContent.KeyID = KeyID;
|
|
||||||
|
|
||||||
/* Print the "CC Option" */
|
|
||||||
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
|
|
||||||
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
|
|
||||||
if(CCOption & 0x20) fprintf(stderr, "Priority Paging ");
|
|
||||||
|
|
||||||
if((CipherType == 2) || (CipherType == 3))
|
|
||||||
{
|
|
||||||
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 1;
|
|
||||||
state->NxdnElementsContent.PartOfNextEncryptedFrame = 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 1;
|
|
||||||
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print the "Call Type" */
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType));
|
|
||||||
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
|
|
||||||
|
|
||||||
/* Print the "Voice Call Option" */
|
|
||||||
NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
|
|
||||||
fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
|
|
||||||
|
|
||||||
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
|
|
||||||
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
|
|
||||||
/* Print the "Cipher Type" */
|
|
||||||
if(CipherType != 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n %s", KYEL);
|
|
||||||
fprintf(stderr, "%s - ", NXDN_Cipher_Type_To_Str(CipherType));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print the Key ID */
|
|
||||||
if(CipherType != 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Key ID %u - ", KeyID & 0xFF);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->nxdn_cipher_type == 0x01 && state->R > 0) //scrambler key value
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
|
||||||
fprintf(stderr, "Value: %05lld", state->R);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
//only grab if CRC is okay
|
|
||||||
if(state->NxdnElementsContent.VCallCrcIsGood)
|
|
||||||
{
|
|
||||||
if ( (SourceUnitID & 0xFFFF) > 0 )
|
|
||||||
{
|
|
||||||
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
|
|
||||||
state->nxdn_last_tg = (DestinationID & 0xFFFF);
|
|
||||||
state->nxdn_key = (KeyID & 0xFF);
|
|
||||||
state->nxdn_cipher_type = CipherType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf(stderr, "(CRC ERR) ");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
|
|
||||||
if (state->nxdn_cipher_type != 0)
|
|
||||||
{
|
|
||||||
state->dmr_encL = 1;
|
|
||||||
}
|
|
||||||
if (state->nxdn_cipher_type == 0 || state->R != 0)
|
|
||||||
{
|
|
||||||
state->dmr_encL = 0;
|
|
||||||
}
|
|
||||||
} /* End NXDN_decode_VCALL() */
|
|
||||||
|
|
||||||
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
state->payload_miN = 0; //zero out
|
|
||||||
|
|
||||||
/* Extract the IV from the VCALL_IV message */
|
|
||||||
for(i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
state->NxdnElementsContent.IV[i] = (uint8_t)ConvertBitIntoBytes(&Message[(i + 1) * 8], 8);
|
|
||||||
|
|
||||||
state->payload_miN = state->payload_miN << 8 | state->NxdnElementsContent.IV[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 2;
|
|
||||||
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
|
|
||||||
|
|
||||||
} /* End NXDN_decode_VCALL_IV() */
|
|
||||||
|
|
||||||
|
|
||||||
char * NXDN_Call_Type_To_Str(uint8_t CallType)
|
|
||||||
{
|
|
||||||
char * Ptr = NULL;
|
|
||||||
|
|
||||||
switch(CallType)
|
|
||||||
{
|
|
||||||
case 0: Ptr = "Broadcast Call"; break;
|
|
||||||
case 1: Ptr = "Group Call"; break;
|
|
||||||
case 2: Ptr = "Unspecified Call"; break;
|
|
||||||
case 3: Ptr = "Reserved"; break;
|
|
||||||
case 4: Ptr = "Individual Call"; break;
|
|
||||||
case 5: Ptr = "Reserved"; break;
|
|
||||||
case 6: Ptr = "Interconnect Call"; break;
|
|
||||||
case 7: Ptr = "Speed Dial Call"; break;
|
|
||||||
default: Ptr = "Unknown Call Type"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ptr;
|
|
||||||
} /* End NXDN_Call_Type_To_Str() */
|
|
||||||
|
|
||||||
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode)
|
|
||||||
{
|
|
||||||
char * Ptr = NULL;
|
|
||||||
|
|
||||||
Duplex[0] = 0;
|
|
||||||
TransmissionMode[0] = 0;
|
|
||||||
|
|
||||||
if(VoiceCallOption & 0x10) strcpy((char *)Duplex, "Duplex");
|
|
||||||
else strcpy((char *)Duplex, "Half Duplex");
|
|
||||||
|
|
||||||
switch(VoiceCallOption & 0x17)
|
|
||||||
{
|
|
||||||
case 0: Ptr = "4800bps/EHR"; break;
|
|
||||||
case 2: Ptr = "9600bps/EHR"; break;
|
|
||||||
case 3: Ptr = "9600bps/EFR"; break;
|
|
||||||
default: Ptr = "Reserved Voice Call Option"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy((char *)TransmissionMode, Ptr);
|
|
||||||
} /* End NXDN_Voice_Call_Option_To_Str() */
|
|
||||||
|
|
||||||
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType)
|
|
||||||
{
|
|
||||||
char * Ptr = NULL;
|
|
||||||
|
|
||||||
switch(CipherType)
|
|
||||||
{
|
|
||||||
case 0: Ptr = ""; break; /* Non-ciphered mode / clear call */
|
|
||||||
case 1: Ptr = "Scrambler"; break;
|
|
||||||
case 2: Ptr = "DES"; break;
|
|
||||||
case 3: Ptr = "AES"; break;
|
|
||||||
default: Ptr = "Unknown Cipher Type"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ptr;
|
|
||||||
} /* End NXDN_Cipher_Type_To_Str() */
|
|
||||||
254
src/nxdn_frame.c
254
src/nxdn_frame.c
@@ -1,254 +0,0 @@
|
|||||||
//NXDN frame handler
|
|
||||||
//Reworked portions from Osmocom OP25 rx_sync.cc
|
|
||||||
|
|
||||||
/* -*- c++ -*- */
|
|
||||||
/*
|
|
||||||
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* This is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this software; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include "nxdn_const.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
// length is implicitly 192, with frame sync in first 10 dibits
|
|
||||||
uint8_t dbuf[182];
|
|
||||||
uint8_t lich;
|
|
||||||
int answer_len=0;
|
|
||||||
uint8_t answer[32];
|
|
||||||
uint8_t sacch_answer[32];
|
|
||||||
int lich_parity_received;
|
|
||||||
int lich_parity_computed;
|
|
||||||
int voice=0;
|
|
||||||
int facch=0;
|
|
||||||
int facch2=0;
|
|
||||||
int 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
|
|
||||||
|
|
||||||
//nxdn bit buffer, for easy assignment handling
|
|
||||||
int nxdn_bit_buffer[364] = {0};
|
|
||||||
int nxdn_dibit_buffer[182] = {0};
|
|
||||||
|
|
||||||
memset (dbuf, 0, sizeof(dbuf));
|
|
||||||
|
|
||||||
//collect lich bits first, if they are good, then we can collect the rest of them
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
nxdn_descramble (lich_dibits, 8);
|
|
||||||
|
|
||||||
lich = 0;
|
|
||||||
for (int i=0; i<8; i++)
|
|
||||||
{
|
|
||||||
lich |= (lich_dibits[i] >> 1) << (7-i);
|
|
||||||
}
|
|
||||||
|
|
||||||
lich_parity_received = lich & 1;
|
|
||||||
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1;
|
|
||||||
lich = lich >> 1;
|
|
||||||
if (lich_parity_received != lich_parity_computed)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "NXDN lich parity error, ignoring frame \n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
voice = 0;
|
|
||||||
facch = 0;
|
|
||||||
facch2 = 0;
|
|
||||||
sacch = 0;
|
|
||||||
cac = 0;
|
|
||||||
switch(lich) { //cases without breaks continue to flow downwards until they hit the break
|
|
||||||
case 0x01: // CAC types
|
|
||||||
case 0x05:
|
|
||||||
cac = 1;
|
|
||||||
break;
|
|
||||||
case 0x28: //facch2 types
|
|
||||||
case 0x29:
|
|
||||||
case 0x2e:
|
|
||||||
case 0x2f:
|
|
||||||
case 0x48:
|
|
||||||
case 0x49:
|
|
||||||
case 0x4e:
|
|
||||||
case 0x4f:
|
|
||||||
case 0x69:
|
|
||||||
case 0x6f:
|
|
||||||
facch2 = 1;
|
|
||||||
break;
|
|
||||||
case 0x32: //facch in 1, vch in 2
|
|
||||||
case 0x33:
|
|
||||||
case 0x52:
|
|
||||||
case 0x53:
|
|
||||||
case 0x73:
|
|
||||||
voice = 2;
|
|
||||||
facch = 1;
|
|
||||||
sacch = 1;
|
|
||||||
break;
|
|
||||||
case 0x34: //vch in 1, facch in 2
|
|
||||||
case 0x35:
|
|
||||||
case 0x54:
|
|
||||||
case 0x55:
|
|
||||||
case 0x75:
|
|
||||||
voice = 1;
|
|
||||||
facch = 2;
|
|
||||||
sacch = 1;
|
|
||||||
break;
|
|
||||||
case 0x36: //vch in both
|
|
||||||
case 0x37:
|
|
||||||
case 0x56:
|
|
||||||
case 0x57:
|
|
||||||
case 0x77:
|
|
||||||
voice = 3;
|
|
||||||
facch = 0;
|
|
||||||
sacch = 1;
|
|
||||||
break;
|
|
||||||
case 0x20: //facch in both
|
|
||||||
case 0x21:
|
|
||||||
case 0x30:
|
|
||||||
case 0x31:
|
|
||||||
case 0x40:
|
|
||||||
case 0x41:
|
|
||||||
case 0x50:
|
|
||||||
case 0x51:
|
|
||||||
case 0x61:
|
|
||||||
case 0x71:
|
|
||||||
voice = 0;
|
|
||||||
facch = 3;
|
|
||||||
sacch = 1;
|
|
||||||
break;
|
|
||||||
case 0x38: //sacch only (NULL?)
|
|
||||||
case 0x39:
|
|
||||||
sacch = 1;
|
|
||||||
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_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
|
||||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
|
||||||
voice = 0;
|
|
||||||
goto END;
|
|
||||||
break;
|
|
||||||
} // end of switch(lich)
|
|
||||||
|
|
||||||
//printframesync after determining we have a good lich and it has something in it
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
//now that we have a good LICH, we can collect all of our dibits
|
|
||||||
//and push them to the proper places for decoding (if LICH calls for that type)
|
|
||||||
for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
|
|
||||||
{
|
|
||||||
dbuf[i+8] = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
nxdn_descramble (dbuf, 182); //sizeof(dbuf)
|
|
||||||
|
|
||||||
//seperate our dbuf (dibit_buffer) into individual bit array
|
|
||||||
for (int i = 0; i < 182; i++)
|
|
||||||
{
|
|
||||||
nxdn_bit_buffer[i*2] = dbuf[i] >> 1;
|
|
||||||
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//sacch
|
|
||||||
for (int i = 0; i < 60; i++)
|
|
||||||
{
|
|
||||||
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
|
||||||
}
|
|
||||||
|
|
||||||
//facch
|
|
||||||
for (int i = 0; i < 144; i++)
|
|
||||||
{
|
|
||||||
facch_bits_a[i] = nxdn_bit_buffer[i+16+60];
|
|
||||||
facch_bits_b[i] = nxdn_bit_buffer[i+16+60+144];
|
|
||||||
}
|
|
||||||
|
|
||||||
//cac
|
|
||||||
for (int i = 0; i < 300; i++)
|
|
||||||
{
|
|
||||||
cac_bits[i] = nxdn_bit_buffer[i+16];
|
|
||||||
}
|
|
||||||
|
|
||||||
//udch or facch2
|
|
||||||
for (int i = 0; i < 348; i++)
|
|
||||||
{
|
|
||||||
facch2_bits[i] = nxdn_bit_buffer[i+16];
|
|
||||||
}
|
|
||||||
|
|
||||||
//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)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf (stderr, " Voice ");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
char ambe_temp[49] = {0};
|
|
||||||
char ambe_d[49] = {0};
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
LFSRN(ambe_temp, ambe_d, 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 (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
|
||||||
if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
|
|
||||||
if (voice) nxdn_voice (opts, state, voice, dbuf);
|
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
END: ; //do nothing
|
|
||||||
}
|
|
||||||
1692
src/nxdn_lib.c
Normal file
1692
src/nxdn_lib.c
Normal file
File diff suppressed because it is too large
Load Diff
218
src/nxdn_voice.c
218
src/nxdn_voice.c
@@ -1,101 +1,169 @@
|
|||||||
//a new and simplified voice handling routine for NXDN based on lich signalling
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//Originally found at - https://github.com/LouisErigHerve/dsd
|
|
||||||
//Modified for use in DSD-FME
|
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
#include "nxdn_const.h"
|
||||||
|
|
||||||
const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
void processNXDNVoice (dsd_opts * opts, dsd_state * state)
|
||||||
0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 2,
|
|
||||||
0, 2, 0, 2, 0, 2,
|
|
||||||
0, 2, 0, 2, 0, 2
|
|
||||||
};
|
|
||||||
|
|
||||||
const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
|
||||||
20, 7, 19, 6, 18, 5,
|
|
||||||
17, 4, 16, 3, 15, 2,
|
|
||||||
14, 1, 13, 0, 12, 10,
|
|
||||||
11, 9, 10, 8, 9, 7,
|
|
||||||
8, 6, 7, 5, 6, 4
|
|
||||||
};
|
|
||||||
|
|
||||||
const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
|
||||||
0, 2, 0, 3, 0, 3,
|
|
||||||
1, 3, 1, 3, 1, 3,
|
|
||||||
1, 3, 1, 3, 1, 3,
|
|
||||||
1, 3, 1, 3, 1, 3,
|
|
||||||
1, 3, 1, 3, 1, 3
|
|
||||||
};
|
|
||||||
|
|
||||||
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
|
||||||
2, 0, 1, 13, 0, 12,
|
|
||||||
22, 11, 21, 10, 20, 9,
|
|
||||||
19, 8, 18, 7, 17, 6,
|
|
||||||
16, 5, 15, 4, 14, 3,
|
|
||||||
13, 2, 12, 1, 11, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182])
|
|
||||||
{
|
{
|
||||||
int i, j, dibit;
|
int i, j, dibit;
|
||||||
int start, stop;
|
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24];
|
||||||
|
unsigned char sacch_raw[72] = {0};
|
||||||
unsigned char *pr;
|
unsigned char sacch_decoded[32] = {0}; /* 26 bit + 6 bit CRC */
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
|
unsigned char *pr;
|
||||||
|
uint8_t CrcIsGood = 0;
|
||||||
|
uint8_t StructureField = 0;
|
||||||
|
uint8_t RAN = 0;
|
||||||
|
uint8_t PartOfFrame = 0;
|
||||||
|
char ambe7bytesArray[7] = {0};
|
||||||
|
int PartOfEncryptedSuperFrame = 0;
|
||||||
|
|
||||||
//these conditions will determine our starting and stopping value for voice
|
if (opts->errorbars == 1)
|
||||||
//i.e. voice in first, voice in second, or voice in both
|
{
|
||||||
if (voice == 1 || voice == 3) start = 0;
|
fprintf(stderr, "VOICE - ");
|
||||||
if (voice == 1) stop = 2;
|
}
|
||||||
if (voice == 2) start = 2;
|
|
||||||
if (voice == 2 || voice == 3) stop = 4;
|
|
||||||
|
|
||||||
for (start; start < stop; start++)
|
/* Start pseudo-random NXDN sequence after
|
||||||
|
* LICH = 16 bit = 8 dibit
|
||||||
|
* ==> Index 8 */
|
||||||
|
pr = (unsigned char *)(&nxdnpr2[8]);
|
||||||
|
for (i = 0; i < 30; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
|
||||||
|
/* Store and de-interleave directly SACCH bits */
|
||||||
|
sacch_raw[(2*i)] = ((*pr) & 1) ^ (1 & (dibit >> 1)); // bit 1
|
||||||
|
sacch_raw[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
// TODO : Deinterleave is done in the "NXDN_SACCH_decode" function
|
||||||
|
// /* Store and de-interleave directly SACCH bits */
|
||||||
|
// sacch_raw[nsW[2*i]] = *pr ^ (1 & (dibit >> 1)); // bit 1
|
||||||
|
// sacch_raw[nsW[2*i+1]] = (1 & dibit); // bit 0
|
||||||
|
pr++;
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf(stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf(stderr, " ");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO : Unpuncture is done in the "NXDN_SACCH_decode" function
|
||||||
|
// /* For the sake of simplicity, handle the re-insertion of punctured bits here.
|
||||||
|
// * This makes the actual Viterbi decoder code a LOT simpler. */
|
||||||
|
// for (i = 30; i < 36; i++)
|
||||||
|
// {
|
||||||
|
// sacch_raw[nsW[2*i]] = 0;
|
||||||
|
// sacch_raw[nsW[2*i+1]] = 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* Decode the SACCH fields */
|
||||||
|
CrcIsGood = NXDN_SACCH_raw_part_decode(sacch_raw, sacch_decoded);
|
||||||
|
StructureField = (sacch_decoded[0]<<1 | sacch_decoded[1]<<0) & 0x03; /* Compute the Structure Field (remove 2 first bits of the SR Information in the SACCH) */
|
||||||
|
RAN = (sacch_decoded[2]<<5 | sacch_decoded[3]<<4 | sacch_decoded[4]<<3 | sacch_decoded[5]<<2 | sacch_decoded[6]<<1 | sacch_decoded[7]<<0) & 0x3F; /* Compute the RAN (6 last bits of the SR Information in the SACCH) */
|
||||||
|
|
||||||
|
/* Compute the Part of Frame according to the structure field */
|
||||||
|
if (StructureField == 3) PartOfFrame = 0;
|
||||||
|
else if(StructureField == 2) PartOfFrame = 1;
|
||||||
|
else if(StructureField == 1) PartOfFrame = 2;
|
||||||
|
else if(StructureField == 0) PartOfFrame = 3;
|
||||||
|
else PartOfFrame = 0; /* Normally we should never go here */
|
||||||
|
|
||||||
|
/* Fill the SACCH structure */
|
||||||
|
state->NxdnSacchRawPart[PartOfFrame].CrcIsGood = CrcIsGood;
|
||||||
|
state->NxdnSacchRawPart[PartOfFrame].StructureField = StructureField;
|
||||||
|
state->NxdnSacchRawPart[PartOfFrame].RAN = RAN;
|
||||||
|
memcpy(state->NxdnSacchRawPart[PartOfFrame].Data, &sacch_decoded[8], 18); /* Copy the 18 bits of SACCH content */
|
||||||
|
|
||||||
|
//fprintf(stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
|
||||||
|
//if(CrcIsGood) fprintf(stderr, " (OK) - ");
|
||||||
|
if(CrcIsGood)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
//fprintf (stderr, "(CRC OK) ");
|
||||||
|
state->nxdn_last_ran = RAN; //disable, try to grab this in voice instead
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf(stderr, "(CRC ERR) ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Determine the current part of superframe
|
||||||
|
* (in an AES or DES encrypted frame, two superframes
|
||||||
|
* are used with the same IV, so to decipher the sound
|
||||||
|
* correctly we must know the current superframe part) */
|
||||||
|
if(state->NxdnElementsContent.PartOfNextEncryptedFrame == 1) PartOfEncryptedSuperFrame = 0;
|
||||||
|
else if(state->NxdnElementsContent.PartOfNextEncryptedFrame == 2) PartOfEncryptedSuperFrame = 1;
|
||||||
|
else PartOfEncryptedSuperFrame = 0;
|
||||||
|
|
||||||
|
/* Decode the SACCH only when all 4 voice frame
|
||||||
|
* SACCH spare parts have been received */
|
||||||
|
if(PartOfFrame == 3)
|
||||||
|
{
|
||||||
|
/* Decode the entire SACCH content */
|
||||||
|
NXDN_SACCH_Full_decode(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "e:");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start pseudo-random NXDN sequence after
|
||||||
|
* LICH = 16 bit = 8 dibit +
|
||||||
|
* SACCH = 60 bit = 30 dibit
|
||||||
|
* = 76 bit = 38 dibit
|
||||||
|
* ==> Index 38 */
|
||||||
|
pr = (unsigned char *)(&nxdnpr2[38]);
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
w = nW;
|
w = nW;
|
||||||
x = nX;
|
x = nX;
|
||||||
y = nY;
|
y = nY;
|
||||||
z = nZ;
|
z = nZ;
|
||||||
|
|
||||||
for (i = 0; i < 36; i++)
|
for (i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
|
dibit = getDibit (opts, state);
|
||||||
|
#ifdef NXDN_DUMP
|
||||||
|
fprintf(stderr, "%c", dibit + 48);
|
||||||
|
#endif
|
||||||
|
ambe_fr[*w][*x] = *pr ^ (1 & (dibit >> 1)); // bit 1
|
||||||
|
pr++;
|
||||||
|
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
//skip 8 lich and 30 sacch dibits already in buffer plus 36 times start position
|
|
||||||
ambe_fr[*w][*x] = dbuf[i+38+start*36] >> 1;
|
|
||||||
ambe_fr[*y][*z] = dbuf[i+38+start*36] & 1;
|
|
||||||
|
|
||||||
w++;
|
w++;
|
||||||
x++;
|
x++;
|
||||||
y++;
|
y++;
|
||||||
z++;
|
z++;
|
||||||
|
}
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
}
|
state->p25vc++; //increment counter by one here?
|
||||||
|
|
||||||
if (opts->payload == 1)
|
} /* End for (j = 0; j < 4; j++) */
|
||||||
|
|
||||||
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
/* Reset the SACCH CRCs when all 4 voice frame
|
||||||
|
* SACCH spare parts have been received */
|
||||||
|
if(PartOfFrame == 3)
|
||||||
|
{
|
||||||
|
/* Reset all CRCs of the SACCH */
|
||||||
|
if (state->R > 0 && state->nxdn_cipher_type == 0x1)
|
||||||
|
{
|
||||||
|
state->payload_miN = state->R;
|
||||||
|
}
|
||||||
|
for(i = 0; i < 4; i++) state->NxdnSacchRawPart[i].CrcIsGood = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
} /* End processNXDNVoice() */
|
||||||
|
|||||||
@@ -24,8 +24,10 @@
|
|||||||
|
|
||||||
|
|
||||||
/* Include ------------------------------------------------------------------*/
|
/* Include ------------------------------------------------------------------*/
|
||||||
|
//#include "nxdnconvolution.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
//#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@@ -59,6 +61,17 @@ static uint64_t * m_dp = NULL;
|
|||||||
|
|
||||||
/* Functions ----------------------------------------------------------------*/
|
/* Functions ----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void CNXDNConvolution_start(void)
|
||||||
|
{
|
||||||
|
memset(m_metrics1, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
|
||||||
|
memset(m_metrics2, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
|
||||||
|
|
||||||
|
m_oldMetrics = m_metrics1;
|
||||||
|
m_newMetrics = m_metrics2;
|
||||||
|
m_dp = m_decisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
|
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
|
||||||
{
|
{
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
@@ -156,15 +169,3 @@ void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsign
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNXDNConvolution_start(void)
|
|
||||||
{
|
|
||||||
memset(m_metrics1, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
|
|
||||||
memset(m_metrics2, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
|
|
||||||
|
|
||||||
m_oldMetrics = m_metrics1;
|
|
||||||
m_newMetrics = m_metrics2;
|
|
||||||
m_dp = m_decisions;
|
|
||||||
|
|
||||||
for (int x = 0; x < 4; x++) CNXDNConvolution_decode(0U, 0U);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -2,15 +2,12 @@
|
|||||||
* p25_frequency.c
|
* p25_frequency.c
|
||||||
* P25 Channel to Frequency Calculator
|
* P25 Channel to Frequency Calculator
|
||||||
*
|
*
|
||||||
* NXDN Channel to Frequency, Courtesy of IcomIcR20 and EricCottrell (base values)
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
* LWVMOBILE
|
||||||
* 2022-11 DSD-FME Florida Man Edition
|
* 2022-09 DSD-FME Florida Man Edition
|
||||||
*-----------------------------------------------------------------------------*/
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
//P25
|
|
||||||
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel)
|
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -31,78 +28,17 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
|
|||||||
int type = state->p25_chan_type[iden];
|
int type = state->p25_chan_type[iden];
|
||||||
int slots_per_carrier[16] = {1,1,1,2,4,2,2,2,2,2,2,2,2,2,2,2}; //from OP25
|
int slots_per_carrier[16] = {1,1,1,2,4,2,2,2,2,2,2,2,2,2,2,2}; //from OP25
|
||||||
int step = (channel & 0xFFF) / slots_per_carrier[type];
|
int step = (channel & 0xFFF) / slots_per_carrier[type];
|
||||||
|
|
||||||
//first, check channel map
|
|
||||||
if (state->trunk_chan_map[channel] != 0)
|
|
||||||
{
|
|
||||||
freq = state->trunk_chan_map[channel];
|
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
|
||||||
return (freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if not found, attempt to find it via calculation
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (state->p25_base_freq[iden] != 0)
|
|
||||||
{
|
|
||||||
freq = (state->p25_base_freq[iden] * 5) + ( step * state->p25_chan_spac[iden] * 125);
|
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
|
||||||
return (freq);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n Base Frequency Not Found - Iden [%d]", iden);
|
|
||||||
fprintf(stderr, "\n or Channel not found in import file");
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//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;
|
|
||||||
|
|
||||||
//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
|
|
||||||
|
|
||||||
//first, check channel map
|
|
||||||
if (state->trunk_chan_map[channel] != 0)
|
|
||||||
{
|
|
||||||
freq = state->trunk_chan_map[channel];
|
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
|
||||||
return (freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if not found, attempt to find it via calculation
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((channel > 0) && (channel <= 400))
|
|
||||||
{
|
|
||||||
if (opts->frame_nxdn48 == 1) base = 450000000;
|
|
||||||
else base = 451000000;
|
|
||||||
|
|
||||||
freq = base + (channel - 1) * 12500;
|
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
|
||||||
return (freq);
|
|
||||||
}
|
|
||||||
else if ((channel >= 401) && (channel <= 800))
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (state->p25_base_freq[iden] != 0)
|
||||||
|
{
|
||||||
|
freq = (state->p25_base_freq[iden] * 5) + ( step * state->p25_chan_spac[iden] * 125);
|
||||||
|
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||||
|
return (freq);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n Base Frequency Not Found - Iden [%d]", iden);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
//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)
|
||||||
if (MFID < 0x2 && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
if (MFID < 0x2 && protectbit == 0 && err == 0 && ec == 0 && PDU[1] != 0x7B )
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s",KYEL);
|
fprintf (stderr, "%s",KMAG);
|
||||||
process_MAC_VPDU(opts, state, 0, PDU);
|
process_MAC_VPDU(opts, state, 0, PDU);
|
||||||
fprintf (stderr, "%s",KNRM);
|
fprintf (stderr, "%s",KNRM);
|
||||||
}
|
}
|
||||||
@@ -120,18 +120,10 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
|
|||||||
long int wacn = (tsbk_byte[3] << 12) | (tsbk_byte[4] << 4) | (tsbk_byte[5] >> 4);;
|
long int wacn = (tsbk_byte[3] << 12) | (tsbk_byte[4] << 4) | (tsbk_byte[5] >> 4);;
|
||||||
int sysid = ((tsbk_byte[5] & 0xF) << 8) | tsbk_byte[6];
|
int sysid = ((tsbk_byte[5] & 0xF) << 8) | tsbk_byte[6];
|
||||||
int channel = (tsbk_byte[7] << 8) | tsbk_byte[8];
|
int channel = (tsbk_byte[7] << 8) | tsbk_byte[8];
|
||||||
fprintf (stderr, "%s",KYEL);
|
fprintf (stderr, "%s",KMAG);
|
||||||
fprintf (stderr, "\n Network Status Broadcast TSBK - Abbreviated \n");
|
fprintf (stderr, "\n Network Status Broadcast TSBK - Abbreviated \n");
|
||||||
fprintf (stderr, " WACN [%05lX] SYSID [%03X] NAC [%03llX]", wacn, sysid, state->p2_cc);
|
fprintf (stderr, " WACN [%05lX] SYSID [%03X] NAC [%03llX]", wacn, sysid, state->p2_cc);
|
||||||
state->p25_cc_freq = process_channel_to_freq(opts, state, channel);
|
state->p25_cc_freq = process_channel_to_freq(opts, state, channel);
|
||||||
state->p25_cc_is_tdma = 0; //flag off for CC tuning purposes when system is qpsk
|
|
||||||
|
|
||||||
//place the cc freq into the list at index 0 if 0 is empty, or not the same,
|
|
||||||
//so we can hunt for rotating CCs without user LCN list
|
|
||||||
if (state->trunk_lcn_freq[0] == 0 || state->trunk_lcn_freq[0] != state->p25_cc_freq)
|
|
||||||
{
|
|
||||||
state->trunk_lcn_freq[0] = state->p25_cc_freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
//only set IF these values aren't already hard set by the user
|
//only set IF these values aren't already hard set by the user
|
||||||
if (state->p2_hardset == 0)
|
if (state->p2_hardset == 0)
|
||||||
|
|||||||
@@ -193,8 +193,8 @@ void process_FACCHc (dsd_opts * opts, dsd_state * state)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " R-S ERR Fc");
|
fprintf (stderr, " R-S ERR Fc");
|
||||||
//if (state->currentslot == 0) state->dmrburstL = 13;
|
if (state->currentslot == 0) state->dmrburstL = 13;
|
||||||
//else state->dmrburstR = 13;
|
else state->dmrburstR = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -247,8 +247,8 @@ void process_FACCHs (dsd_opts * opts, dsd_state * state)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " R-S ERR Fs");
|
fprintf (stderr, " R-S ERR Fs");
|
||||||
//if (state->currentslot == 0) state->dmrburstL = 13;
|
if (state->currentslot == 0) state->dmrburstL = 13;
|
||||||
//else state->dmrburstR = 13;
|
else state->dmrburstR = 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,8 +752,8 @@ void process_P2_DUID (dsd_opts * opts, dsd_state * state)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " DUID ERR %d", duid_decoded);
|
fprintf (stderr, " DUID ERR %d", duid_decoded);
|
||||||
//if (state->currentslot == 0) state->dmrburstL = 12;
|
if (state->currentslot == 0) state->dmrburstL = 12;
|
||||||
//else state->dmrburstR = 12;
|
else state->dmrburstR = 12;
|
||||||
err_counter++;
|
err_counter++;
|
||||||
}
|
}
|
||||||
if (err_counter > 1) //&& opts->aggressive_framesync == 1
|
if (err_counter > 1) //&& opts->aggressive_framesync == 1
|
||||||
|
|||||||
889
src/p25p2_vpdu.c
889
src/p25p2_vpdu.c
@@ -62,7 +62,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
else slot = state->currentslot;
|
else slot = state->currentslot;
|
||||||
|
|
||||||
//assigning here if OECI MAC SIGNAL, after passing RS and CRC
|
//assigning here if OECI MAC SIGNAL, after passing RS and CRC
|
||||||
if (state->p2_is_lcch == 1)
|
if (state->p2_is_lcch)
|
||||||
{
|
{
|
||||||
if (slot == 1) state->dmrburstL = 30;
|
if (slot == 1) state->dmrburstL = 30;
|
||||||
else state->dmrburstR = 30;
|
else state->dmrburstR = 30;
|
||||||
@@ -74,781 +74,10 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
goto END_PDU;
|
goto END_PDU;
|
||||||
}
|
}
|
||||||
|
|
||||||
//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");
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
//MFID90 Voice Grants, A3, A4, and A5
|
//New PDUs added below here as of 10-23-2022
|
||||||
//MFID90 Group Regroup Channel Grant - Implicit
|
|
||||||
if (MAC[1+len_a] == 0xA3 && MAC[2+len_a] == 0x90)
|
|
||||||
{
|
|
||||||
int mfid = MAC[2+len_a];
|
|
||||||
int channel = (MAC[5+len_a] << 8) | MAC[6+len_a];
|
|
||||||
int sgroup = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
|
||||||
long int freq = 0;
|
|
||||||
fprintf (stderr, "\n MFID90 Group Regroup Channel Grant - Implicit");
|
|
||||||
fprintf (stderr, "\n CHAN [%04X] Group [%d][%04X]", channel, sgroup, sgroup);
|
|
||||||
freq = process_channel_to_freq (opts, state, channel);
|
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == sgroup)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 && 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//rigctl
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, freq);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//if playing back files, and we still want to see what freqs are in use in the ncurses terminal
|
|
||||||
//might only want to do these on a grant update, and not a grant by itself?
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MFID90 Group Regroup Channel Grant - Explicit
|
|
||||||
if (MAC[1+len_a] == 0xA4 && MAC[2+len_a] == 0x90)
|
|
||||||
{
|
|
||||||
int mfid = MAC[2+len_a];
|
|
||||||
int channel = (MAC[5+len_a] << 8) | MAC[6+len_a];
|
|
||||||
int channelr = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
|
||||||
int sgroup = (MAC[9+len_a] << 8) | MAC[10+len_a];
|
|
||||||
long int freq = 0;
|
|
||||||
fprintf (stderr, "\n MFID90 Group Regroup Channel Grant - Explicit");
|
|
||||||
fprintf (stderr, "\n CHAN [%04X] Group [%d][%04X]", channel, sgroup, sgroup);
|
|
||||||
freq = process_channel_to_freq (opts, state, channel);
|
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == sgroup)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 && 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, freq);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//if playing back files, and we still want to see what freqs are in use in the ncurses terminal
|
|
||||||
//might only want to do these on a grant update, and not a grant by itself?
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
if (sgroup == state->lasttg || sgroup == state->lasttgR)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MFID90 Group Regroup Channel Grant Update
|
|
||||||
if (MAC[1+len_a] == 0xA5 && MAC[2+len_a] == 0x90)
|
|
||||||
{
|
|
||||||
int channel1 = (MAC[4+len_a] << 8) | MAC[5+len_a];
|
|
||||||
int group1 = (MAC[6+len_a] << 8) | MAC[7+len_a];
|
|
||||||
int channel2 = (MAC[8+len_a] << 8) | MAC[9+len_a];
|
|
||||||
int group2 = (MAC[10+len_a] << 8) | MAC[11+len_a];
|
|
||||||
long int freq1 = 0;
|
|
||||||
long int freq2 = 0;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n MFID90 Group Regroup Channel Grant Update");
|
|
||||||
fprintf (stderr, "\n Channel 1 [%04X] Group 1 [%d][%04X]", channel1, group1, group1);
|
|
||||||
freq1 = process_channel_to_freq (opts, state, channel1);
|
|
||||||
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
|
||||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
|
||||||
|
|
||||||
//monstrocity below should get us evaluating and tuning groups...hopefully, will be first come first served though, no priority
|
|
||||||
//see how many loops we need to run on when to tune if first group is blocked
|
|
||||||
int loop = 1;
|
|
||||||
if (channel1 == channel2) loop = 1;
|
|
||||||
else loop = 2;
|
|
||||||
//assigned inside loop
|
|
||||||
long int tunable_freq = 0;
|
|
||||||
int tunable_chan = 0;
|
|
||||||
int tunable_group = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < loop; j++)
|
|
||||||
{
|
|
||||||
//assign our internal variables for check down on if to tune one freq/group or not
|
|
||||||
if (j == 0)
|
|
||||||
{
|
|
||||||
tunable_freq = freq1;
|
|
||||||
tunable_chan = channel1;
|
|
||||||
tunable_group = group1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tunable_freq = freq2;
|
|
||||||
tunable_chan = channel2;
|
|
||||||
tunable_group = group2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == tunable_group)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//check to see if the group candidate is blocked first
|
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0)) //DE is digital encrypted, B is block
|
|
||||||
{
|
|
||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
|
||||||
{
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//rigctl
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
|
||||||
//probably best to only set these when really tuning
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
|
||||||
j = 8; //break loop
|
|
||||||
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, tunable_freq);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
j = 8; //break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//if playing back files, and we still want to see what freqs are in use in the ncurses terminal
|
|
||||||
//might only want to do these on a grant update, and not a grant by itself?
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
if (tunable_group == state->lasttg || tunable_group == state->lasttgR)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = tunable_freq;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Standard P25 Tunable Commands
|
|
||||||
//Group Voice Channel Grant (GRP_V_CH_GRANT)
|
|
||||||
if (MAC[1+len_a] == 0x40)
|
|
||||||
{
|
|
||||||
int svc = MAC[2+len_a];
|
|
||||||
int channel = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
|
||||||
int group = (MAC[5+len_a] << 8) | MAC[6+len_a];
|
|
||||||
int source = (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | MAC[9+len_a];
|
|
||||||
long int freq = 0;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update");
|
|
||||||
fprintf (stderr, "\n SVC [%02X] CHAN [%04X] Group [%d] Source [%d]", svc, channel, group, source);
|
|
||||||
freq = process_channel_to_freq (opts, state, channel);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 && 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//rigctl
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, freq);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//if playing back files, and we still want to see what freqs are in use in the ncurses terminal
|
|
||||||
//might only want to do these on a grant update, and not a grant by itself?
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
if (group == state->lasttg || group == state->lasttgR)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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)
|
|
||||||
{
|
|
||||||
int channel = (MAC[2+len_a] << 8) | MAC[3+len_a];
|
|
||||||
int target = (MAC[4+len_a] << 16) | (MAC[5+len_a] << 8) | MAC[6+len_a];
|
|
||||||
int source = (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | MAC[9+len_a];
|
|
||||||
long int freq = 0;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n Unit to Unit Channel Grant");
|
|
||||||
if ( MAC[1+len_a] == 0x46) fprintf (stderr, " Update");
|
|
||||||
fprintf (stderr, "\n CHAN [%04X] Source [%d] Target [%d]", channel, source, target);
|
|
||||||
freq = process_channel_to_freq (opts, state, channel);
|
|
||||||
|
|
||||||
//Skip tuning private calls if private calls is disabled
|
|
||||||
if (opts->trunk_tune_private_calls == 0) goto SKIPCALL;
|
|
||||||
|
|
||||||
//unit to unit needs work, may fail under certain conditions (first blocked, second allowed, etc) (labels should still work though)
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == source || state->group_array[i].groupNumber == target)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 && 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//rigctl
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
SetFreq(opts->rigctl_sockfd, freq);
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, freq);
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
if (target == state->lasttg || target == state->lasttgR)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SKIPCALL: ; //do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
//Group Voice Channel Grant Update Multiple - Explicit
|
|
||||||
if (MAC[1+len_a] == 0x25)
|
|
||||||
{
|
|
||||||
int svc1 = MAC[2+len_a];
|
|
||||||
int channelt1 = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
|
||||||
int channelr1 = (MAC[5+len_a] << 8) | MAC[6+len_a];
|
|
||||||
int group1 = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
|
||||||
int svc2 = MAC[9+len_a];
|
|
||||||
int channelt2 = (MAC[10+len_a] << 8) | MAC[11+len_a];
|
|
||||||
int channelr2 = (MAC[12+len_a] << 8) | MAC[13+len_a];
|
|
||||||
int group2 = (MAC[14+len_a] << 8) | MAC[15+len_a];
|
|
||||||
long int freq1t = 0;
|
|
||||||
long int freq1r = 0;
|
|
||||||
long int freq2t = 0;
|
|
||||||
long int freq2r = 0;
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update Multiple - Explicit");
|
|
||||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc1, channelt1, channelr1, group1, group1);
|
|
||||||
freq1t = process_channel_to_freq (opts, state, channelt1);
|
|
||||||
freq1r = process_channel_to_freq (opts, state, channelr1);
|
|
||||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc2, channelt2, channelr2, group2, group2);
|
|
||||||
freq1t = process_channel_to_freq (opts, state, channelt2);
|
|
||||||
freq1r = process_channel_to_freq (opts, state, channelr2);
|
|
||||||
|
|
||||||
int loop = 1;
|
|
||||||
if (channelt2 == channelt2) loop = 1;
|
|
||||||
else loop = 2;
|
|
||||||
//assigned inside loop
|
|
||||||
long int tunable_freq = 0;
|
|
||||||
int tunable_chan = 0;
|
|
||||||
int tunable_group = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < loop; j++)
|
|
||||||
{
|
|
||||||
//assign our internal variables for check down on if to tune one freq/group or not
|
|
||||||
if (j == 0)
|
|
||||||
{
|
|
||||||
tunable_freq = freq1t;
|
|
||||||
tunable_chan = channelt1;
|
|
||||||
tunable_group = group1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tunable_freq = freq2t;
|
|
||||||
tunable_chan = channelt2;
|
|
||||||
tunable_group = group2;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == tunable_group)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 && 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
|
||||||
j = 8; //break loop
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, tunable_freq);
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
j = 8; //break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
if (tunable_group == state->lasttg || tunable_group == state->lasttgR)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = tunable_freq;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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
|
|
||||||
{
|
|
||||||
int so1 = MAC[2+len_a];
|
|
||||||
int channel1 = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
|
||||||
int group1 = (MAC[5+len_a] << 8) | MAC[6+len_a];
|
|
||||||
int so2 = MAC[7+len_a];
|
|
||||||
int channel2 = (MAC[8+len_a] << 8) | MAC[9+len_a];
|
|
||||||
int group2 = (MAC[10+len_a] << 8) | MAC[11+len_a];
|
|
||||||
int so3 = MAC[12+len_a];
|
|
||||||
int channel3 = (MAC[13+len_a] << 8) | MAC[14+len_a];
|
|
||||||
int group3 = (MAC[15+len_a] << 8) | MAC[16+len_a];
|
|
||||||
long int freq1 = 0;
|
|
||||||
long int freq2 = 0;
|
|
||||||
long int freq3 = 0;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update Multiple - Implicit");
|
|
||||||
fprintf (stderr, "\n Channel 1 [%04X] Group 1 [%d][%04X]", channel1, group1, group1);
|
|
||||||
freq1 = process_channel_to_freq (opts, state, channel1);
|
|
||||||
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
|
||||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
|
||||||
fprintf (stderr, "\n Channel 3 [%04X] Group 3 [%d][%04X]", channel3, group3, group3);
|
|
||||||
freq3 = process_channel_to_freq (opts, state, channel3);
|
|
||||||
|
|
||||||
int loop = 3;
|
|
||||||
|
|
||||||
long int tunable_freq = 0;
|
|
||||||
int tunable_chan = 0;
|
|
||||||
int tunable_group = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < loop; j++)
|
|
||||||
{
|
|
||||||
//assign our internal variables for check down on if to tune one freq/group or not
|
|
||||||
if (j == 0)
|
|
||||||
{
|
|
||||||
tunable_freq = freq1;
|
|
||||||
tunable_chan = channel1;
|
|
||||||
tunable_group = group1;
|
|
||||||
}
|
|
||||||
else if (j == 1)
|
|
||||||
{
|
|
||||||
tunable_freq = freq2;
|
|
||||||
tunable_chan = channel2;
|
|
||||||
tunable_group = group2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tunable_freq = freq3;
|
|
||||||
tunable_chan = channel3;
|
|
||||||
tunable_group = group3;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == tunable_group)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//check to see if the group candidate is blocked first
|
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0)) //DE is digital encrypted, B is block
|
|
||||||
{
|
|
||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
|
||||||
{
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//rigctl
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
|
||||||
//probably best to only set these when really tuning
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
|
||||||
j = 8; //break loop
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, tunable_freq);
|
|
||||||
//probably best to only set these when really tuning
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
j = 8; //break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
if (tunable_group == state->lasttg || tunable_group == state->lasttgR)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = tunable_freq;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Group Voice Channel Grant Update - Implicit
|
|
||||||
if (MAC[1+len_a] == 0x42)
|
|
||||||
{
|
|
||||||
int channel1 = (MAC[2+len_a] << 8) | MAC[3+len_a];
|
|
||||||
int group1 = (MAC[4+len_a] << 8) | MAC[5+len_a];
|
|
||||||
int channel2 = (MAC[6+len_a] << 8) | MAC[7+len_a];
|
|
||||||
int group2 = (MAC[8+len_a] << 8) | MAC[9+len_a];
|
|
||||||
long int freq1 = 0;
|
|
||||||
long int freq2 = 0;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update - Implicit");
|
|
||||||
fprintf (stderr, "\n Channel 1 [%04X] Group 1 [%d][%04X]", channel1, group1, group1);
|
|
||||||
freq1 = process_channel_to_freq (opts, state, channel1);
|
|
||||||
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
|
||||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
|
||||||
|
|
||||||
int loop = 1;
|
|
||||||
if (channel1 == channel2) loop = 1;
|
|
||||||
else loop = 2;
|
|
||||||
//assigned inside loop
|
|
||||||
long int tunable_freq = 0;
|
|
||||||
int tunable_chan = 0;
|
|
||||||
int tunable_group = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < loop; j++)
|
|
||||||
{
|
|
||||||
//assign our internal variables for check down on if to tune one freq/group or not
|
|
||||||
if (j == 0)
|
|
||||||
{
|
|
||||||
tunable_freq = freq1;
|
|
||||||
tunable_chan = channel1;
|
|
||||||
tunable_group = group1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tunable_freq = freq2;
|
|
||||||
tunable_chan = channel2;
|
|
||||||
tunable_group = group2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
|
||||||
{
|
|
||||||
if (state->group_array[i].groupNumber == tunable_group)
|
|
||||||
{
|
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//check to see if the group candidate is blocked first
|
|
||||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0)) //DE is digital encrypted, B is block
|
|
||||||
{
|
|
||||||
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
|
|
||||||
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && tunable_freq != 0) //if we aren't already on a VC and have a valid frequency already
|
|
||||||
{
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//rigctl
|
|
||||||
if (opts->use_rigctl == 1)
|
|
||||||
{
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
|
||||||
SetFreq(opts->rigctl_sockfd, tunable_freq);
|
|
||||||
//probably best to only set these when really tuning
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
|
||||||
j = 8; //break loop
|
|
||||||
}
|
|
||||||
//rtl_udp
|
|
||||||
else if (opts->audio_in_type == 3)
|
|
||||||
{
|
|
||||||
rtl_udp_tune (opts, state, tunable_freq);
|
|
||||||
//probably best to only set these when really tuning
|
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
opts->p25_is_tuned = 1;
|
|
||||||
j = 8; //break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
if (tunable_group == state->lasttg || tunable_group == state->lasttgR)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = tunable_freq;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = tunable_freq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Group Voice Channel Grant Update - Explicit
|
|
||||||
if (MAC[1+len_a] == 0xC3)
|
|
||||||
{
|
|
||||||
int svc = MAC[2+len_a];
|
|
||||||
int channelt = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
|
||||||
int channelr = (MAC[5+len_a] << 8) | MAC[6+len_a];
|
|
||||||
int group = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
|
||||||
long int freq1 = 0;
|
|
||||||
long int freq2 = 0;
|
|
||||||
|
|
||||||
fprintf (stderr, "\n Group Voice Channel Grant Update - Explicit");
|
|
||||||
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc, channelt, channelr, group, group);
|
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->p25_trunk == 0)
|
|
||||||
{
|
|
||||||
if (group == state->lasttg || group == state->lasttgR)
|
|
||||||
{
|
|
||||||
//P1 FDMA
|
|
||||||
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq1;
|
|
||||||
//P2 TDMA
|
|
||||||
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//SNDCP Data Channel Announcement
|
//SNDCP Data Channel Announcement
|
||||||
if (MAC[1+len_a] == 0xD6)
|
if (MAC[1+len_a] == 0xD6)
|
||||||
@@ -910,6 +139,35 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
process_channel_to_freq (opts, state, channelr);
|
process_channel_to_freq (opts, state, channelr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//End New PDUs added
|
||||||
|
|
||||||
|
//Group Voice Channel Grant (GRP_V_CH_GRANT)
|
||||||
|
if (MAC[1+len_a] == 0x40)
|
||||||
|
{
|
||||||
|
int svc = MAC[2+len_a];
|
||||||
|
int channel = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
||||||
|
int group = (MAC[5+len_a] << 8) | MAC[6+len_a];
|
||||||
|
int source = (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | MAC[9+len_a];
|
||||||
|
|
||||||
|
fprintf (stderr, "\n Group Voice Channel Grant Update");
|
||||||
|
fprintf (stderr, "\n SVC [%02X] CHAN [%04X] Group [%d] Source [%d]", svc, channel, group, source);
|
||||||
|
process_channel_to_freq (opts, state, channel);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unit-to-Unit Voice Service Channel Grant (UU_V_CH_GRANT)
|
||||||
|
if (MAC[1+len_a] == 0x44)
|
||||||
|
{
|
||||||
|
int channel = (MAC[2+len_a] << 8) | MAC[3+len_a];
|
||||||
|
int target = (MAC[4+len_a] << 16) | (MAC[5+len_a] << 8) | MAC[6+len_a];
|
||||||
|
int source = (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | MAC[9+len_a];
|
||||||
|
|
||||||
|
fprintf (stderr, "\n Unit to Unit Channel Grant");
|
||||||
|
fprintf (stderr, "\n CHAN [%04X] Source [%d] Target [%d]", channel, source, target);
|
||||||
|
process_channel_to_freq (opts, state, channel);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//Unit-to-Unit Answer Request (UU_ANS_REQ)
|
//Unit-to-Unit Answer Request (UU_ANS_REQ)
|
||||||
if (MAC[1+len_a] == 0x45)
|
if (MAC[1+len_a] == 0x45)
|
||||||
{
|
{
|
||||||
@@ -1067,25 +325,75 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
int sysclass1 = MAC[6+len_a];
|
int sysclass1 = MAC[6+len_a];
|
||||||
int channel2 = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
int channel2 = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
||||||
int sysclass2 = MAC[9+len_a];
|
int sysclass2 = MAC[9+len_a];
|
||||||
long int freq1 = 0;
|
|
||||||
long int freq2 = 0;
|
|
||||||
if (1 == 1) //state->p2_is_lcch == 1
|
if (1 == 1) //state->p2_is_lcch == 1
|
||||||
{
|
{
|
||||||
|
|
||||||
fprintf (stderr, "\n Secondary Control Channel Broadcast - Implicit\n");
|
fprintf (stderr, "\n Secondary Control Channel Broadcast - Implicit\n");
|
||||||
fprintf (stderr, " RFSS[%03d] SITE ID [%03X] CHAN1 [%04X] SSC [%02X] CHAN2 [%04X] SSC [%02X]", rfssid, siteid, channel1, sysclass1, channel2, sysclass2);
|
fprintf (stderr, " RFSS[%03d] SITE ID [%03X] CHAN1 [%04X] SSC [%02X] CHAN2 [%04X] SSC [%02X]", rfssid, siteid, channel1, sysclass1, channel2, sysclass2);
|
||||||
|
|
||||||
freq1 = process_channel_to_freq (opts, state, channel1);
|
process_channel_to_freq (opts, state, channel1);
|
||||||
freq2 = process_channel_to_freq (opts, state, channel2);
|
process_channel_to_freq (opts, state, channel2);
|
||||||
}
|
}
|
||||||
|
|
||||||
//place the cc freq into the list at index 0 if 0 is empty so we can hunt for rotating CCs without user LCN list
|
}
|
||||||
if (state->trunk_lcn_freq[1] == 0)
|
|
||||||
{
|
//Group Voice Channel Grant Update - Implicit
|
||||||
state->trunk_lcn_freq[1] = freq1;
|
if (MAC[1+len_a] == 0x42)
|
||||||
state->trunk_lcn_freq[2] = freq2;
|
{
|
||||||
state->lcn_freq_count = 3; //increment to three
|
int channel1 = (MAC[2+len_a] << 8) | MAC[3+len_a];
|
||||||
}
|
int group1 = (MAC[4+len_a] << 8) | MAC[5+len_a];
|
||||||
|
int channel2 = (MAC[6+len_a] << 8) | MAC[7+len_a];
|
||||||
|
int group2 = (MAC[8+len_a] << 8) | MAC[9+len_a];
|
||||||
|
|
||||||
|
fprintf (stderr, "\n Group Voice Channel Grant Update - Implicit");
|
||||||
|
fprintf (stderr, "\n Channel 1 [%04X] Group 1 [%d][%04X]", channel1, group1, group1);
|
||||||
|
|
||||||
|
if (state->lasttg == group1)
|
||||||
|
{
|
||||||
|
state->p25_vc_freq[0] = process_channel_to_freq (opts, state, channel1);
|
||||||
|
}
|
||||||
|
else if (state->lasttgR == group1)
|
||||||
|
{
|
||||||
|
state->p25_vc_freq[1] = process_channel_to_freq (opts, state, channel1);
|
||||||
|
}
|
||||||
|
else process_channel_to_freq (opts, state, channel1);
|
||||||
|
//only run next channel if not identical to first channel
|
||||||
|
if (group1 != group2)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n Channel 2 [%04X] Group 2 [%d][%04X]", channel2, group2, group2);
|
||||||
|
if (state->lasttg == group2)
|
||||||
|
{
|
||||||
|
state->p25_vc_freq[0] = process_channel_to_freq (opts, state, channel2);
|
||||||
|
}
|
||||||
|
else if (state->lasttgR == group2)
|
||||||
|
{
|
||||||
|
state->p25_vc_freq[1] = process_channel_to_freq (opts, state, channel2);
|
||||||
|
}
|
||||||
|
else process_channel_to_freq (opts, state, channel2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Group Voice Channel Grant Update - Explicit
|
||||||
|
if (MAC[1+len_a] == 0xC3)
|
||||||
|
{
|
||||||
|
int svc = MAC[2+len_a];
|
||||||
|
int channelt = (MAC[3+len_a] << 8) | MAC[4+len_a];
|
||||||
|
int channelr = (MAC[5+len_a] << 8) | MAC[6+len_a];
|
||||||
|
int group = (MAC[7+len_a] << 8) | MAC[8+len_a];
|
||||||
|
|
||||||
|
fprintf (stderr, "\n Group Voice Channel Grant Update - Explicit");
|
||||||
|
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc, channelt, channelr, group, group);
|
||||||
|
process_channel_to_freq (opts, state, channelt);
|
||||||
|
process_channel_to_freq (opts, state, channelr);
|
||||||
|
if (slot == 0)
|
||||||
|
{
|
||||||
|
state->lasttg = group;
|
||||||
|
}
|
||||||
|
else state->lasttgR = group;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1237,7 +545,6 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, "\n Network Status Broadcast - Abbreviated \n");
|
fprintf (stderr, "\n Network Status Broadcast - Abbreviated \n");
|
||||||
fprintf (stderr, " LRA [%02X] WACN [%05X] SYSID [%03X] NAC [%03X] CHAN-T [%04X]", lra, lwacn, lsysid, lcolorcode, channel);
|
fprintf (stderr, " LRA [%02X] WACN [%05X] SYSID [%03X] NAC [%03X] CHAN-T [%04X]", lra, lwacn, lsysid, lcolorcode, channel);
|
||||||
state->p25_cc_freq = process_channel_to_freq (opts, state, channel);
|
state->p25_cc_freq = process_channel_to_freq (opts, state, channel);
|
||||||
state->p25_cc_is_tdma = 1; //flag on for CC tuning purposes when system is qpsk
|
|
||||||
if (state->p2_hardset == 0 ) //state->p2_is_lcch == 1 shim until CRC is working, prevent bogus data
|
if (state->p2_hardset == 0 ) //state->p2_is_lcch == 1 shim until CRC is working, prevent bogus data
|
||||||
{
|
{
|
||||||
state->p2_wacn = lwacn;
|
state->p2_wacn = lwacn;
|
||||||
@@ -1245,13 +552,6 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
state->p2_cc = lcolorcode;
|
state->p2_cc = lcolorcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//network status broadcast, extended
|
//network status broadcast, extended
|
||||||
if (MAC[1+len_a] == 0xFB)
|
if (MAC[1+len_a] == 0xFB)
|
||||||
@@ -1267,7 +567,6 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
fprintf (stderr, " LRA [%02X] WACN [%05X] SYSID [%03X] NAC [%03X] CHAN-T [%04X] CHAN-R [%04X]", lra, lwacn, lsysid, lcolorcode, channelt, channelr);
|
fprintf (stderr, " LRA [%02X] WACN [%05X] SYSID [%03X] NAC [%03X] CHAN-T [%04X] CHAN-R [%04X]", lra, lwacn, lsysid, lcolorcode, channelt, channelr);
|
||||||
process_channel_to_freq (opts, state, channelt);
|
process_channel_to_freq (opts, state, channelt);
|
||||||
process_channel_to_freq (opts, state, channelr);
|
process_channel_to_freq (opts, state, channelr);
|
||||||
state->p25_cc_is_tdma = 1; //flag on for CC tuning purposes when system is qpsk
|
|
||||||
if (state->p2_hardset == 0 ) //state->p2_is_lcch == 1 shim until CRC is working, prevent bogus data
|
if (state->p2_hardset == 0 ) //state->p2_is_lcch == 1 shim until CRC is working, prevent bogus data
|
||||||
{
|
{
|
||||||
state->p2_wacn = lwacn;
|
state->p2_wacn = lwacn;
|
||||||
@@ -1289,7 +588,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
if (1 == 1) //state->p2_is_lcch == 1
|
if (1 == 1) //state->p2_is_lcch == 1
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n Adjacent Status Broadcast - Abbreviated\n");
|
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);
|
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] CHAN-T [%04X] SSC [%02X]", lra, rfssid, lsysid, channelt, sysclass);
|
||||||
process_channel_to_freq (opts, state, channelt);
|
process_channel_to_freq (opts, state, channelt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1308,7 +607,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
|||||||
if (1 == 1) //state->p2_is_lcch == 1
|
if (1 == 1) //state->p2_is_lcch == 1
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n Adjacent Status Broadcast - Extended\n");
|
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);
|
fprintf (stderr, " LRA [%02X] RFSS[%03d] SYSID [%03X] CHAN-T [%04X] CHAN-R [%04X] SSC [%02X]", lra, rfssid, lsysid, channelt, channelr, sysclass);
|
||||||
process_channel_to_freq (opts, state, channelt);
|
process_channel_to_freq (opts, state, channelt);
|
||||||
process_channel_to_freq (opts, state, channelr);
|
process_channel_to_freq (opts, state, channelr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " CRC12 ERR S");
|
fprintf (stderr, " CRC12 ERR S");
|
||||||
//if (state->currentslot == 0) state->dmrburstL = 14;
|
if (state->currentslot == 0) state->dmrburstL = 14;
|
||||||
//else state->dmrburstR = 14;
|
else state->dmrburstR = 14;
|
||||||
goto END_SMAC;
|
goto END_SMAC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,8 +84,8 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
{
|
{
|
||||||
fprintf (stderr, " CRC16 ERR L");
|
fprintf (stderr, " CRC16 ERR L");
|
||||||
state->p2_is_lcch = 0; //turn flag off here
|
state->p2_is_lcch = 0; //turn flag off here
|
||||||
//if (state->currentslot == 0) state->dmrburstL = 14;
|
if (state->currentslot == 0) state->dmrburstL = 14;
|
||||||
//else state->dmrburstR = 14;
|
else state->dmrburstR = 14;
|
||||||
goto END_SMAC;
|
goto END_SMAC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf (stderr, "CRC16 ERR ");
|
fprintf (stderr, "CRC16 ERR ");
|
||||||
}
|
}
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KMAG);
|
||||||
process_MAC_VPDU(opts, state, 1, SMAC);
|
process_MAC_VPDU(opts, state, 1, SMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
if (state->currentslot == 1) state->dmrburstL = 24;
|
if (state->currentslot == 1) state->dmrburstL = 24;
|
||||||
else state->dmrburstR = 24;
|
else state->dmrburstR = 24;
|
||||||
fprintf (stderr, " MAC_IDLE ");
|
fprintf (stderr, " MAC_IDLE ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KMAG);
|
||||||
process_MAC_VPDU(opts, state, 1, SMAC);
|
process_MAC_VPDU(opts, state, 1, SMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
if (state->currentslot == 1) state->dmrburstL = 21;
|
if (state->currentslot == 1) state->dmrburstL = 21;
|
||||||
else state->dmrburstR = 21;
|
else state->dmrburstR = 21;
|
||||||
fprintf (stderr, " MAC_ACTIVE ");
|
fprintf (stderr, " MAC_ACTIVE ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KMAG);
|
||||||
process_MAC_VPDU(opts, state, 1, SMAC);
|
process_MAC_VPDU(opts, state, 1, SMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -244,7 +244,7 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
|
|||||||
if (state->currentslot == 1) state->dmrburstL = 22;
|
if (state->currentslot == 1) state->dmrburstL = 22;
|
||||||
else state->dmrburstR = 22;
|
else state->dmrburstR = 22;
|
||||||
fprintf (stderr, " MAC_HANGTIME ");
|
fprintf (stderr, " MAC_HANGTIME ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KMAG);
|
||||||
process_MAC_VPDU(opts, state, 1, SMAC);
|
process_MAC_VPDU(opts, state, 1, SMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -303,8 +303,8 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf (stderr, " CRC12 ERR F");
|
fprintf (stderr, " CRC12 ERR F");
|
||||||
//if (state->currentslot == 0) state->dmrburstL = 14;
|
if (state->currentslot == 0) state->dmrburstL = 14;
|
||||||
//else state->dmrburstR = 14;
|
else state->dmrburstR = 14;
|
||||||
goto END_FMAC;
|
goto END_FMAC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
// if (opcode == 0x0)
|
// if (opcode == 0x0)
|
||||||
// {
|
// {
|
||||||
// fprintf (stderr, " MAC_SIGNAL ");
|
// fprintf (stderr, " MAC_SIGNAL ");
|
||||||
// fprintf (stderr, "%s", KYEL);
|
// fprintf (stderr, "%s", KMAG);
|
||||||
// process_MAC_VPDU(opts, state, 0, FMAC);
|
// process_MAC_VPDU(opts, state, 0, FMAC);
|
||||||
// fprintf (stderr, "%s", KNRM);
|
// fprintf (stderr, "%s", KNRM);
|
||||||
// }
|
// }
|
||||||
@@ -456,7 +456,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
|
|
||||||
}
|
}
|
||||||
fprintf (stderr, " MAC_IDLE ");
|
fprintf (stderr, " MAC_IDLE ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KMAG);
|
||||||
process_MAC_VPDU(opts, state, 0, FMAC);
|
process_MAC_VPDU(opts, state, 0, FMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -465,7 +465,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
if (state->currentslot == 0) state->dmrburstL = 21;
|
if (state->currentslot == 0) state->dmrburstL = 21;
|
||||||
else state->dmrburstR = 21;
|
else state->dmrburstR = 21;
|
||||||
fprintf (stderr, " MAC_ACTIVE ");
|
fprintf (stderr, " MAC_ACTIVE ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KMAG);
|
||||||
process_MAC_VPDU(opts, state, 0, FMAC);
|
process_MAC_VPDU(opts, state, 0, FMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
@@ -474,7 +474,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
if (state->currentslot == 0) state->dmrburstL = 22;
|
if (state->currentslot == 0) state->dmrburstL = 22;
|
||||||
else state->dmrburstR = 22;
|
else state->dmrburstR = 22;
|
||||||
fprintf (stderr, " MAC_HANGTIME ");
|
fprintf (stderr, " MAC_HANGTIME ");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KMAG);
|
||||||
process_MAC_VPDU(opts, state, 0, FMAC);
|
process_MAC_VPDU(opts, state, 0, FMAC);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,10 @@ processProVoice (dsd_opts * opts, dsd_state * state)
|
|||||||
char imbe7100_fr2[7][24];
|
char imbe7100_fr2[7][24];
|
||||||
const int *w, *x;
|
const int *w, *x;
|
||||||
|
|
||||||
fprintf (stderr," VOICE");
|
if (opts->errorbars == 1)
|
||||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1)
|
{
|
||||||
{
|
fprintf (stderr,"VOICE ");
|
||||||
fprintf (stderr, "%s", KGRN);
|
}
|
||||||
fprintf (stderr, " Site [%02llX][%03lld] Group [%05d] Source [%08d] LCN[%02d] ",
|
|
||||||
state->edacs_site_id, state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
for (i = 0; i < 64; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -790,7 +790,7 @@ static void *controller_thread_fn(void *arg)
|
|||||||
/* Set the sample rate */
|
/* Set the sample rate */
|
||||||
verbose_set_sample_rate(dongle.dev, dongle.rate);
|
verbose_set_sample_rate(dongle.dev, dongle.rate);
|
||||||
fprintf (stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample);
|
fprintf (stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample);
|
||||||
fprintf (stderr, "Press CTRL + C to close.\n");
|
fprintf (stderr, "Press CTRL + C to close.\n"); //Kindly remind user to double tap CTRL + C
|
||||||
|
|
||||||
while (!exitflag) {
|
while (!exitflag) {
|
||||||
safe_cond_wait(&s->hop, &s->hop_m);
|
safe_cond_wait(&s->hop, &s->hop_m);
|
||||||
@@ -959,7 +959,7 @@ static void *socket_thread_fn(void *arg) {
|
|||||||
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
||||||
//verbose_set_frequency (dongle.dev, new_freq);
|
//verbose_set_frequency (dongle.dev, new_freq);
|
||||||
//fprintf (stderr, "Tuning to: %d [Hz] (central freq: %d [Hz])\n", new_freq, new_freq + freq_offset);
|
//fprintf (stderr, "Tuning to: %d [Hz] (central freq: %d [Hz])\n", new_freq, new_freq + freq_offset);
|
||||||
fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq);
|
fprintf (stderr, "Tuning to: %d [Hz] \n", new_freq);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
if(buffer[0] == 1) {
|
if(buffer[0] == 1) {
|
||||||
|
|||||||
BIN
tone48.wav
BIN
tone48.wav
Binary file not shown.
Reference in New Issue
Block a user