69 Commits

Author SHA1 Message Date
lwvmobile
69d31151dd MBE: Add NXDN AES Decryption support (untested) for SDRTrunk .mbe files; #298 2026-07-10 16:39:16 -04:00
lwvmobile
6fd7176659 MBE: Fixes and Tweaks on SDRTrunk .mbe files for NXDN Scrambler Key Loading; #298 2026-06-29 16:18:31 -04:00
lwvmobile
c960f9932c DMR: Fix Response Packet Nack Response Reasons; #354 2026-06-25 13:36:25 -04:00
lwvmobile
e877e7b9de WAV: Fix Typo on per-call custom tags and print to console log; #352 2026-06-17 15:53:17 -04:00
lwvmobile
43d63b20dc WAV: Add custom tag option for per-call wav files; #352 2026-06-13 16:08:29 -04:00
lwvmobile
e6d2dd7934 P25: Create a Tait LCW Format Decode; 2026-06-11 18:23:56 -04:00
lwvmobile
8c3ae61b1a MBE: Support for SDRTrunk NXDN MBE files (WIP); #298
DMR: Clarify and Decode FSN Value on Data Headers; #350

NXDN: Fix errors on KS creation on AES discard bytes on voice application;

GCC: Fix Misc unused-but-set-variable warnings (GCC Version GNU 16.1.1);
2026-06-11 17:52:12 -04:00
lwvmobile
f443063d3b Import Fixes: #348
Make adjustments to groupinfo struct; Misc tweaks to code using groupinfo items;

Move group_array from stack to heap; Calloc adequate memory for it;

Switch to strncpy to fix potential issues with large strings copying OOB and crashing;
2026-05-20 05:25:38 -04:00
lwvmobile
ed1d1d630c NXDN: Add Event History entries for the GPS report, undecoded, and still encrypted Data Calls;
P25p1: Add / Fix Several TSBKs including Station ID and Extended Function Command; Add UDP Port Decode for EFJ Atlas Data Registration Server;
2026-03-28 11:54:16 -04:00
lwvmobile
56f6f6d78e NXDN: Tweak to Frame Sync, restore other sync patterns based on polarity; Fixes random frame skips on NXDN96 trunking;
NXDN: Initial GPS Report Commit;

NXDN: Don't assign GI, TG/RID in VCALL_ASSGN; Leads to duplicate entries in Event History, particularly when private call;

NMEA: Sentence Checker with start, end, checksum, and supplimentary string checks P25p1 Tier 1 Location Service and Also observed on old Type-D system;

LRRP: Fix LRRP.exe style reports to use date/time strings with colons and slashes;

NXDN: Tweak to DCALL to check for a selective retry when blk_num is not equal to pf_num and invalidate, we don't have the memory of the last PDU attempt (and may have successfully received it anyways, even if the radio did not);
2026-03-20 11:10:15 -04:00
lwvmobile
a6bdfa913f NXDN: DCALL Finish up data decryption methods;
NXDN: Fix Block Assembly on "idas" types, they do have the same blocking structure;

NXDN: Fix CRC32 and "total_bytes" when padding bytes are present;

NXDN: Add placeholder GPS function and reverse byte load for GPS location data found in certain DCALL messages;

DMR: Init AES keyloader arrays for PDU;

Misc cleanup and tweaks;
2026-03-13 23:43:43 -04:00
lwvmobile
ad77f266c4 NXDN: Add DCALL / SDCALL Header and Data Block Assembly; SDCALL_IV Function;
NXDN: Disable LICH "dividing" offbits check on sync detection, causes too many blips on still okay signal, particularly trunking;
2026-03-13 14:43:14 -04:00
lwvmobile
f72eb31b30 Merge pull request #342 from hardt0x1c/fix/dmr-payload-pointer-reset
fix: correct dmr_payload_p buffer pointer assignment #340
2026-03-12 13:14:25 -04:00
Constantin
441ba31d1b fix: correct dmr_payload_p buffer pointer assignment 2026-03-08 23:22:49 +03:00
lwvmobile
7d94abd76a DMR: Only Apply Hytera BP Key if defacto static silence frame is not present; 2026-03-01 10:28:05 -05:00
lwvmobile
7b89174bb1 P25: Fix MFIDA4 Location on PTT; 2026-03-01 08:36:58 -05:00
lwvmobile
f6f5f80361 DMR: Fix forced refresh items; #339
P25: Tweak Tait Talker Alias 7-bit to check for ASCII values >= 0x20;

RTL: Tweaks to RMS;
2026-02-27 14:44:40 -05:00
lwvmobile
b7cd501cc1 RTL: Add input gain adjustments during ncurses session using g/G and update example usage (credit: ki4hyf);
Group trunking shortcut key changed from g to T;
2026-02-19 21:07:44 -05:00
lwvmobile
9f03ad123d NXDN: New Frame Sync Fine Tuning;
Tweak lmix, lmax, qsort indexing values based on sync pattern length;
2026-02-13 14:55:00 -05:00
lwvmobile
d3303aea28 P25p1: 1/2 Trellis Viterbi Improvements;
P25p1 and DMR: 3/4 Trellis Viterbi Improvements;
2026-02-10 23:36:18 -05:00
lwvmobile
615f67536f NXDN: Add some debug polarity dumps; 2026-02-05 17:46:01 -05:00
lwvmobile
12012a9eee NXDN: Add Cmake option (-DNOS=ON) to switch back to old NXDN sync with pos and inv polarity;
There is bound to be at least one person who will complain, or listens to the harmonic and not the signal, or has a mixed bag of things and doesn't want to specify polarity;
2026-02-05 16:53:44 -05:00
lwvmobile
53a256bee8 NXDN: Rework code for the FACCH1 duplicate message check post NXDN Viterbi overhaul;
Use static array to hold facch1 message after recovery and compare when LICH type calls for 2 individual FACCH1 messages in an NXDN frame;

Zero out facch1_storage on SACCH or SCCH decode (happens first) and after potential comparison time in FACCH1 decode;
2026-02-03 02:57:02 -05:00
lwvmobile
d7ce5e4cae NXDN: Add conditions to invalidate Superframe SACCH sequences;
Invalidate entire SACCH superframe in storage if CRC error on individual SACCH or if next SR value (part of frame) is not the expected value;

Set initial nxdn_part_of_frame value to 3 - Ideal sequence is 1/1 NSF to 1/4 SF SACCH; also set to 3 on misc frame types (CAC, FACCH2, etc)
2026-02-03 01:43:12 -05:00
lwvmobile
5af38d3563 NXDN: Revert Dibit Collection;
NXDN: Minor Tweak to STD-T102 Message Types;
2026-02-01 20:58:41 -05:00
lwvmobile
034855069a NXDN: Fix NXDN_LICH_OFFBITS and LICH_ERR_THRESHOLD to work as expected post NXDN Viterbi overhaul; Revert back to 1 bit error on lich "dividing";
NXDN: Skip Dibits prior to exit on bad sync in nxdn_frame;

NXDN: Reimplement the CAC fail demodulator and dibit buffer soft reset;
2026-02-01 20:19:33 -05:00
lwvmobile
3ee5acef0a Minor Viterbi Tweaks;
Seperate Viterbi Code into new .c file;
2026-01-31 15:40:35 -05:00
lwvmobile
b5bd9a7238 Compiler Warning Fix (llx); 2026-01-30 18:14:42 -05:00
lwvmobile
56b491ed3e NXDN: Soft Decision Viterbi;
NXDN: Tweak to LICH Dibit "Dividing" Error Threshold (1 to 2);

NXDN: Change MessageType to 8-bit and Decoding to Include the F1 and F2 Flag; Make Tweaks for DCR/ARIB Messages to compensate;

NXDN: Frame Sync Tweaks to help reduce false sync patterns; Make Inverted NXDN user option; cmake enabled defines for Type-C, Type-D, and DCR decoding (all enabled by default);

NXDN: Decode DST_ID_INFO Message;

NXDN: Rework Code on Standard Caller Alias and add a 16bit alias variants for ASCII, SJIS, BIG5, or UTF16 alias decoding (configure via cmake, default is ASCII);

NXDN: Seperate PROP_FORM 0x3F from Alias via checkdown;

YSF: Soft Decision Viterbi;

YSF: Fix BUMBLEBEETUNA text string on partial decode;

P25p2: Fix Harris GPS MAC message to fixed len, or do not decode (observed bugfix);

P25p2: Add MFID90 Unknown 0x8F and 0xBF MAC Message;

DOCS: Update complete_usage_options; Remove M17 Encoding Section;
2026-01-30 17:52:07 -05:00
lwvmobile
a26a680fd6 Merge pull request #336 from wormuz/fix-mbe-playback-arg-order
Fix -r option argument order dependency
2026-01-30 08:51:07 -05:00
lwvmobile
4ec2387140 Merge pull request #335 from wormuz/fix-mbe-static-wav-write
Fix -w option not writing wav in MBE playback mode
2026-01-30 08:48:32 -05:00
Konstantin Cheremisov
385a9a1747 Fix -r option argument order dependency
Move state.optind assignment from inside case 'r' to after the
getopt loop completes. This ensures optind points to the first
positional argument regardless of option order.

Before: -r file.amb -w output.wav would fail with "could not open -w"
After: argument order no longer matters for -r with other options
2026-01-29 14:46:15 +02:00
Konstantin Cheremisov
2267f5d007 Fix -w option not writing wav in MBE playback mode
The -w option sets static_wav_file=1 and dmr_stereo_wav=0, but
playMbeFiles() only checked dmr_stereo_wav==1 condition, causing
wav files to be empty (only 44-byte header) when using -r with -w.

Added static_wav_file check to both IMBE and AMBE playback branches.
2026-01-29 14:32:57 +02:00
lwvmobile
99f422898a Update and Push Tag; 2026-01-06 21:18:13 -05:00
lwvmobile
4924bd249a Remove Remnants of ITPP; #331
Tweaks to Decrypted MBE Playback; #298 #329
2026-01-05 12:56:07 -05:00
lwvmobile
a5e000a14c Remove ITPP Dependencies; #331
Rework P25p1 NID code from KA1RBI's OP25 code;

Remove ITPP Cmake, build config scripts, docs, etc.
2026-01-04 20:31:30 -05:00
lwvmobile
53eaccc892 .mbe file playback DMRA Forced ALG, KEY, Late Entry IV for DMR; #298 #329
Working RC4;

WIP on Moto BP Forced;
2026-01-02 01:41:18 -05:00
lwvmobile
e95cd4dd3a Fix AES-OFB and Misc Issues on .mbe file playback; #329 WIP 2026-01-01 23:00:41 -05:00
lwvmobile
48b0c4025b Add defined(__CYGWIN__) Fix for #330 2025-12-31 18:29:36 -05:00
lwvmobile
2171df515d Merge pull request #330 from Coriou/fix/macos-oss-build
macOS: disable OSS backend cleanly and fix build/link errors
2025-12-31 17:47:50 -05:00
Benjamin Coriou
83d7460a0b macOS: disable OSS backend cleanly and fix build/link errors
macOS has no OSS (/dev/dsp) support, but dsd-fme currently compiles and links OSS code unconditionally, leading to build and link failures.

This PR:

* disables OSS backend cleanly on macOS

* provides a no-op openOSSOutput() stub to satisfy callers

* guards OSS-specific headers and ioctl usage

* preserves existing behavior on Linux/OSS platforms

This allows dsd-fme to build and run correctly on macOS using PulseAudio/CoreAudio paths.
2025-12-31 15:03:37 +01:00
lwvmobile
38ad11dbef Fix AES-OFB type on .mbe file playback; #329 2025-12-29 22:36:47 -05:00
lwvmobile
eddd6cb8b1 Add DES(56) and AES(128/256) OFB Decryption to P25 .mbe file playback; #329
This does not include DES-XL due to needing to know HDU or LDU2 and complications of SDRtrunk P1 ESS handling, and also unknown Phase 2 method for XL.

Nor TDEA (never observed on P25 despite in spec);
2025-12-29 22:25:23 -05:00
lwvmobile
abf28593b1 Fix some Cygwin64 Build Script Issues and Instructions; #324
Use sed to replace line in ITPP CmakeLists.txt file to update the cmake_minimum_required to an acceptible version for newer cmake;

Include SIMD Math Libraries libopenblas, liblapack-devel, libfftw3-devel for Cygwin64;

If perceived performance issues arise, may consider removing the Math Libraries above from Cygwin64 install script
2025-12-14 19:51:34 -05:00
lwvmobile
bc4aeff164 Misc Fixes; #324
Fix cyg_portable.sh for newest version of Cygwin64;

Fix p25_frequency process_channel_to_freq to allow a channel value of (0000);

Fix Event Format Nit and add EOT Delay on CC for EDACS/PV;
2025-12-12 21:51:37 -05:00
lwvmobile
632041a44a M17: Add Packet Identifier 0x07 for TLE; 2025-11-16 14:30:52 -05:00
lwvmobile
f1cd83e924 DCR: Add SB0 Lich Code 0x08 and decode CSM as Alias, if present; #323
NXDN: Fix Type-C SCCH CRC Check (Pass 2);

NXDN: Fix CRC12 Issues;
2025-11-10 20:52:35 -05:00
lwvmobile
50bf004427 DMR: Kirisun LFSR Fix; #319 2025-11-10 20:32:05 -05:00
lwvmobile
85a6372c2e M17: Put ECD Back In; #322
Don't decode signature frames as voice;

Check 1600 Arbitrary Data to see if it has text in it before attempting to decode it;
2025-11-08 12:48:25 -05:00
lwvmobile
eb76ea2039 DMR: Add -M option to allow forcing any algid value;
Remove Setting from DMR_LE and place in MBE section instead;

Change state->M to state->forced_alg_id to make it easier to find later;
2025-10-31 09:41:10 -04:00
lwvmobile
14edf325bc Tweak zeroes_threshold AMBE KS application; 2025-10-31 08:24:53 -04:00
lwvmobile
e11ed353b8 Change custom NXDN PN95 -* argument to -_ ; #318
Using -* exhibits unusual behavior and may read contents of directory and set any value based on what is in there;

Don't use for future CLI arguments;

Update Usage;
2025-10-31 08:13:03 -04:00
lwvmobile
f8521fda99 Don't Apply Keystream when Silence or Zero DMR AMBE Frames; #319
This is primarily a fix for those CCR Radios that use the static silence frame and doesn't encrypt it either at the start of, or end of TX;

The Zeroes Fix (000D2C00000000) is for Anytone or TXI who has a load of zeroes starting, or ending a TX, or during the TXI window on certain VC-F frames;
2025-10-31 08:06:39 -04:00
lwvmobile
fa47d0321b Update and Strip Down M17 Features; #322 2025-10-31 05:58:45 -04:00
lwvmobile
095d982b62 Kirisun Advanced and Universal Encryption Support; #319
Above Commit is by Anonymous Submission;

Add Example Usage of -S for Baofeng DM32 Custom / Basic Encryption Mode;
2025-10-30 02:32:30 -04:00
lwvmobile
0060378321 DMR: Connect Systems Extended Encryption Support; #321 2025-10-16 12:28:16 -04:00
lwvmobile
8ea6415424 DMR: Baofeng Advanced (PC5) Encryption Support; #320
Above Commit is by Anonymous Submission;
2025-10-15 17:57:15 -04:00
lwvmobile
eb24239c51 DMR: LFSR Function for Kirisun Encryption Methods; #319
Add LFSR to DMR Alg Refresh;

Full Link Control and SBLE Tweaks Regarding Kirisun Enc;
2025-10-11 21:02:59 -04:00
lwvmobile
ac1c658870 DMR: Initial Support for Kirisun "Advanced" and "Universal" Encryption Identifiers and Link Control; #319 2025-10-10 05:53:36 -04:00
lwvmobile
cf032ab955 NXDN: Add SHIFT-JIS support and UTF-8 Conversion and decode Talker Alias for MT 0x27 ALIAS_ARIB; #318
NXDN: Redo NXDN VCALL_ARIB based on observed values and ARIB STD-B54;

Add UTF16 to UTF8 encoding for DMR Talker Alias when its UTF16;

Encoding Unicode / UTF16 to UTF8 Allows for display into Ncurses Terminal, Event History, etc when its CJK and not normal ISO7/ ASCII or UTF8 encoded as UTF16;

Might be beneficial to convert and run all encoded text through UTF8 encode in the future.
2025-10-08 19:54:35 -04:00
lwvmobile
2d04878506 NXDN: Add Placeholders with Partial Decode or Assembly for MT 0x21, 0x27, and 0x28; #318 2025-10-08 05:43:38 -04:00
lwvmobile
748bb5b191 Rewrite NXDN Dibit PN95 Code and allow custom seed value; #318 2025-10-07 06:13:19 -04:00
lwvmobile
f3b89f1c51 DMR: Initial Support for Vertex Standard Encrypted PDU Header; #316 2025-09-21 14:09:52 -04:00
lwvmobile
b2d388a210 DMR: Initial Support for Vertex Standard Text PDU on Port 5007; #316 2025-09-21 00:29:04 -04:00
lwvmobile
c883e2bf0b DMR: Tytera AP (PC4) Encryption Support Update to Allow 128-bit or 256-bit keys; 2025-09-20 23:49:33 -04:00
lwvmobile
f8ca0a8e08 DMR: Retevis AP (RC2) Encryption Support Update to Allow 128-bit or 256-bit keys; 2025-09-19 22:44:50 -04:00
lwvmobile
99b1fd2fe2 DMR: Tweaks to PDU Decryption;
Allow Manually Loaded Key Value AES on DMR PDU;

Add support for CCR AES-ECB PDU Decryption;
2025-09-18 12:13:54 -04:00
lwvmobile
b0a46a541d P25: Clear Stale Keys when using the Keyloader and a CC Return is initiated via LCW or Logic on END_PTT; 2025-09-11 23:19:16 -04:00
lwvmobile
3aabd85604 DMR: Retevis / Anytone AP (RC2) Encryption Support; 2025-09-11 22:49:22 -04:00
71 changed files with 13545 additions and 10820 deletions

3
.gitignore vendored
View File

@@ -9,5 +9,4 @@ examples
*.ans
*.patch
*.zip
result
.cache
result

View File

@@ -39,6 +39,62 @@ if (LZ)
add_definitions(-DLIMAZULUTWEAKS)
endif ()
#use cmake option -DASCII=ON to enable ASCII only Alias Support in NXDN
option(ASCII
"Build with ASCII only Alias Support in NXDN Enabled" ON)
if (ASCII)
add_definitions(-DASCII_DECODE)
endif ()
#use cmake option -DASCII=OFF -DBIG5=ON to enable ASCII and BIG5 Alias Support in NXDN
option(BIG5
"Build with BIG5 and ASCII Alias Support in NXDN Enabled" OFF)
if (BIG5)
add_definitions(-DBIG5_DECODE)
endif ()
#use cmake option -DASCII=OFF -DSJIS=ON to enable ASCII and SJIS Alias Support in NXDN
option(SJIS
"Build with SJIS and ASCII Alias Support in NXDN Enabled" OFF)
if (SJIS)
add_definitions(-DSJIS_DECODE)
endif ()
#use cmake option -DASCII=OFF -DUTF16=ON to enable ASCII and UTF16BE Alias Support in NXDN
option(UTF16
"Build with UTF16BE and ASCII Alias Support in NXDN Enabled" OFF)
if (UTF16)
add_definitions(-DUTF16_DECODE)
endif ()
#use cmake option -DTYPEC=OFF to disable NXDN Type-C and Conventional Decoding (enabled by default)
option(TYPEC
"Build with NXDN Type-C and Conventional Decoding Enabled" ON)
if (TYPEC)
add_definitions(-DTYPE_C_DECODE)
endif ()
#use cmake option -DTYPED=OFF to disable NXDN Type-D / IDAS Decoding (enabled by default)
option(TYPED
"Build with NXDN Type-D / IDAS Decoding Enabled" ON)
if (TYPED)
add_definitions(-DTYPE_D_DECODE)
endif ()
#use cmake option -DDCR=OFF to disable Japanese DCR (NXDN Variant) Decoding (enabled by default)
option(DCR
"Build with Japanese DCR (NXDN Variant) Decoding Enabled" ON)
if (DCR)
add_definitions(-DJPNDCR_DECODE)
endif ()
#use cmake option -DNOS=ON to enable older NXDN sync w/ pos and inv polarity, disable newer seperated (disabled by default)
option(NOS
"Build with Old NXDN Sync (pos and inv polarity) Enabled" OFF)
if (NOS)
add_definitions(-DNXDN_OLD_SYNC)
endif ()
#use cmake option -DSID=ON to enable P25p1 Soft ID decoding -- Experimental
option(SID
"Build with P25p1 LSD/Soft ID Enabled" OFF)
@@ -52,14 +108,13 @@ git_describe(GIT_TAG)
find_package(LibSndFile REQUIRED)
find_package(MBE REQUIRED)
find_package(ITPP REQUIRED)
find_package(RTLSDR)
find_package(Curses REQUIRED)
find_package(PulseAudio REQUIRED)
find_package(CODEC2)
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIRS})
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES})
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIRS})
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES})
if(RTLSDR_FOUND)
find_package(Threads)

View File

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

View File

@@ -70,7 +70,7 @@ if [ "$RELEASE" = "y" ]; then
cp .profile dsd-fme-portable/dsd-fme/
#move (cut) the bat files to the portable folder root and delete the other ones so users won't get confused
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/start-dsd-fme.bat dsd-fme-portable/
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/*.bat dsd-fme-portable/
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/example_options.txt dsd-fme-portable/
mv dsd-fme-portable/dsd-fme/examples/cygwin_bat/complete_usage_options.txt dsd-fme-portable/
rm -rf dsd-fme-portable/dsd-fme/examples/cygwin_bat
@@ -94,7 +94,6 @@ if [ "$RELEASE" = "y" ]; then
#compiled items into release dsd-fme folder
cp codec2/build/src/cygcodec2* dsd-fme-portable/dsd-fme/
cp itpp-4.3.1/build/itpp/cygitpp* dsd-fme-portable/dsd-fme/
cp rtl-sdr/build/src/cyg* dsd-fme-portable/dsd-fme/
cp mbelib/build/cygmbe* dsd-fme-portable/dsd-fme/
@@ -128,7 +127,6 @@ if [ "$RELEASE" = "y" ]; then
cp /bin/cygdbus* dsd-fme-portable/dsd-fme/
cp /bin/cygedit* dsd-fme-portable/dsd-fme/
cp /bin/cygexpat* dsd-fme-portable/dsd-fme/
cp /bin/cygfam* dsd-fme-portable/dsd-fme/
cp /bin/cygfdisk* dsd-fme-portable/dsd-fme/
cp /bin/cygffi* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3_threads* dsd-fme-portable/dsd-fme/
@@ -152,7 +150,6 @@ if [ "$RELEASE" = "y" ]; then
cp /bin/cyggomp* dsd-fme-portable/dsd-fme/
cp /bin/cyggpg-error* dsd-fme-portable/dsd-fme/
cp /bin/cyggpgme* dsd-fme-portable/dsd-fme/
cp /bin/cyggsasl* dsd-fme-portable/dsd-fme/
cp /bin/cyggsm* dsd-fme-portable/dsd-fme/
cp /bin/cyggssapi_krb5* dsd-fme-portable/dsd-fme/
cp /bin/cyggthread* dsd-fme-portable/dsd-fme/
@@ -179,7 +176,6 @@ if [ "$RELEASE" = "y" ]; then
cp /bin/cygncurses* dsd-fme-portable/dsd-fme/
cp /bin/cygnettle* dsd-fme-portable/dsd-fme/
cp /bin/cygnghttp* dsd-fme-portable/dsd-fme/
cp /bin/cygntlm* dsd-fme-portable/dsd-fme/
cp /bin/cygobjc* dsd-fme-portable/dsd-fme/
cp /bin/cygogg* dsd-fme-portable/dsd-fme/
cp /bin/cygopus* dsd-fme-portable/dsd-fme/

View File

@@ -17,11 +17,6 @@ if [ "$ANSWER" = "y" ]; then
sudo pacman -Syu #always run a full update first, partial upgrades aren't supported in Arch -- including downloading dependencies, that may require an updated dependency, and breaks same dependency on another package, a.k.a, dependency hell
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget
printf "Installing itpp 4.3.1 from Arch Strike --Tested on Arch 2023.08.01 and Manjaro XFCE 22.1.3 Minimal\n"
wget https://mirror.archstrike.org/x86_64/archstrike/itpp-4.3.1-3-x86_64.pkg.tar.xz
sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
sudo rm -R itpp-4.3.1-3-x86_64.pkg.tar.xz
git clone https://github.com/lwvmobile/mbelib
cd mbelib
git checkout ambe_tones

View File

@@ -19,18 +19,6 @@ if [ "$ANSWER" = "y" ]; then
export LD_LIBRARY_PATH
echo $LD_LIBRARY_PATH
#ITPP
cd $cdir
printf "Installing itpp 4.3.1 from source http://sourceforge.net/projects/itpp/files/latest/download?source=files\n Please wait!\n"
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
tar xjf itpp-latest.tar.bz2
cd itpp-4.3.1/
mkdir build
cd build
cmake ..
make -j $(nproc)
make install
#MBELIB
cd $cdir
printf "Installing mbelib\n Please wait!\n"

View File

@@ -12,13 +12,7 @@ read -p "Have you viewed the patent notice? y/N " ANSWER
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
if [ "$ANSWER" = "y" ]; then
sudo apt update
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncurses-dev libncurses6 libcodec2-dev
chkitpp=$(dpkg -l libitpp-dev 2>/dev/null | awk '/^ii/ {print 1} END {if (NR == 0) print 0}')
if [ "$chkitpp" -lt "1" ];then
printf "\nFor some reason, ITPP did NOT install!\nYou may have to manually install it.\n\n"
read -p "Press Enter to exit..." x
exit 1
fi
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncurses-dev libncurses6 libcodec2-dev
git clone https://github.com/lwvmobile/mbelib
cd mbelib
git checkout ambe_tones

View File

@@ -12,13 +12,7 @@ read -p "Have you viewed the patent notice? y/N " ANSWER
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
if [ "$ANSWER" = "y" ]; then
sudo apt update
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev libcodec2-dev
chkitpp=$(dpkg -l libitpp-dev 2>/dev/null | awk '/^ii/ {print 1} END {if (NR == 0) print 0}')
if [ "$chkitpp" -lt "1" ];then
printf "\nFor some reason, ITPP did NOT install!\nYou may have to manually install it.\n\n"
read -p "Press Enter to exit..." x
exit 1
fi
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncursesw5-dev libcodec2-dev
git clone https://github.com/lwvmobile/mbelib
cd mbelib
git checkout ambe_tones

View File

@@ -18,7 +18,7 @@
-f2 P25P2 (may need to specify wacn/sys/nac manually if traffic channel)
-fx X2-TDMA
-fy YSF
-fz M17 Audio
-fz M17 Stream
```
## Conventional Frequency Scanner
@@ -190,7 +190,7 @@ y - toggle scanner (needs either rtl input, or rigctl connection)
1 - Toggle Voice Synthesis in TDMA Slot 1 or FDMA Conventional Voice
2 - Toggle Voice Synthesis in TDMA Slot 2
w - Toggle Trunking/Playback White List (Allow A Groups Only) / Black List (Block B or DE groups only) Mode
g - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25, NXDN)
T - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25, NXDN)
u - Toggle Trunking Tuning to Private Calls (DMR T3, Cap+, P25)
d - Toggle Trunking Tuning to Data Calls (DMR T3, NXDN)
e - Toggle Trunking Tuning to Encrypted Calls (P25)
@@ -222,6 +222,12 @@ N - Toggle Passband Filter on Analog Voice or Raw Signal Monitoring
v - Cycle RTL Input Volume Multiplier 1X, 2X, 3X (lower v)
g - Manually decrease RTL gain
G - Manually increase RTL gain
{ - Manually decrease PPM
} - Manually increase PPM
```
## Sending Audio to a Icecast 2 Server via FFmpeg (Windows)
@@ -311,70 +317,3 @@ On the remote side where dsd-fme is, use the TCP input:
`dsd-fme -i tcp:192.168.7.8:1234 -N`
Note: As always, when using rtl_fm, play with `-l` and `-g` to find a good squelch and gain values for your environment - you will know if the squelch is the right value when `input.wav` does not increase in size in the absence of useful signal on the monitored frequency.
## Using the Experimental M17 Encoder (and Decoder)
You can use the experimental M17 Voice Stream and Packet encoder with the following examples.
Encode/Decode Local Feedback Voice Stream (Single Session):
`dsd-fme -fZ -i pulse -o pulse -M M17:3:SP5WWP:BROADCAST:48000`
Using the `-8` option (raw audio monitor) will instead send encoded raw audio to output instead of decoded audio. For example,
To send a voice stream out of stdout into SOCAT TCP to a second/remote DSD-FME session to decode:
Encode (Upper Z):
`dsd-fme -fZ -8 -i pulse -M M17:9:LWVMOBILE:DSD-FME:48000 -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
Decode (Lower z):
`dsd-fme -fz -i tcp:192.168.7.8:7355`
NOTE: When using tcp input for M17 decoding, DSD-FME will keep the TCP connection alive with constant retries, so no need to restart the decoding session on connection failure.
SMS Packet Encoder (Text Only):
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it."`
SMS Packet Encoder (over SOCAT TCP):
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it and SOCAT to send it over TCP." -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
Many combinations of input and output can be used as well, and using features like raw audio saving with `-6 m17str.wav` will save a wav file of your encoded audio to be replayed later with `-i m17str.wav`
Also note, features function similarly, like -Z for payload, and input options include pulse, oss, stdin, rtl, tcp, etc, using those as the mic in for voice encoding. With the right setup of input/output/input using SOCAT TCP pipes, it is conceivable to use DSD-FME to convert analog audio from NFM into M17 audio, or even change another digital format supported by DSD-FME into M17 audio, if desired (purely for research and fun purposes). Ncurses terminal will function with internal encoder/decoder and wav file creation, with toggle encode/tx with `\` key, but will not function with stdout properly.
The `-M` switch is used to specify user M17 variables, the string is in the format of M17:CAN:SRC:DST:INPUT_RATE.
CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.
BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'
Input Rate Default is 48000; Use Multiples of 8000 up to 48000.
Values not entered into the M17: string are set to default values. M17:7:DSD-FME:DSD-FME:48000
Fun Example Transmuting OTA Analog Things into M17:
Start up in sequence.
Terminal #1 (RTL Input):
`dsd-fme -fZ -8 -M M17:1:ANALOG:M17:48000 -i rtl:0:162.425M -o - | socat stdio tcp-listen:7310,forever,reuseaddr`
Terminal #2:
`dsd-fme -fz -i tcp:127.0.0.1:7310`
Fun Example Transmuting OTA Digital Things into M17:
Start up all three of these in sequence.
Terminal #1 (connecting to SDR++):
`dsd-fme -fi -i tcp:127.0.0.1:7355 -o - | socat stdio tcp-listen:7300,forever,reuseaddr`
Terminal #2:
`dsd-fme -fZ -8 -M M17:2:NXDN:M17:8000 -i tcp:127.0.0.1:7300 -o - | socat stdio tcp-listen:7301,forever,reuseaddr`
Terminal #3:
`dsd-fme -fz -i tcp:127.0.0.1:7301`
Note: Transmuting only carries over voice when audio is output, and will not carry any ID values, or any other data payloads.
This mode is only meant for educational or fun purposes, please do not retransmit stuff that doesn't belong to you.
Information on this section will be updated as features are added or modified, etc. If you need help with a particular function and can't find a suitable answer, always open a new issue here, or make contact on the radio reference forum for additional examples, guidance, ideas, help, etc.

View File

@@ -43,9 +43,9 @@ Debian/Mint/Ubuntu/Pi
```
sudo apt update
#Ubuntu 22.04 lts / Debian 11 and lower
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libcodec2-dev
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libcodec2-dev
#Ubuntu 24.04 lts / Debian 12
#sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncurses-dev libncurses6 libcodec2-dev
#sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncurses-dev libncurses6 libcodec2-dev
```
Fedora 36/37 -- from https://github.com/lwvmobile/dsd-fme/issues/99
@@ -60,8 +60,6 @@ Arch -- https://github.com/lwvmobile/dsd-fme/issues/153 and https://github.com/l
```
sudo pacman -Syu
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget rtl-sdr
wget https://mirror.archstrike.org/x86_64/archstrike/itpp-4.3.1-3-x86_64.pkg.tar.xz
sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
```
## Headless Ubuntu Server/Pi
@@ -69,24 +67,7 @@ sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
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 libcodec2-dev
```
### 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 ..
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncursesw5-dev pulsemixer pulseaudio libcodec2-dev
```
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")

View File

@@ -1,4 +1,4 @@
Build Version: AW 2025-182-g24c1dd9
Build Version: AW 2026-30-g43d63b2
MBElib Version: 1.3.4
CODEC2 Support Enabled
@@ -23,8 +23,6 @@ Input/Output options:
rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)
tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)
tcp:192.168.7.5:7355 for custom address and port
m17udp for M17 UDP/IP socket bind input (default host 127.0.0.1; default port 17000)
m17udp:192.168.7.8:17001 for M17 UDP/IP bind input (Binding Address and Port
filename.bin for OP25/FME capture bin files
filename.wav for 48K/1 wav files (SDR++, GQRX)
filename.wav -s 96000 for 96K/1 wav files (DSDPlus)
@@ -37,8 +35,6 @@ Input/Output options:
null for no audio output
udp for UDP socket blaster output (default host 127.0.0.1; default port 23456)
udp:192.168.7.8:23470 for UDP socket blaster output (Target Address and Port
m17udp for M17 UDP/IP socket blaster output (default host 127.0.0.1; default port 17000)
m17udp:192.168.7.8:17001 for M17 UDP/IP blaster output (Target Address and Port
-d <dir> Create mbe data files, use this directory (TDMA version is experimental)
-r <files> Read/Play saved mbe data from file(s)
-g <float> Audio Digital Output Gain (Default: 0 = Auto; )
@@ -54,6 +50,8 @@ Input/Output options:
-w <file> Output synthesized speech to a single static .wav file. (Do not use with -P Per Call Switch)
-P Enable Per Call WAV file saving. (Do not use with -w filename.wav single wav file switch)
(Per Call works with everything now and doesn't require ncurses terminal!)
-j <string> Custom Tag(s), colon delimited, to append to Per Call decoded .wav files.
-j TGT_LAPD:TAG_FIRE:FREQ_851987500 (Total 3 Tags, Maximum Letter Length = 100)
-a Enable Call Alert Beep
(Warning! Might be annoying.)
-J <file> Specify Filename for Event Log Output.
@@ -81,26 +79,6 @@ RTL-SDR options:
Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2
Encoder options:
-fZ M17 Stream Voice Encoder
Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i pulse -6 m17signal.wav -8 -N 2> m17encoderlog.txt
Run M17 Encoding, listening to pulse audio server, with internal decode/playback and output to 48k/1 wav file
Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i tcp -o pulse -8 -N 2> m17encoderlog.txt
Run M17 Encoding, listening to default tcp input, without internal decode/playback and output to 48k/1 analog output device
-fP M17 Packet Encoder
Example: dsd-fme -fP -M M17:9:DSD-FME:LWVMOBILE -6 m17pkt.wav -8 -S 'Hello World'
-fB M17 BERT Encoder
Example: dsd-fme -fB -M M17:9:DSD-FME:LWVMOBILE -6 m17bert.wav -8
-M M17 Encoding User Configuration String: M17:CAN:SRC:DST:INPUT_RATE:VOX (see examples above).
CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.
BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'
Input Rate Default is 48000; Use Multiples of 8000 up to 48000.
VOX Enabled on 1; (Default = 0)
Values not entered into the M17: string are set to default values.
Decoder options:
-fa Auto Detection
-fA Passive Analog Audio Monitor
@@ -112,7 +90,6 @@ Decoder options:
-fx Decode only X2-TDMA
-fy Decode only YSF
-fz Decode only M17*
-fU Decode only M17 UDP/IP Frame***
-fi Decode only NXDN48* (6.25 kHz) / IDAS*
-fn Decode only NXDN96* (12.5 kHz)
-fp Decode only ProVoice*
@@ -128,15 +105,16 @@ Decoder options:
-xx Expect non-inverted X2-TDMA signal
-xr Expect inverted DMR signal
-xd Expect inverted ICOM dPMR signal
-xz Expect inverted M17 signal
-xn Expect inverted NXDN/DCR/IDAS signal
* denotes frame types that cannot be auto-detected.
** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.
*** configure UDP Input with -i m17:127.0.0.1:17000
Advanced Decoder options:
-X <hex> Manually Set P2 Parameters (WACN, SYSID, CC/NAC)
(-X BEE00ABC123)
-_ <dec> Manually Set NXDN PN95 Dibit Scrambler Seed (1-511)
(-* 261)
-D <dec> Manually Set TIII DMR Location Area n bit len (0-10)(10 max)
(Value defaults to max n bit value for site model size)
(Setting 0 will show full Site ID, no area/subarea)
@@ -150,10 +128,9 @@ Advanced Decoder options:
Use this feature to allow MAC_SIGNAL even if CRC errors.
-F Relax DMR RAS/CRC CSBK/DATA Pass/Fail
Enabling on some systems could lead to bad channel assignments/site data decoding if bad or marginal signal
-F Relax M17 LSF/PKT CRC Error Checking
-b <dec> Manually Enter Basic Privacy Key (Decimal Value of Key Number)
(NOTE: This used to be the 'K' option!
(NOTE: This used to be the 'K' option!)
-H <hex> Manually Enter Hytera 10/32/64 Char Basic Privacy Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
@@ -161,7 +138,7 @@ Advanced Decoder options:
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-H <hex> Manually Enter AES-128 or AES-256 Hex Key (see example below)
-H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
@@ -172,14 +149,28 @@ Advanced Decoder options:
-2 <hex> Manually Enter and Enforce TYT 16-bit BP Key Value (DMR) (Hex Value)
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) AP Hex Key (see example below)
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) 128 or 256 bit AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-! '736B9A9C5645288B 243AD5CB8701EF8A'
-! '1111111111111111 1111111111111111 1111111111111111 1111111111111111'
-@ <hex> Manually Enter and Enforce Retevis Advanced Privacy (RC2) 128 or 256 bit AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-@ '736B9A9C5645288B 243AD5CB8701EF8A'
-@ '1122334455667788 99AABBCCDDEEFF11 1122334455667788 99AABBCCDDEEFF11'
-5 <hex> Manually Enter and Enforce TYT Enhanced Privacy (AES-128) EP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-5 '736B9A9C5645288B 243AD5CB8701EF8A'
-+ <hex> Manually Enter and Enforce Baofeng Advanced Privacy (PC5-128 or PC5-256) AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC'
-+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC EF01EF01EF01EF01 EF01EF01EF01EF01'
-^ <hex> Manually Enter and Enforce Connect Systems 72-bit (9-byte) Extended Encryption Hex Key (see example below)
-^ 3BBE782C0430008271
-9 <dec> Manually Enter and Enforce Kenwood 15-bit Scrambler Key Value (DMR) (Dec Value)
-A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value)
@@ -191,6 +182,8 @@ Advanced Decoder options:
-S 49:123456789ABC80
For Example, enter 49-bit Keystream (MBP 70) as:
-S 49:ED0AED4AED4AED4A
For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:
-S 49:11AA22BB33CC44
-k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.
Only supports NXDN, DMR Basic Privacy (decimal value).
@@ -203,6 +196,8 @@ Advanced Decoder options:
-0 Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR)
-M <hex> Force Entered Alg ID Value over Missing PI header/LE Encryption Identifiers (DMR)
-3 Disable DMR Late Entry Encryption Identifiers (VC6 Single Burst)
Note: Disable this if false positives on Voice ENC occur.

View File

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

View File

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

View File

@@ -59,8 +59,9 @@
#include "p25p1_heuristics.h"
//OSS support
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__)
#include <sys/soundcard.h>
#endif
#include <pulse/pulseaudio.h> //PULSE AUDIO
#include <pulse/simple.h> //PULSE AUDIO
#include <pulse/error.h> //PULSE AUDIO
@@ -172,8 +173,8 @@ typedef struct pa_devicelist
typedef struct
{
unsigned long int groupNumber;
char groupMode[8]; //char *?
char groupName[50];
char groupMode[3];
char groupName[100];
} groupinfo;
@@ -323,6 +324,7 @@ typedef struct
char wav_out_file[1024];
char wav_out_fileR[1024];
char wav_out_file_raw[1024];
char wav_custom_tag[100];
char symbol_out_file[1024];
char lrrp_out_file[1024];
char event_out_file[1024];
@@ -353,25 +355,23 @@ typedef struct
int uvquality;
int inverted_x2tdma;
int inverted_dmr;
int inverted_nxdn;
int mod_threshold;
int ssize;
int msize;
int playfiles;
int m17encoder;
int m17encoderbrt;
int m17encoderpkt;
int m17decoderip;
int delay;
int use_cosine_filter;
int unmute_encrypted_p25;
int rtl_dev_index;
int rtl_gain_value;
int rtl_gain_actual;
int rtl_squelch_level;
int rtl_volume_multiplier;
int rtl_udp_port;
int rtl_bandwidth;
int rtl_started;
long int rtl_pwr;
long int rtl_rms;
int monitor_input_audio;
int analog_only;
int pulse_raw_rate_in;
@@ -414,7 +414,6 @@ typedef struct
short int aggressive_framesync;
int frame_m17;
int inverted_m17;
FILE *symbolfile;
int call_alert;
@@ -431,12 +430,6 @@ typedef struct
int udp_portno;
char udp_hostname[1024];
//M17 UDP for IP frame output
int m17_use_ip; //if enabled, open UDP and broadcast IP frame
int m17_portno; //default is 17000
int m17_udp_sock; //actual UDP socket for M17 to send to
char m17_hostname[1024];
//tcp socket for SDR++, etc
int tcp_sockfd;
int tcp_portno;
@@ -643,7 +636,7 @@ typedef struct
unsigned long long int K2;
unsigned long long int K3;
unsigned long long int K4;
int M;
uint8_t forced_alg_id;
int menuopen;
//AES Key Segments
@@ -879,7 +872,7 @@ typedef struct
//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
groupinfo * group_array;
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
@@ -898,6 +891,7 @@ typedef struct
uint8_t nxdn_sacch_frame_segcrc[4];
uint8_t nxdn_alias_block_number;
char nxdn_alias_block_segment[4][4][8];
uint16_t nxdn_pn95_seed;
//site/srv/cch info
char nxdn_location_category[14];
@@ -952,15 +946,11 @@ typedef struct
//M17 Storage
uint8_t m17_lsf[360];
uint8_t m17_pkt[850];
uint8_t m17_pbc_ct; //pbc packet counter
uint8_t m17_str_dt; //stream contents
unsigned long long int m17_dst;
unsigned long long int m17_src;
uint8_t m17_can; //can value that was decoded from signal
int m17_can_en; //can value supplied to the encoding side
int m17_rate; //sampling rate for audio input
int m17_vox; //vox enabled via PWR value
char m17_dst_csd[20];
char m17_src_csd[20];
@@ -968,18 +958,22 @@ typedef struct
char m17_src_str[50];
char m17_dst_str[50];
uint8_t m17_meta[16]; //packed meta
uint8_t m17_enc; //enc type
uint8_t m17_enc_st; //scrambler or data subtye
int m17encoder_tx; //if TX (encode + decode) M17 Stream is enabled
int m17encoder_eot; //signal if we need to send the EOT frame
uint8_t m17_meta[16]; //packed meta
uint8_t m17_aes_iv[16]; //aes iv
uint8_t m17_enc; //enc type
uint8_t m17_enc_st; //scrambler or data subtye
char m17_text_string[1024];
char m17_gnss_string[1024];
char m17_data_string[1024];
char m17_meta_string[1024];
float m17_viterbi_err;
//misc str storage
char str50a[50];
char str50b[50];
char str50c[50];
char m17dat[50]; //user supplied m17 data input string
char m17sms[800]; //user supplied sms text string
//Codec2
#ifdef USE_CODEC2
@@ -991,6 +985,9 @@ typedef struct
int tyt_ap;
int tyt_bp;
int tyt_ep;
// retrevis rc2
int retevis_ap;
void *rc2_context;
//kenwood scrambler on DMR with forced application
int ken_sc;
@@ -998,6 +995,12 @@ typedef struct
//anytone bp
int any_bp;
//baofeng ap
int baofeng_ap;
//connect systems ee
int csi_ee;
//generic ks
int straight_ks;
int straight_mod;
@@ -1014,13 +1017,6 @@ typedef struct
//M17 Sync Patterns
#define M17_LSF "11113313"
#define M17_STR "33331131"
//alternating with last symbol opposite of first symbol of LSF
#define M17_PRE "31313131"
#define M17_PIV "13131313"
#define M17_PRE_LSF "3131313133331131" //Preamble + LSF
#define M17_PIV_LSF "1313131311113313" //Preamble + LSF
#define M17_BRT "31331111"
#define M17_PKT "13113333"
#define FUSION_SYNC "31111311313113131131"
#define INV_FUSION_SYNC "13333133131331313313"
@@ -1190,7 +1186,7 @@ void openWavOutFileLR (dsd_opts * opts, dsd_state * state); //stereo wav file fo
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state);
SNDFILE * open_wav_file (char * dir, char * temp_filename, uint16_t sample_rate, uint8_t ext);
SNDFILE * close_wav_file(SNDFILE * wav_file);
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, Event_History_I * event_struct);
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, char * custom_tag, Event_History_I * event_struct);
SNDFILE * close_and_delete_wav_file(SNDFILE * wav_file, char * wav_out_filename);
void openSymbolOutFile (dsd_opts * opts, dsd_state * state);
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state);
@@ -1244,13 +1240,6 @@ void processDSTAR_HD (dsd_opts * opts, dsd_state * state); //DSTAR Header
void processDSTAR_SD (dsd_opts * opts, dsd_state * state, uint8_t * sd); //DSTAR Slow Data
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
void processM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR)
void processM17PKT(dsd_opts * opts, dsd_state * state); //M17 (PKT)
void processM17LSF(dsd_opts * opts, dsd_state * state); //M17 (LSF)
void processM17IPF(dsd_opts * opts, dsd_state * state); //M17 (IPF)
void encodeM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR) encoder
void encodeM17BRT(dsd_opts * opts, dsd_state * state); //M17 (BRT) encoder
void encodeM17PKT(dsd_opts * opts, dsd_state * state); //M17 (PKT) encoder
void decodeM17PKT(dsd_opts * opts, dsd_state * state, uint8_t * input, int len); //M17 (PKT) decoder
void processP2(dsd_opts * opts, dsd_state * state); //P2
void processTSBK(dsd_opts * opts, dsd_state * state); //P25 Trunking Single Block
void processMPDU(dsd_opts * opts, dsd_state * state); //P25 Multi Block PDU (SAP 0x61 FMT 0x15 or 0x17 for Trunking Blocks)
@@ -1276,25 +1265,25 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state);
uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c);
void ncursesClose ();
//new NXDN Functions start here!
//NXDN Functions Start Here
void nxdn_frame (dsd_opts * opts, dsd_state * state);
void nxdn_descramble (uint8_t dibits[], int len);
//nxdn deinterleaving/depuncturing functions
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
//type-d 'idas' deinterleaving/depuncturing functions
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction);
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
//DCR Mode
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
//MT and Voice
void nxdn_pn95_dibit_scrambler(dsd_state * state, uint8_t * dibits, int len);
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
//Osmocom OP25 12 Rate Trellis Decoder (for NXDN, M17, YSF, etc)
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf);
//All-in-One NXDN Soft Decision Viterbi Function based on libM17
uint32_t nxdn_soft_decision_viterbi(uint8_t * bits, const uint16_t * interleave, uint8_t * puncture, int d_len, int p_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
//NXDN Conventional and Type-C
void nxdn_facch1(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t frame);
void nxdn_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_cac(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
//type-d 'idas'
void idas_scch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t direction);
void idas_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
//Japanese DCR
void dcr_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void dcr_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t lich);
//OP25 NXDN CRC functions
int load_i(const uint8_t val[], int len);
@@ -1303,15 +1292,18 @@ uint16_t crc12f(const uint8_t buf[], int len);
uint16_t crc15(const uint8_t buf[], int len);
uint16_t crc16cac(const uint8_t buf[], int len);
uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
uint32_t nxdn_message_crc32(uint8_t * input, int len);
/* NXDN Convolution functions */
void CNXDNConvolution_start(void);
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
void CNXDNConvolution_init();
//YSF Soft Decision Viterbi
uint32_t ysf_soft_decision_viterbi(uint8_t * dbuf, int d_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
//libM17 viterbi decoder
//libm17 magic soft decision based viterbi
#define SYM_PER_PLD 184
void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD]);
void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2]);
void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_PLD*2]);
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len);
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len);
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos);
@@ -1319,6 +1311,9 @@ uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len);
void viterbi_reset(void);
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2);
//libm17 based soft decision viterbi for variable len and new punctures
void slice_symbols_to_len(uint16_t * out, const float * inp, int len);
//keeping these
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
@@ -1327,16 +1322,29 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message);
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_Data_Call_Option_To_Str(uint8_t DataCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
//added these
void NXDN_decode_Prop(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_Alias16(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_sdcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
//Type-D SCCH Message Decoder
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
void NXDN_decode_VCALL_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
uint32_t sjis_char_to_unicode(uint16_t sjis);
uint32_t big5_char_to_unicode(uint16_t big5);
int utf8_encode(int c, char *buf);
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
@@ -1376,7 +1384,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18]); //simplier trellis decoder
uint32_t viterbi_r34 (uint8_t * input, uint8_t * output);
void beeper (dsd_opts * opts, dsd_state * state, int lr, int id, int ad, int len);
void dmr_gateway_identifier (uint32_t source, uint32_t target); //translate special addresses
@@ -1398,10 +1406,11 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input);
void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int type);
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot);
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input);
void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot, int phase);
void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src);
void utf16_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input);
void utf8_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input);
uint8_t nmea_sentence_checker(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t slot, int len);
//"DMR STEREO"
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
@@ -1580,7 +1589,6 @@ void init_rrc_filter_memory();
//misc audio filtering for analog
long int raw_rms(short *samples, int len, int step);
long int raw_pwr(short *samples, int len, int step);
void init_audio_filters(dsd_state * state);
void lpf(dsd_state * state, short * input, int len);
void hpf(dsd_state * state, short * input, int len);
@@ -1610,9 +1618,9 @@ int udp_socket_connect(dsd_opts * opts, dsd_state * state);
int udp_socket_connectA(dsd_opts * opts, dsd_state * state);
void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
int m17_socket_receiver(dsd_opts * opts, void * data);
int udp_socket_connectM17(dsd_opts * opts, dsd_state * state);
int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
//15-bit Scrambler
void pdu_scrambler_keystream_creation(uint8_t * ks, int lfsr, int len);
//RC4 function prototypes
void rc4_voice_decrypt (int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[]);
@@ -1630,25 +1638,33 @@ void aes_cfb_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in,
void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
//Tytera Encryption Modes
//Tytera / Retevis / Anytone / Kenwood / Misc DMR Encryption Modes
void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int fnum);
void tyt_ep_aes_keystream_creation(dsd_state * state, char * input);
void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input);
//Misc Other Encryption Modes
void retevis_rc2_keystream_creation(dsd_state *state, char *input);
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input);
void anytone_bp_keystream_creation(dsd_state * state, char * input);
void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input);
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]);
void straight_mod_xor_keystream_creation(dsd_state * state, char * input);
//Kirisun
uint32_t kirisun_lfsr(unsigned long long int mi);
void kirisun_adv_keystream_creation(dsd_state *state);
void kirisun_uni_keystream_creation(dsd_state *state);
//Hytera Enhanced
void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value);
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len);
void hytera_enhanced_alg_refresh(dsd_state * state);
//LFSR to expand either a DMR 32-bit or P25/NXDN 64-bit MI into a 128-bit IV for AES
void lfsr_64_to_128(uint8_t * iv);
void LFSR128(dsd_state * state);
void LFSR128n(dsd_state * state);
void LFSR128d(dsd_state * state);
void LFSR128npdu(dsd_state * state);
#ifdef __cplusplus
@@ -1661,7 +1677,7 @@ void cleanup_rtlsdr_stream();
int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
void rtlsdr_sighandler();
void rtl_dev_tune(dsd_opts * opts, long int frequency);
long int rtl_return_pwr();
long int rtl_return_rms();
void rtl_clean_queue();
#endif
@@ -1672,6 +1688,9 @@ 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 isch_lookup (uint64_t isch); //isch map lookup
//Phase 1 NID Check
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity);
#ifdef __cplusplus
}
#endif

View File

@@ -16,7 +16,7 @@
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
// 02110-1301, USA.
static const uint8_t PERM_12_5[] = {
static const uint16_t PERM_12_5[] = {
0,12,24,36,48,
1,13,25,37,49,
2,14,26,38,50,
@@ -31,7 +31,7 @@ static const uint8_t PERM_12_5[] = {
11,23,35,47,59
};
static const uint8_t PERM_16_9[] = {
static const uint16_t PERM_16_9[] = {
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,

View File

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

147
include/pc5.h Normal file
View File

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

43
include/rc2.h Normal file
View File

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

View File

@@ -6,7 +6,6 @@
, ncurses
, pulseaudio
, rtl-sdr
, itpp
, codec2
, portaudioSupport ? true
, portaudio ? null
@@ -23,7 +22,6 @@ stdenv.mkDerivation {
buildInputs = [
mbelib
libsndfile
itpp
rtl-sdr
ncurses.dev
pulseaudio.dev
@@ -39,7 +37,7 @@ stdenv.mkDerivation {
tap audio and synthesize the decoded speech. Speech synthesis requires
mbelib, which is a separate package.
'';
homepage = "https://github.com/szechyjs/dsd";
homepage = "https://github.com/lwvmobile/dsd-fme";
license = licenses.mit;
platforms = platforms.unix;
};

417
src/crypt-kir.c Normal file
View File

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

468
src/crypt-pc5.c Normal file
View File

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

404
src/crypt-rc2.c Normal file
View File

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

View File

@@ -159,28 +159,96 @@ void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int
void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input)
{
unsigned char key1[16] = {0};
unsigned char key2[16] = {0};
unsigned char key1[64]; memset(key1, 0, sizeof(key1));
unsigned char key2[64]; memset(key2, 0, sizeof(key2));
char buf[1024];
strncpy(buf, input, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
char *pEnd;
uint64_t K1 = strtoull(buf, &pEnd, 16);
uint64_t K2 = strtoull(pEnd, &pEnd, 16);
uint64_t K3 = strtoull(pEnd, &pEnd, 16);
uint64_t K4 = strtoull(pEnd, &pEnd, 16);
if (K3 != 0 || K4 != 0) //256-bit keys loaded as ASCII characters
{
//start loading K1, K2, K3, and K4 as ASCII to fill 64 bytes of key1
int k = 0; uint8_t x = 0;
u64_to_bytes_be(K1, &key1[0]);
u64_to_bytes_be(K2, &key1[8]);
for (int i = 0; i < 16; i++)
{
x = ((K1 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
for (int i=0;i<16;i++) key2[i] = key1[15-i];
for (int i = 0; i < 16; i++)
{
x = ((K2 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
/* Create key schedule */
create_keys(&ctx, key2, sizeof(key2));
ctx.rounds = nbround;
for (int i = 0; i < 16; i++)
{
x = ((K3 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
fprintf (stderr,"DMR TYT AP (PC4) 128-bit Key %016llX%016llX with Forced Application\n", (unsigned long long int)K1, (unsigned long long int)K2);
state->tyt_ap = 1;
for (int i = 0; i < 16; i++)
{
x = ((K4 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
//debug
// fprintf (stderr, "ASCII: ");
// for (int i = 0; i < 64; i++)
// fprintf (stderr, " %02X", key1[i]);
// fprintf (stderr, "\n");
/* Create key schedule */
create_keys(&ctx, key1, 64);
ctx.rounds = nbround;
fprintf(stderr, "DMR TYT AP (PC4) 256-bit Key %016llX%016llX%016llX%016llX with Forced Application\n",
(unsigned long long)K1, (unsigned long long)K2, (unsigned long long)K3, (unsigned long long)K4);
state->tyt_ap = 1;
}
else //128-bit loaded as reverse bytes
{
u64_to_bytes_be(K1, &key1[0]);
u64_to_bytes_be(K2, &key1[8]);
// reverse load the key bytes into key2
for (int i=0;i<16;i++)
key2[i] = key1[15-i];
/* Create key schedule */
create_keys(&ctx, key2, 16);
ctx.rounds = nbround;
fprintf (stderr,"DMR TYT AP (PC4) 128-bit Key %016llX%016llX with Forced Application\n",
(unsigned long long int)K1, (unsigned long long int)K2);
state->tyt_ap = 1;
}
}
@@ -238,4 +306,56 @@ void tyt_ep_aes_keystream_creation(dsd_state * state, char * input)
fprintf (stderr,"DMR TYT EP (AES-128) Key %016llX%016llX with Forced Application\n", K1, K2);
state->tyt_ep = 1;
}
}
//connect systems 72-bit (9-byte) Extended Encryption //TODO: Move this later
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24])
{
char interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//interleave the frame
const int *w, *x, *y, *z;
w = rW; x = rX; y = rY; z = rZ;
for (int8_t i = 0; i < 36; i++)
{
interleaved[(i*2)+0] = ambe_fr[*w][*x];
interleaved[(i*2)+1] = ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
uint8_t ks_bytes[9]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t ks[72]; memset(ks, 0, sizeof(ks));
//keys are loaded in reverse byte order, stored in state->static_ks_bits[0]
for (int i = 0; i < 9; i++)
ks_bytes[i] = state->static_ks_bits[0][8-i];
//convert byte array into a bit array
unpack_byte_array_into_bit_array(ks_bytes, ks, 9);
//apply keystream to interleave
for (int8_t i = 0; i < 72; i++)
interleaved[i] ^= ks[71-i];
//deinterleave back into ambe_fr frame
w = rW; x = rX; y = rY; z = rZ;
int k = 0;
for (int8_t i = 0; i < 36; i++)
{
ambe_fr[*w][*x] = interleaved[k++];
ambe_fr[*y][*z] = interleaved[k++];
w++;
x++;
y++;
z++;
}
}

View File

@@ -1,12 +1,35 @@
/*-------------------------------------------------------------------------------
* dmr_34.c
* DMR (and P25) 3/4 Rate Simple Trellis Decoder
* DMR (and P25) 3/4 Rate Viterbi Trellis Decoder (Improved)
*
* Code Sourced From: Unknown
* No License Provided
*
* LWVMOBILE
* 2023-12 DSD-FME Florida Man Edition
* 2026-02 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#define INRANGE 8 // Serial input is grouped in 3 bits, each input value ranges 0~7
#define INSTATIC 8 // Number of possible states in the state machine, range 0~7
#define INDATA 49 // Input 144 bits, grouped into 3-bit units → outputs 48 values (0~15) + one final 0 to determine end state
#define INSYMBOL 49 // Number of input symbols
#define PATH 64 // For the i-th (48≥i≥2) input, there are 64 branch metrics → 64 possible paths
#define SELECTPATH 8 // Among the 64 paths, select the 8 with smallest metrics, then choose the smallest one as final output
#define BITELEN 9 //
/******************************************************************************
* Header file includes
*******************************************************************************/
#include "dsd.h"
#include <time.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/******************************************************************************
* Global variable definitions
*******************************************************************************/
uint8_t interleave[98] = {
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
@@ -14,111 +37,288 @@ uint8_t interleave[98] = {
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
//this is a convertion table for converting the dibit pairs into constellation points
uint8_t constellation_map[16] = {
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
static int16_t staticX[INSTATIC][INRANGE] =
{
{ 1,-3,-3,1,3,-1,-1,3 },
{ -3,1,3,-1,-1,3,1,-3 },
{ -1,3,3,-1,-3,1,1,-3 },
{ 3,-1,-3,1,1,-3,-1,3 },
{ -3,1,1,-3,-1,3,3,-1 },
{ 1,-3,-1,3,3,-1,-3,1 },
{ 3,-1,-1,3,1,-3,-3,1 },
{ -1,3,1,-3,-3,1,3,-1 }
};
//digitized dibit to OTA symbol conversion for reference
//0 = +1; 1 = +3;
//2 = -1; 3 = -3;
//finite state machine values
uint8_t fsm[64] = {
0, 8, 4, 12, 2, 10, 6, 14,
4, 12, 2, 10, 6, 14, 0, 8,
1, 9, 5, 13, 3, 11, 7, 15,
5, 13, 3, 11, 7, 15, 1, 9,
3, 11, 7, 15, 1, 9, 5, 13,
7, 15, 1, 9, 5, 13, 3, 11,
2, 10, 6, 14, 0, 8, 4, 12,
6, 14, 0, 8, 4, 12, 2, 10};
//attempt to find the surviving path, or the 'best' path available (most positions gained)
uint8_t fix_34(uint8_t * p, uint8_t state, int position)
static int16_t staticY[INSTATIC][INRANGE] =
{
int i, j, k, counter, best_p, best_v, survivors;
uint8_t temp_s, tri, t;
{ -1,3,-1,3,-3,1,-3,1 },
{ -1,3,-3,1,-3,1,-1,3 },
{ -1,3,-1,3,-3,1,-3,1 },
{ -1,3,-3,1,-3,1,-1,3 },
{ -3,1,-3,1,-1,3,-1,3 },
{ -3,1,-1,3,-1,3,-3,1 },
{ -3,1,-3,1,-1,3,-1,3 },
{ -3,1,-1,3,-1,3,-3,1 }
//status of surviving paths -- debug
int s[8];
memset (s, 0, 8*sizeof(int));
};
//assign all potentially correct points to temporary storage
uint8_t temp_p[8];
temp_p[0] = (p[position] ^ 1) & 0xF;
temp_p[1] = (p[position] ^ 3) & 0xF;
temp_p[2] = (p[position] ^ 5) & 0xF;
temp_p[3] = (p[position] ^ 7) & 0xF;
temp_p[4] = (p[position] ^ 9) & 0xF;
temp_p[5] = (p[position] ^ 11) & 0xF;
temp_p[6] = (p[position] ^ 13) & 0xF;
temp_p[7] = (p[position] ^ 15) & 0xF;
best_p = 0; //best position
best_v = 0; //best path value
for (k = 0; k < 8; k++)
{
temp_s = state;
counter = 0;
tri = 0;
for (i = position; i < 49; i++)
{
//assign temp t either as our temp_p, or next point of i
if (i == position) t = temp_p[k];
else t = p[i];
if (tri != 0xFF) //while the path survives
{
tri = 0xFF;
for (j = 0; j < 8; j++)
{
if ( fsm[(temp_s*8)+j] == t )
{
//return our tribit value and state for the next point
tri = temp_s = (uint8_t)j;
counter++;
break;
}
}
if (counter > best_p)
{
best_p = counter;
best_v = k; //if we make it further on current path, assign as best path
}
// surviving path -- made it to 49
if (i == 48) s[k] = 1;
}
}
}
//NOTE: If there are no surviving paths, then that means there is another bit/point err
//Ideally, at the end of a single or multiple fixes, there should only be one survivor, but in practice,
//I've seen up to 4 surviving paths and somehow, get a good CRC and a good LRRP or LOCN decode
//multiple survivors seems common when the starting err position is very close to 49
//tally number of surviving paths
survivors = 0;
for (k = 0; k < 8; k++)
{
if (s[k] == 1) survivors++;
}
//debug
// fprintf (stderr, "START: %d; BEST_P: %d; BEST_V: %d; Survivors: %d; Point: %d; ", position, best_p, best_v, survivors, temp_p[best_v]);
return temp_p[best_v]; //return the point value of the best path's starting point value
/**
* @brief Convert 0~7 symbols into 3-bit (0/1) bitstream
*
* @Param [in] psymbol Input decimal symbol values
* @Param [in] num Number of input symbols
* @Param [out] pbit Output 3-bit stream
*
*/
void TriBit(uint16_t *psymbol, uint16_t num, uint16_t *pbit)
{
int16_t temp, i;
for (i = 0; i < num; i++)
{
temp = psymbol[i];
*pbit++ = (temp >> 2) & 0x01;
*pbit++ = (temp >> 1) & 0x01;
*pbit++ = temp & 0x01;
}
}
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
/**
* @brief Organize the decoded 3-bit stream into 16-bit word format for output
*
* @Param [in] ptriBitDecRslt Input 3-bit decoded bitstream
* @Param [out] poutdata Output packed 16-bit words
*
*/
void TriBitToShort(uint16_t *ptriBitDecRslt, uint16_t *poutdata)
{
int i, j;
int16_t i, j, k = 0;
uint16_t bitArray[144] = { 0 };
memset(poutdata, 0, sizeof(uint16_t) * 9);
TriBit(ptriBitDecRslt, 48, bitArray);
for (i = 0; i < 9; i++)
{
for (j = 15; j >= 0; j--)
{
poutdata[i] += bitArray[k++] << j;
}
}
}
/**
* @brief Among 8 paths, select the one with the smallest metric and return its index
*
* @Param [in] pdata Array of 8 path metrics
* @return Index of the path with minimum metric
*
*/
uint16_t MinPath(uint16_t *pdata)
{
uint16_t i, j = 0;
uint16_t min = pdata[0]; // Take first value as initial minimum
for (i = 0; i < SELECTPATH; i++)
{
if (pdata[i] < min) // If current value is smaller
{
min = pdata[i]; // Update minimum
j = i;
}
}
return j; // Return index of minimum path
}
/**
* @brief From 64 paths, select the 8 paths with smallest metrics,
* and output corresponding state and input values
*
* @Param [in] parrary Array of 64 branch metrics
* @Param [out] pipath_ed Sorted 8 smallest path metrics
* @Param [out] pstate States corresponding to the 8 best paths
* @Param [out] pdata Input values corresponding to the 8 best paths
*
*/
void PathFunc(uint16_t *parrary, uint16_t *pipath_ed, uint16_t *pstate, uint16_t *pdata)
{
int16_t i, j, locT = 0, ValT = 0;
uint16_t dataT[PATH] = { 0 };
memcpy(dataT, parrary, sizeof(uint16_t)*PATH);
for (i = 0; i < INRANGE; i++)
{
ValT = dataT[0];
locT = 0;
for (j = 1; j < PATH; j++)
{
if (dataT[j] < ValT)
{
ValT = dataT[j];
locT = j;
}
}
dataT[locT] = 10000;
pipath_ed[i] = ValT;
pstate[i] = locT / 8; // quotient → state
pdata[i] = locT % 8; // remainder → input symbol
}
}
/**
* @brief Calculate path metric (Manhattan distance)
*
* @Param [in] dataAX X-coordinate of received constellation point A
* @Param [in] dataBX X-coordinate of reference constellation point B
* @Param [in] dataAY Y-coordinate of received constellation point A
* @Param [in] dataBY Y-coordinate of reference constellation point B
* @return Path metric result
*
*/
uint16_t DistanceCal(int16_t dataAX, int16_t dataBX, int16_t dataAY, int16_t dataBY)
{
uint16_t DistanceRslt = 0;
DistanceRslt = abs(dataAX - dataBX) + abs(dataAY - dataBY);
//DistanceRslt = sqrt((dataAX - dataBX)*(dataAX - dataBX) + (dataAY - dataBY)*(dataAY - dataBY));
return DistanceRslt;
}
/**
* @brief Convert 2-bit value to constellation symbol (±1, ±3)
*
* @Param [in] InBit Input 2-bit value (0~3)
* @return Constellation symbol value
*
*/
int16_t BitToSymbol(int16_t InBit)
{
switch (InBit)
{
case 0:
return 1;
case 1:
return 3;
case 2:
return -1;
case 3:
return -3;
default:
return 1;
}
}
/**
* @brief Map input symbols (0~15) to constellation points (±1, ±3) in I/Q
*
* @Param [in] pinstar Input symbol array (0~15)
* @Param [in] num Number of symbols
* @Param [out] pdataX Output I-channel (X) coordinates
* @Param [out] pdataY Output Q-channel (Y) coordinates
*
*/
void MapStar(uint16_t *pinstar, uint16_t num, int16_t *pdataX, int16_t *pdataY)
{
int16_t i, itemp;
for (i = 0; i < num; i++)
{
itemp = (pinstar[i] >> 2) & 0x03;
*pdataX++ = BitToSymbol(itemp);
itemp = pinstar[i] & 0x03;
*pdataY++ = BitToSymbol(itemp);
}
}
/**
* @brief Viterbi decoding function — selects the most likely sequence
*
* @Param [in] pindata Input symbol sequence (values 0~15)
* @Param [out] outdata Decoded output (packed 16-bit words)
*
*/
void Virterbi(uint16_t *pindata, uint16_t outdata[])
{
int16_t i, p, j = 1;
uint16_t lastDistnc[INRANGE] = { 0 }; // Branch metrics for final input (8 values)
uint16_t crntDistnc[64] = { 0 }; // All 64 possible branch metrics for current step
uint16_t DecodeOut[INDATA][INRANGE] = { {0 }}; // Possible input sequences up to current step (8 survivors)
uint16_t DecodeOutEnd[INDATA][INRANGE] = { {0 }}; // Temporary buffer for path history
uint16_t exInDistnc[INRANGE] = { 0 }; // 8 survivor path metrics
uint16_t triBitDecRslt[INDATA] = { 0 }; // Final decoded 3-bit symbols
int16_t starX[INDATA] = { 0 }, starY[INDATA] = { 0 }; // Constellation point coordinates (I/Q)
uint16_t minDistnc = 0;
uint16_t ConstelPData[INRANGE] = { 0 }; // Input symbols leading to next states
uint16_t ConstelPState[INSTATIC] = { 0 }; // Survivor states
MapStar(pindata, INDATA, starX, starY); // Map input symbols to constellation points
// Branch metrics for the first input symbol (starting from state 0)
for (i = 0; i < INRANGE; i++)
{
ConstelPData[i] = i; // Next state value
DecodeOut[0][i] = i; // Possible input symbols from state 0
exInDistnc[i] = DistanceCal(starX[0], staticX[0][i], starY[0], staticY[0][i]);
}
// Compute branch metrics for each subsequent input symbol
for (j = 1; j < INDATA - 1; j++) // for each input symbol
{
for (p = 0; p < INSTATIC; p++) // for each current survivor state
{
for (i = 0; i < INRANGE; i++) // for each possible input
{
crntDistnc[p * 8 + i] = DistanceCal(starX[j], staticX[ConstelPData[p]][i], starY[j], staticY[ConstelPData[p]][i]) + exInDistnc[p];
}
}
PathFunc(crntDistnc, exInDistnc, ConstelPState, ConstelPData); // Select 8 best paths
for (p = 0; p <= j; p++)
{
for (i = 0; i < INRANGE; i++)
{
if (p < j)
{
DecodeOutEnd[p][i] = DecodeOut[p][ConstelPState[i]];
}
else
{
DecodeOut[p][i] = ConstelPData[i];
DecodeOutEnd[p][i] = DecodeOut[p][i];
}
}
}
memcpy(DecodeOut, DecodeOutEnd, j * 8 * sizeof(int16_t));
}
// Final step — force ending in state 0
for (i = 0; i < INRANGE; i++)
{
lastDistnc[i] = DistanceCal(starX[INDATA - 1], staticX[DecodeOut[INDATA - 2][i]][0], starY[INDATA - 1], staticY[DecodeOut[INDATA - 2][i]][0]) + exInDistnc[i];
}
minDistnc = MinPath(lastDistnc); // Find best final path
for (i = 0; i < INDATA - 1; i++)
{
triBitDecRslt[i] = DecodeOutEnd[i][minDistnc];
}
TriBitToShort(triBitDecRslt, outdata);
}
uint32_t viterbi_r34 (uint8_t * input, uint8_t * output)
{
int i = 0;
uint32_t irr_err = 0; //irrecoverable errors
uint8_t deinterleaved_dibits[98];
@@ -135,80 +335,20 @@ uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
for (i = 0; i < 49; i++)
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
//convert our dibit pairs into constellation point values
uint8_t point[49];
memset (point, 0xFF, sizeof(point));
uint16_t pindata[49]; memset (pindata, 0, sizeof(pindata));
uint16_t outdata[18]; memset (outdata, 0, sizeof(outdata));
for (i = 0; i < 49; i++)
point[i] = constellation_map[nibs[i]];
for (int i = 0; i < 49; i++)
pindata[i] = (uint16_t)nibs[i];
//debug view points
// fprintf (stderr, "\n P =");
// for (i = 0; i < 49; i++)
// fprintf (stderr, " %02d", point[i]);
Virterbi(pindata, outdata);
//convert constellation points into tribit values using the FSM
uint8_t state = 0;
uint32_t tribits[49];
memset (tribits, 0xF, sizeof(tribits));
for (i = 0; i < 49; i++)
int k = 0;
for (int i = 0; i < 9; i++)
{
for (j = 0; j < 8; j++)
{
if ( fsm[(state*8)+j] == point[i] )
{
//return our tribit value and state for the next point
tribits[i] = state = (uint8_t)j;
break;
}
}
//if tribit value is greater than 7, then we have a decoding error
if (tribits[i] > 7)
{
irr_err++; //tally number of errors
//debug point, position of error, and state value
// fprintf (stderr, "\n P: %d, %d:%d; ", point[i], i, state);
//attempt to fix point by looking for the best path from here
point[i] = fix_34(point, state, i);
//Make a hard decision and flip point to fit in current state
// point[i] ^= 7; //lucky number 7 (0111) //fallback
//decrement one and resume decoding
i--;
}
output[k++] = (outdata[i] >> 8) & 0xFF;
output[k++] = (outdata[i] >> 0) & 0xFF;
}
//debug view tribits/states
// fprintf (stderr, "\n T =");
// for (i = 0; i < 49; i++)
// fprintf (stderr, " %02d", tribits[i]);
//convert tribits into a return payload
uint32_t temp = 0;
//break into chunks of 24 bit values and shuffle into 8-bit (byte) treturn values
for (i = 0; i < 6; i++)
{
temp = (tribits[(i*8)+0] << 21) + (tribits[(i*8)+1] << 18) + (tribits[(i*8)+2] << 15) + (tribits[(i*8)+3] << 12) +
(tribits[(i*8)+4] << 9) + (tribits[(i*8)+5] << 6) + (tribits[(i*8)+6] << 3) + (tribits[(i*8)+7] << 0);
treturn[(i*3)+0] = (temp >> 16) & 0xFF;
treturn[(i*3)+1] = (temp >> 8) & 0xFF;
treturn[(i*3)+2] = (temp >> 0) & 0xFF;
}
//trellis point/state err tally
// if (irr_err != 0)
// fprintf (stderr, " P_ERR = %d", irr_err);
return (0);
return irr_err; //TODO: Need to pull viterbi metrics from upper functions
}

View File

@@ -270,13 +270,12 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
{
strcat (rsp_string, "NACK - ");
if (r_type == 0) strcat (rsp_string, "Illegal Format");
if (r_type == 1) strcat (rsp_string, "Illegal Format");
if (r_type == 2) strcat (rsp_string, "Packet CRC ERR");
if (r_type == 3) strcat (rsp_string, "Memory Full");
if (r_type == 4) strcat (rsp_string, "FSN Out of Seq");
if (r_type == 5) strcat (rsp_string, "Undeliverable");
if (r_type == 6) strcat (rsp_string, "PKT Out of Seq");
if (r_type == 7) strcat (rsp_string, "Invalid User");
if (r_type == 1) strcat (rsp_string, "Packet CRC ERR");
if (r_type == 2) strcat (rsp_string, "Memory Full");
if (r_type == 3) strcat (rsp_string, "FSN Out of Seq");
if (r_type == 4) strcat (rsp_string, "Undeliverable");
if (r_type == 5) strcat (rsp_string, "PKT Out of Seq");
if (r_type == 6) strcat (rsp_string, "Invalid User");
}
if (r_class == 2) strcat (rsp_string, "SACK - Retry");
// if (r_status) strcat (rsp_string, " - %d", r_status);
@@ -293,8 +292,19 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
//Confirmed or Unconfirmed Data Packets Header
if (dpf == 2 || dpf == 3)
{
if (dpf == 2) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN %d", sap, sap_string, f, bf, poc, fsn);
if (dpf == 3) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN %d", sap, sap_string, f, bf, poc, s, ns, fsn);
uint8_t fsn_number = (fsn & 7) + 1;
char fsn_string[50];
memset(fsn_string, 0, sizeof(fsn_string));
if (fsn == 0) sprintf(fsn_string, "%s", "Unconfirmed data single fragment");
else if (fsn == 8) sprintf(fsn_string, "%s", "Confirmed data single fragment");
else if (fsn >= 9) sprintf(fsn_string, "%s", "Last confirmed data fragment with number");
else sprintf(fsn_string, "%s", "Subsequent confirmed data fragment with number");
if (dpf == 2) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN: [%X] - %s %d; ", sap, sap_string, f, bf, poc, fsn, fsn_string, fsn_number);
if (dpf == 3) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN: [%X] - %s %d; ", sap, sap_string, f, bf, poc, s, ns, fsn, fsn_string, fsn_number);
state->data_header_blocks[slot] = bf;
if (dpf == 3) state->data_conf_data[slot] = 1; //set confirmed data delivery flag for additional CRC checks, block assembly, etc.
@@ -335,7 +345,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
//The SAP found here is the actual SAP of the message (like a P25 ndary SAP, and can chain together according to ETSI)
fprintf (stderr, " - SAP %02d [%s] - MFID %02X [%s]", p_sap, sap_string, p_mfid, mfid_string);
//p_sap 1 on mfid 10 (moto) has been observed as the first block of LRRP data (unique fixed format?)
//p_sap 1 on mfid 10 MNIS Header
if (p_mfid == 0x10 && p_sap == 1)
{
//add the header to the first 10 bytes of the storage (sans this header's CRC)
@@ -365,7 +375,42 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
}
//Start Setting DMR Data Packet Encryption Variables
if (p_sap != 1 && p_mfid == 0x10)
if (p_sap != 1 && p_mfid == 0x77)
{
fprintf (stderr, "\n Vertex Standard PDU ENC Header:");
fprintf (stderr, " MFID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8));
//Unsure of how similar this is to the DMRA Enc Header, but using
//guess work and best judgment from single sample of Vertex Enhanced
//main issue is, I don't know if the 0x01 signalled is a key id (which is known to be 0x01)
//or if it is an encryption alg id (alg may have to be inferred based on what is loaded in radio)
//set to 0x100 so it won't trigger any weird flags, but still has a non-zero value to be checked later
if (state->currentslot == 0) state->dmr_so = 0x100;
else state->dmr_soR = 0x100;
if (state->currentslot == 0)
state->payload_keyid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
else state->payload_keyidR = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
fprintf (stderr, " Key ID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8));
//going to use algid of 0x7 to signal a vertex standard enc method
if (state->currentslot == 0)
state->payload_algid = 0x7;
else state->payload_algidR = 0x7;
//unknown if MI, or IV is present on this header (need the AES256 Vertex PDU to compare)
if (state->currentslot == 0)
state->payload_mi = (unsigned long long int)ConvertBitIntoBytes(&dheader_bits[48], 32);
else state->payload_miR = (unsigned long long int)ConvertBitIntoBytes(&dheader_bits[48], 32);
//reset ks start value
state->data_ks_start[slot] = 0;
}
else if (p_sap != 1 && p_mfid == 0x10)
{
//check ENC bit, assuming this is an ENC bit, or SVC OPT like thing (or could be an opcode for the rest of the extended header)
@@ -376,7 +421,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
else state->dmr_soR = 0x100;
}
fprintf (stderr, "\n PDU ENC Header:");
fprintf (stderr, "\n DMRA PDU ENC Header:");
fprintf (stderr, " MFID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8));
fprintf (stderr, " ENC: %X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[20], 4));
@@ -993,9 +1038,9 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
R = state->rkey_array[state->payload_keyidR];
//loader for aes keys
uint8_t kaes[32];
uint8_t empt[32];
uint8_t maes[16];
uint8_t kaes[32]; memset(kaes, 0, sizeof(kaes));
uint8_t empt[32]; memset(empt, 0, sizeof(empt));
uint8_t maes[16]; memset(maes, 0, sizeof(maes));
for (i = 0; i < 8; i++)
{
kaes[i+0] = ((state->rkey_array[kid+0x000]) >> (56-(i*8))) & 0xFF;
@@ -1007,6 +1052,21 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
if (memcmp(kaes, empt, sizeof(kaes)) != 0) akl = 1;
}
//if still not akl, see if there is anything manually loaded into K1, K2, K3, K4 instead
if (akl == 0)
{
for (i = 0; i < 8; i++)
{
kaes[i+0] = (state->K1) >> (56-(i*8)) & 0xFF;
kaes[i+8] = (state->K2) >> (56-(i*8)) & 0xFF;
kaes[i+16] = (state->K3) >> (56-(i*8)) & 0xFF;
kaes[i+24] = (state->K4) >> (56-(i*8)) & 0xFF;
}
//if kaes is loaded with a key, then flag on the key loaded variable
if (memcmp(kaes, empt, sizeof(kaes)) != 0) akl = 1;
}
if (R == 0 && state->R != 0) R = state->R;
//easier to manually load up rather than make a loop (RC4)
@@ -1022,19 +1082,25 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
//print alg/key and value if loaded
fprintf (stderr, "\n PDU ALG: %02X; Key ID: %02X;", alg, kid);
if (alg != 0) fprintf (stderr, " MI(32): %08llX;", mi);
if (alg != 0 && mi != 0) fprintf (stderr, " MI(32): %08llX;", mi);
if (alg == 0) fprintf (stderr, " Moto BP;");
if (alg == 1) fprintf (stderr, " RC4;");
if (alg == 2) fprintf (stderr, " DES;");
if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;");
if (alg == 7) fprintf (stderr, " VTX STD;");
if (R && alg != 0) fprintf (stderr, " Key: %010llX;", R);
//generate 128-bit IV from 32-bit MI
//expand 32-bit MI to 128-bit IV for AES mode data decryption
//we only want to do this at the moment of keystream generation
if (alg == 5)
if (alg == 4 || alg == 5)
{
//for the LFSR128d function to show the correct alg
if (state->currentslot == 0)
state->payload_algid += 0x20;
else state->payload_algidR += 0x20;
fprintf (stderr, "\n");
LFSR128d(state);
if (state->currentslot == 0)
@@ -1049,30 +1115,54 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
decrypted_pdu = 1;
}
if (alg == 5 && akl == 1) //AES-256
else if (alg == 5 && akl == 1 && mi != 0) //AES-256 OFB
{
int nblocks = (state->data_byte_ctr[slot] / 16) + 1;
aes_ofb_keystream_output (maes, kaes, ob, 2, nblocks);
decrypted_pdu = 1;
}
else if (alg == 5 && akl == 1 && mi == 0) //AES-256 ECB (CCR without an IV)
{
//void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de)
int nblocks = (state->data_byte_ctr[slot] / 16) + 0; //was +1
for (i = 0; i < nblocks; i++)
{
aes_ecb_bytewise_payload_crypt(state->dmr_pdu_sf[slot]+start, kaes, state->dmr_pdu_sf[slot]+start, 2, 0);
start += 16;
}
decrypted_pdu = 1;
}
//other algs available, not sure if others will be used in any DMRA offerings
if (alg == 2 && R != 0) //Tait DES
else if (alg == 2 && R != 0) //Tait DES
{
int nblocks = (state->data_byte_ctr[slot] / 8) + 1;
des_multi_keystream_output (mi, R, ob, 1, nblocks);
decrypted_pdu = 1;
}
if (alg == 4 && akl == 1) //AES-128
else if (alg == 4 && akl == 1 && mi != 0) //AES-128 OFB
{
int nblocks = (state->data_byte_ctr[slot] / 16) + 1;
aes_ofb_keystream_output (maes, kaes, ob, 0, nblocks);
decrypted_pdu = 1;
}
else if (alg == 4 && akl == 1 && mi == 0) //AES-128 ECB (CCR without an IV)
{
//void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de)
int nblocks = (state->data_byte_ctr[slot] / 16) + 0; //was +1
for (i = 0; i < nblocks; i++)
{
aes_ecb_bytewise_payload_crypt(state->dmr_pdu_sf[slot]+start, kaes, state->dmr_pdu_sf[slot]+start, 0, 0);
start += 16;
}
decrypted_pdu = 1;
}
//NOTE: Observed that keystream should not be applied to pad bytes or CRC
//apply keystream here, only if alg is 1 or 4 AND key is available!
//apply keystream here, only if key is available and keystream created, not ECB.
if (alg == 1 && R != 0)
{
for (i = 0; i < end; i++)
@@ -1084,15 +1174,17 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
for (i = 0; i < end; i++)
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
}
else if (alg == 4 && akl != 0)
else if (alg == 4 && akl != 0 && mi != 0)
{
for (i = 0; i < end; i++)
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
state->dmr_pdu_sf[slot][i+start] ^= ob[(i+16)%3096];
}
else if (alg == 5 && akl != 0)
else if (alg == 5 && akl != 0 && mi != 0)
{
for (i = 0; i < end; i++)
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
state->dmr_pdu_sf[slot][i+start] ^= ob[(i+16)%3096];
}
//BP key application

View File

@@ -502,6 +502,13 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
@@ -695,8 +702,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
}
else if (state->payload_algid == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 0;
hytera_enhanced_alg_refresh(state);
dmr_alg_refresh (opts, state);
}
if (state->payload_algidR >= 0x21)
@@ -706,8 +713,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
}
else if (state->payload_algidR == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 1;
hytera_enhanced_alg_refresh(state);
dmr_alg_refresh (opts, state);
}
@@ -954,6 +961,13 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
@@ -1055,10 +1069,10 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
state->currentslot = 1;
dmr_alg_refresh (opts, state);
}
else if (state->payload_algid == 0x02)
else if (state->payload_algidR == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 1;
hytera_enhanced_alg_refresh(state);
dmr_alg_refresh (opts, state);
}

View File

@@ -438,7 +438,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
uint8_t TrellisReturn[18];
memset (TrellisReturn, 0, sizeof(TrellisReturn));
IrrecoverableErrors = dmr_34(tdibits, TrellisReturn);
IrrecoverableErrors = viterbi_r34(tdibits, TrellisReturn);
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
//they may have been successfully corrected, the CRC will reveal as much

View File

@@ -72,6 +72,15 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
so ^= 0x40;
}
//Kirisun
if (*IrrecoverableErrors == 0 && CRCCorrect == 1 && fid == 0x0A) //&& (so & 0x40) == 0x40
{
//disable late entry for DMRA, Flag 3 if encrypted, 0 if not
if (so & 0x40)
opts->dmr_le = 3;
else opts->dmr_le = 0;
}
//read ahead a little to get this for the xpt flag
if (*IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68)
{
@@ -310,8 +319,8 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
}
//unknown other manufacturer or OTA ENC, etc.
//removed tait from the list, added hytera 0x08
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && is_kenwood_sc == 0)
//removed tait from the list, added hytera 0x08, added Kirisun and KW Scrambler LC
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && fid != 0x0A && is_kenwood_sc == 0)
{
if (type == 1) fprintf (stderr, "%s \n", KYEL);
if (type == 2) fprintf (stderr, "%s \n", KYEL);
@@ -551,7 +560,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
if (opts->p25_trunk == 1 && opts->trunk_tune_enc_calls == 0) //&& type != 2
{
int i, lo = 0;
uint32_t t = 0; char gm[8]; char gn[50];
uint32_t t = 0; char gm[3]; char gn[100];
//check to see if this group already exists, or has already been locked out, or is allowed
for (i = 0; i <= state->group_tally; i++)
@@ -654,6 +663,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
}
//should rework this back into the upper portion
if (fid == 0x0A) fprintf (stderr, "Kirisun ");
if (fid == 0x68) fprintf (stderr, "Hytera ");
if (is_xpt) fprintf (stderr, "XPT ");
if (fid == 0x68 && flco == 0x00)

View File

@@ -14,23 +14,6 @@ void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc,
uint8_t slot = state->currentslot;
//enforce RC4 due to missing PI header, but with valid SVC Opts
//ideally, this would be handled by VC-F single burst, but its not fully reliable compared to this
//due to multiple signalling occurring inside of it, depending on system type
if (state->M == 0x21)
{
if (slot == 0 && state->dmr_so & 0x40)
{
state->payload_algid = 0x21;
state->payload_keyid = 0xFF;
}
if (slot == 1 && state->dmr_soR & 0x40)
{
state->payload_algidR = 0x21;
state->payload_keyidR = 0xFF;
}
}
//collect our fragments and place them into storage
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);
@@ -236,6 +219,23 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s\n", KNRM);
}
if (state->payload_algid == 0x35 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
state->DMRvcL = 0;
state->payload_mi = kirisun_lfsr(state->payload_mi);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid);
fprintf (stderr, " MI(32): %08llX;", state->payload_mi);
fprintf (stderr, " Kirisun");
if (state->payload_algid == 0x36)
fprintf (stderr, " Advanced;");
else if (state->payload_algid == 0x37)
fprintf (stderr, " Universal;");
else fprintf (stderr, " Encryption;");
fprintf (stderr, "%s\n", KNRM);
}
}
if (state->currentslot == 1)
{
@@ -266,6 +266,23 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s\n", KNRM);
}
if (state->payload_algidR == 0x35 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
{
state->DMRvcR = 0;
state->payload_miR = kirisun_lfsr(state->payload_miR);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR);
fprintf (stderr, " MI(32): %08llX;", state->payload_miR);
fprintf (stderr, " Kirisun");
if (state->payload_algidR == 0x36)
fprintf (stderr, " Advanced;");
else if (state->payload_algidR == 0x37)
fprintf (stderr, " Universal;");
else fprintf (stderr, " Encryption;");
fprintf (stderr, "%s\n", KNRM);
}
}
}
@@ -390,6 +407,41 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
//NOTE: on above, I belive that we need to check by opcode as well, as a CRC3 can have multiple collisions
//so we need to exclude op/alg 0 and 3 from the check (does algID 0x03/0x23 even exist?)
//Kirisun Placeholder
if (opts->dmr_le == 3)
{
if (irr_err != 0)
{
uint32_t sbrcpl = 0;
for(i = 0; i < 32; i++)
{
sbrcpl = sbrcpl << 1;
sbrcpl |= sbrc_interleaved[i] & 1;
}
if (opts->payload == 0) fprintf (stderr, "\n");
fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
}
else if (irr_err == 0 && sbrc_hex != 0) //first batch had sbrc_hex 0x5F1, second batch had 0x5E1 instead, what changed?
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot %d", state->currentslot+1);
fprintf (stderr, " DMR LE SB Kirisun Encryption Identifier;");
fprintf (stderr, "%s ", KNRM);
if (state->currentslot == 0)
{
if (state->payload_algid == 0 && state->dmr_so & 0x40)
state->payload_algid = 0x35; //placeholder value
}
else
{
if (state->payload_algidR == 0 && state->dmr_soR & 0x40)
state->payload_algidR = 0x35; //placeholder value
}
}
}
if (opts->dmr_le == 1)
{
if (irr_err != 0)
@@ -466,7 +518,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
if (state->dmr_so & 0x40 && key != 0 && alg != 0)
{
//if we aren't forcing a particular alg or privacy key set
if (state->M == 0)
if (state->forced_alg_id == 0)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
@@ -492,7 +544,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
if (state->dmr_soR & 0x40 && key != 0 && alg != 0)
{
//if we aren't forcing a particular alg or privacy key set
if (state->M == 0)
if (state->forced_alg_id == 0)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);

View File

@@ -167,6 +167,16 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
}
//Kirisun Check VC-F for Enc Identifiers -- has bad fec, unless its loaded differently
//SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun
// if (opts->dmr_le == 3 && vc == 6)
// {
// bool kokay = 0;
// // kokay = Golay_24_12_decode(syncdata);
// unsigned long long int kirisun = (unsigned long long int)ConvertBitIntoBytes(&syncdata[0], 48);
// fprintf (stderr, "Kiri: %012llX; Golay Okay: %d; \n", kirisun, kokay);
// }
for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i];
for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40];
@@ -253,6 +263,13 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
@@ -564,6 +581,13 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);

View File

@@ -482,6 +482,34 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
sprintf (state->dmr_lrrp_gps[slot], "ETSI TMS SRC: %d; DST: %d; ", src24, dst24);
utf16_to_text(state, 1, len, input+28);
}
//Vertex Standard TMS Port
else if (port1 == 5007 && port2 == 5007)
{
//get pad amount to see where to stop parsing UTF16 text
int pad = state->data_block_poc[slot];
//sanity check
if (len > (50+pad))
len = len - pad - 50;
fprintf (stderr, "VTX STD TMS; ");
//+28, get 9 bytes for unknown "header" information
//unknown elements 0E0002000000000000, could include a len value or type (utf-16, utf-8, is07?)
//the sample has a len of 8 utf-16 chars, only way to get that is on the 2 if not byte aligned (1000)
if (opts->payload == 1)
{
// fprintf (stderr, "Len: %03d; ", len);
fprintf (stderr, "??: ");
for (int i = 0; i < 9; i++)
fprintf (stderr, "%02X", input[i+28]);
fprintf (stderr, "; Text: ");
}
sprintf (state->dmr_lrrp_gps[slot], "VTX TMS SRC: %d; DST: %d; ", src24, dst24);
utf16_to_text(state, 1, len, input+49);
}
else if (port1 == 5017 && port2 == 5017)
{
//sanity check
@@ -493,6 +521,12 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
lip_protocol_decoder(opts, state, bits);
}
//known P25 Ports
else if (port1 == 9361 && port2 == 9361)
{
sprintf (state->dmr_lrrp_gps[slot], "P25 Atlas SRC(IP): %d.%d.%d.%d; DST(IP): %d.%d.%d.%d; ",
input[12], input[13], input[14], input[15], input[16], input[17], input[18], input[19]);
fprintf (stderr, "Atlas Data Registration Server;"); //EF Johnson Atlas
}
else if (port1 == 49198 && port2 == 49198)
{
sprintf (state->dmr_lrrp_gps[slot], "P25 Tier 2 LOCN SRC(IP): %d.%d.%d.%d; DST(IP): %d.%d.%d.%d; ",
@@ -967,8 +1001,9 @@ void dmr_locn (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * DMR_P
//write to LRRP file
if (opts->lrrp_file_output == 1)
{
char * timestr = getTimeC();
char * datestr = getDateS();
char * timestr = getTimeC();
//open file by name that is supplied in the ncurses terminal, or cli
FILE * pFile; //file pointer

View File

@@ -12,7 +12,6 @@
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
{
UNUSED2(opts, CRCCorrect);
uint8_t MFID = PI_BYTE[1];
@@ -26,6 +25,57 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->last_cc_sync_time = time(NULL);
}
if (MFID == 0x0A && CRCCorrect == 1) //Kirisun
{
uint8_t so = PI_BYTE[2]; //in VLC and PI, this byte is 0x40, so thinking this could be SVC_OPT
uint8_t alg = PI_BYTE[0]; //observed 0x36 and 0x37 for Kirisun Advanced and Universal Privacy
// uint8_t kid = PI_BYTE[2]; //user info conveyed there is only 16 allotments for Key, and keys are channel specific, so no key id value
uint32_t target = ((unsigned long long int)PI_BYTE[7] << 16) | ((unsigned long long int)PI_BYTE[8] << 8) | ((unsigned long long int)PI_BYTE[9] << 0);
//TODO: Use ALG and TGT value here to produce a key id via hashing it
uint8_t hash = alg * target % 256; //more complex hash later (honestly, probably not)
//MI only appears to be 32-bit
uint32_t mi = ((uint32_t)PI_BYTE[3] << 24) | ((uint32_t)PI_BYTE[4] << 16) |
((uint32_t)PI_BYTE[5] << 8) | ((uint32_t)PI_BYTE[6] << 0);
if (state->currentslot == 0)
{
state->dmr_so = so;
state->payload_algid = alg;
state->payload_keyid = hash;
state->payload_mi = mi;
}
else
{
state->dmr_soR = so;
state->payload_algidR = alg;
state->payload_keyidR = hash;
state->payload_miR = mi;
}
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot %d", state->currentslot+1);
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08X;", alg, hash, mi);
fprintf (stderr, " Kirisun ");
if (alg == 0x36)
fprintf (stderr, "Advanced;");
else if (alg == 0x37)
fprintf (stderr, "Universal;");
else fprintf (stderr, "Encryption;");
fprintf (stderr, "%s", KNRM);
//disable late entry for DMRA, Flag 3 for future check of VC-F 48-bit region with Golay 24,12 Encoding
//SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun
//tested patent above with information provided, VC-F had a SB, but the 48-bit
//full value yielded bad Golay results, and user submitted info contradicts this patent for samples provided
//Late Entry MI does appear to work, however, and reports a good CRC for that as well,
//reverse engineered LFSR for Kirisun lines up with the late entry for 32-bit MI values.
opts->dmr_le = 3;
}
if (MFID == 0x68) //Hytera Enhanced
{
if (state->currentslot == 0)
@@ -423,13 +473,13 @@ unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len)
{
uint8_t bit = (mi[i] >> 7) & 1;
mi[i] <<= 1;
if (bit) mi[i] ^= taps[i%5];
if (bit) mi[i] ^= taps[i%len];
mi[i] |= bit;
}
unsigned long long int mi_value = 0;
for (uint8_t i = 0; i < 5; i++)
for (uint8_t i = 0; i < len; i++)
{
mi_value <<= 8;
mi_value |= mi[i];
@@ -470,4 +520,38 @@ void hytera_enhanced_alg_refresh(dsd_state * state)
if (state->currentslot == 0)
state->payload_mi = mi_value;
else state->payload_miR = mi_value;
}
}
uint32_t kirisun_lfsr(unsigned long long int mi)
{
uint32_t taps = 0xD459C4F1;
uint32_t lfsr = (uint32_t)mi;
uint32_t new_mi = 0;
for (int i = 0; i < 4; i++)
{
uint8_t byte = 0;
for (int j = 0; j < 8; j++)
{
uint32_t temp = (lfsr << 1);
uint8_t msb = (lfsr >> 31) & 1;
if (msb)
{
byte |= (1 << j);
lfsr = temp ^ taps;
}
else lfsr = temp; //was previously temp ^ 1, but that isn't a primitive
}
new_mi <<=8;
new_mi |= byte;
}
return new_mi;
}

View File

@@ -581,11 +581,15 @@ void tait_iso7_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_
for (uint8_t i = 0; i < len; i++)
{
alias[i] = (uint8_t)ConvertBitIntoBytes(&input[16+(i*7)], 7);
fprintf (stderr, "%c", alias[i]);
// fprintf (stderr, "%X,", alias[i]); //debug
if (alias[i] == 0x2C) //change a comma to a dot
alias[i] = 0x2E;
else if (alias[i] < 0x20) //change any garble / control chars to a space
alias[i] = 0x20;
fprintf (stderr, "%c", alias[i]);
}
//flag to indicate this already exists in import or group struct
@@ -794,22 +798,18 @@ void dmr_talker_alias_lc_decode (dsd_opts * opts, dsd_state * state, uint8_t slo
}
else if (char_size == 16)
{
int ptr = 0;
setlocale(LC_ALL, ""); //needed when encoded alias contains Chinese (or probably any non-roman charset that isn't default on users terminal)
for (i = 0; i < end; i++)
{
uint16_t character = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][(i*16)], 16);
char ch[2]; ch[0] = character & 0xFF; ch[1] = 0;
if (character >= 0x20 && character != 0x7F)
fprintf (stderr, "%lc", character);
else fprintf (stderr, " ");
if (character == 0)
strcat (alias_string, " ");
else if (character >= 0x20 && character <= 0xFE)
strcat (alias_string, ch);
else
strcat (alias_string, "*");
//Encode to UTF-8
ptr += utf8_encode(character, alias_string+ptr);
//debug
// fprintf (stderr, " [%04X], ", character);

View File

@@ -137,9 +137,11 @@ void openPulseInput(dsd_opts * opts)
lt.maxlength = -1;
lt.prebuf = -1;
lt.tlength = -1;
if (opts->m17encoder == 1)
opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME4", PA_STREAM_RECORD, dev, "M17 Voice Input", &cc, NULL, &lt, &err);
else opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, &lt, &err);
// if (opts->m17encoder == 1)
// opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME4", PA_STREAM_RECORD, dev, "M17 Voice Input", &cc, NULL, &lt, &err);
// else opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, &lt, &err);
opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, &lt, &err);
if (err != 0)
{
@@ -187,6 +189,14 @@ void parse_pulse_output_string (dsd_opts * opts, char * input)
}
}
#ifdef __APPLE__
// macOS does not support OSS (/dev/dsp).
// Provide a stub so callers can link while using Pulse/CoreAudio backends.
void openOSSOutput(dsd_opts *opts)
{
(void)opts;
}
#else
void openOSSOutput (dsd_opts * opts)
{
int fmt;
@@ -205,6 +215,7 @@ void openOSSOutput (dsd_opts * opts)
exit(1);
}
fmt = 0;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
{
@@ -301,6 +312,7 @@ void openOSSOutput (dsd_opts * opts)
}
}
}
#endif
void
processAudio (dsd_opts * opts, dsd_state * state)

View File

@@ -100,7 +100,7 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encL = 0;
}
}
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
@@ -126,7 +126,7 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encR = 0;
}
}
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
@@ -716,7 +716,7 @@ void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state)
encL = 0;
}
}
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3)
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
if (state->aes_key_loaded[0] == 1)
{
@@ -1131,7 +1131,7 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encL = 0;
}
}
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
@@ -1157,7 +1157,7 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encR = 0;
}
}
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
@@ -2194,6 +2194,8 @@ void agsm (dsd_opts * opts, dsd_state * state, short * input, int len)
state->aout_gainA = coeff; //store for internal use
UNUSED(avg);
}
//until analog agc is fixed, going to use a manual gain control on this

View File

@@ -218,13 +218,13 @@ void watchdog_event_history (dsd_opts * opts, dsd_state * state, uint8_t slot)
if (slot == 0 && opts->wav_out_f != NULL)
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, event_struct);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, event_struct);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
}
else if (slot == 1 && opts->wav_out_fR != NULL)
{
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, event_struct);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, event_struct);
opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0);
}
@@ -695,7 +695,7 @@ void watchdog_event_current (dsd_opts * opts, dsd_state * state, uint8_t slot)
if (state->ea_mode == 1)
{
sprintf (event_string, "%s %s %s TGT: %07d; SRC: %07d; LCN: %02d; SITE: %d:%d.%04X; %s;", datestr, timestr, sys_string, target_id, source_id, channel, sys_id1, sys_id2, sys_id3, sup_str);
sprintf (event_string, "%s %s %s TGT: %07d; SRC: %07d; LCN: %02d; SITE: %d:%d.%04X; %s; ", datestr, timestr, sys_string, target_id, source_id, channel, sys_id1, sys_id2, sys_id3, sup_str);
}
else
{

View File

@@ -18,6 +18,7 @@
#include "dsd.h"
#include "p25p1_const.h" //for imbe fr (7200)
#include "dmr_const.h" //for ambe+2 fr
#include "bp.h" //for bp key table
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
{
@@ -501,7 +502,7 @@ SNDFILE * close_wav_file(SNDFILE * wav_file)
return wav_file;
}
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, Event_History_I * event_struct)
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, char * custom_tag, Event_History_I * event_struct)
{
sf_close(wav_file);
@@ -537,11 +538,46 @@ SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename,
char new_filename[2000];
memset (new_filename, 0, sizeof(new_filename));
//parse any possible custom tags or aliasing (up to 3)
char custom_string_temp[100];
memset(custom_string_temp, 0, sizeof(custom_string_temp));
memcpy(custom_string_temp, custom_tag, sizeof(custom_string_temp));
char * token = strtok(custom_string_temp, ":");
char tag[3][25];
memset(tag, 0, sizeof(tag));
sprintf(tag[0], "%s", "");
sprintf(tag[1], "%s", "");
sprintf(tag[2], "%s", "");
for (int i = 0; i < 3; i++)
{
if (token != NULL)
{
tag[i][0] = '_';
strncpy(tag[i]+1, token, 23);
tag[i][24] = '\0';
token = strtok(NULL, ":");
//look for _TGT_, convert to strings
if (strncmp("_TGT_", tag[i], 5) == 0)
{
sprintf (tgt_str, "%s", tag[i]+5);
sprintf (src_str, "%d", source_id);
is_string = 1;
memset(tag[i], 0, sizeof(tag[i]));
sprintf(tag[i], "%s", "");
}
}
else break;
}
//check for String based TGT and SRC values (M17, YSF, DSTAR)
if (is_string == 1)
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tgt_str, src_str);
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s%s%s%s_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tag[0], tag[1], tag[2], tgt_str, src_str);
else //is a numerical value
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s_TGT_%d_SRC_%d.wav", dir, datestr, timestr, random_number, sys_str, gi_str, target_id, source_id);
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s%s%s%s_TGT_%d_SRC_%d.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tag[0], tag[1], tag[2], target_id, source_id);
if (timestr != NULL)
{
@@ -928,9 +964,12 @@ uint16_t parse_raw_user_string (char * input, uint8_t * output)
return len;
}
//DMRA late entry items
static int ambe2_counter;
static int dmra_le;
uint16_t ambe2_str_to_decode(dsd_opts * opts, dsd_state * state, char * ambe_str, uint8_t * ks, uint16_t ks_idx, uint8_t dmra, uint8_t is_enc, uint8_t ks_available)
{
UNUSED(opts);
char ambe_fr[4][24]; memset (ambe_fr, 0, sizeof(ambe_fr));
uint8_t dibit_pair = 0;
@@ -976,6 +1015,39 @@ uint16_t ambe2_str_to_decode(dsd_opts * opts, dsd_state * state, char * ambe_str
}
//if using DMRA Late Entry IV mechanics, evaluate for IV
if (dmra_le)
{
//make a copy of ambe_fr codeword 3 (MI fragment)
uint8_t c3[24]; memset (c3, 0, sizeof(c3));
for (int i = 0; i < 24; i++)
c3[i] = ambe_fr[3][i];
//force slot to 0
state->currentslot = 0;
uint8_t c3_hex = (uint64_t)ConvertBitIntoBytes(c3, 4);
//debug
// fprintf (stderr, "\n AMBE#: %02d / %d / %d; F: %X;", ambe2_counter, ambe2_counter/3, ambe2_counter%3, c3_hex);
//use div and mod 3 to set current storage fragment
state->late_entry_mi_fragment[0][(ambe2_counter/3)+1][ambe2_counter%3] = c3_hex;
//run LFSR evaluate stored codewords for matching IV or replace if not match or not set
if (ambe2_counter == 17)
{
if (state->payload_mi != 0)
{
fprintf (stderr, "\n");
LFSR(state);
}
fprintf (stderr, "\n");
dmr_late_entry_mi (opts, state);
}
}
char ambe_d[49]; memset(ambe_d, 0, sizeof(ambe_d));
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
state->errs2 = state->errs;
@@ -1052,7 +1124,6 @@ uint16_t ambe2_str_to_decode(dsd_opts * opts, dsd_state * state, char * ambe_str
uint16_t imbe_str_to_decode(dsd_opts * opts, dsd_state * state, char * imbe_str, uint8_t * ks, uint16_t ks_idx, uint8_t is_enc, uint8_t ks_available)
{
UNUSED(opts);
char imbe_fr[8][23]; memset (imbe_fr, 0, sizeof(imbe_fr));
uint8_t dibit_pair = 0;
@@ -1199,6 +1270,16 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
state->synctype = -1;
state->lastsynctype = -1;
//reset encryption variables
state->payload_mi = 0;
state->payload_algid = 0;
state->payload_keyid = 0;
if (state->keyloader == 1)
{
state->R = 0;
state->aes_key_loaded[0] = 0;
}
//watchdog for event history
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
@@ -1210,6 +1291,12 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
uint8_t ks_i[3000]; memset (ks_i, 0, sizeof(ks_i));
uint16_t ks_idx_i = 808; //keystream index value IMBE (start at 808 for out of order ESS)
int imbe_counter = 0; //count IMBE frames for when to skip 2 bytes of ks and juggle keystreams
//Used in conjunction with DMR's DMRA Late Entry IV
ambe2_counter = 0; //count AMBE+2 frames for DMRA late entry IV mechanics (static)
dmra_le = 0; //whether or not to attempt looking for the DMRA Late Entry
if (state->forced_alg_id != 0)
state->payload_mi = 0;
source_size = fread (source_str, 1, 0x100000, opts->mbe_in_f);
@@ -1293,6 +1380,18 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
state->lastsynctype = 10;
}
if (strncmp ("NXDN", str_buffer, 4) == 0)
{
//set AMBE+2 protocol here
protocol = 3;
//disable dmra (+7 on ks_idx)
is_dmra = 0;
state->synctype = 28;
state->lastsynctype = 28;
}
//open .imb or .amb file, if desired, but only after setting a synctype
if (state->synctype != -1)
{
@@ -1303,6 +1402,57 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
if (strncmp ("tag", str_buffer, 3) == 0)
{
str_buffer = strtok(NULL, "\n"); //copies to End of Line
//debug
// fprintf (stderr, " TAG: %s", str_buffer);
//NOTE: Nothing coded for SDRTrunk MBE files provides information regarding
//possible FACCH1 steals on voice frames, so keystrem index position here can
//become unsynced on some voice frames if trunking or other does FACCH1 steals
//when the system is really busy, or radio drops out. Writing code to read ahead
//would still not tell us if the FACCH1 steal was in vocoder socket 1 or 2
//NOTE: Above may become even more noticeable on DES or AES as those keystreams
//are generated for two voice superframes, and the decryption may become unsynced
//until the next VCALL_IV is read in from the MBE file.
//Check NXDN tag value, set the ks_idx to proper position
//by looking for SACCH_1, SACCH_2, SACCH_3, or SACCH_4 for scrambler
if (alg_id == 1 && protocol == 3)
{
if (strncmp ("SACCH 1", str_buffer+4, 7) == 0)
{
ks_idx = 0;
if (opts->payload == 1)
fprintf (stderr, "\n PF 1/4");
}
else if (strncmp ("SACCH 2", str_buffer+4, 7) == 0)
{
ks_idx = 49*4;
if (opts->payload == 1)
fprintf (stderr, "\n PF 2/4");
}
else if (strncmp ("SACCH 3", str_buffer+4, 7) == 0)
{
ks_idx = 49*8;
if (opts->payload == 1)
fprintf (stderr, "\n PF 3/4");
}
else if (strncmp ("SACCH 4", str_buffer+4, 7) == 0)
{
ks_idx = 49*12;
if (opts->payload == 1)
fprintf (stderr, "\n PF 4/4");
}
}
}
if (strncmp ("call_type", str_buffer, 9) == 0)
{
str_buffer = strtok(NULL, " : \""); //next value after any : "" string
@@ -1336,6 +1486,29 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
key_id = 0;
iv_hex = 0;
//NXDN Forced Scrambler
if (is_enc == 1 && state->forced_alg_id == 1 && protocol == 3)
{
alg_id = state->forced_alg_id;
//Key Load Forced Scrambler Key by TGT or SRC value
if (state->forced_alg_id == 1 && state->keyloader == 1)
{
if (state->rkey_array[target] != 0)
state->R = state->rkey_array[target];
else if (state->rkey_array[source] != 0)
state->R = state->rkey_array[source];
}
if (alg_id == 1 && state->R != 0)
{
pdu_scrambler_keystream_creation(ks, state->R, 16*49);
ks_available = 1;
}
//DES and AES still require the IV for KS creation
}
//debug set value
// fprintf (stderr, " ENC: %d;", is_enc);
@@ -1344,6 +1517,73 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
//if forcing a DMRA EP mode, turn on dmra_le and set alg here
if (state->forced_alg_id >= 0x21 && state->forced_alg_id <= 0x25)
{
is_dmra = 1;
dmra_le = 1;
is_enc = 1;
alg_id = state->forced_alg_id;
state->payload_algid = alg_id;
rc4_db = 256;
rc4_mod = 9;
//since we can't keyring on a forced alg, we can perhaps look it up
//via the TO field for the TG value, as long as it isn't greated than 0x1FFFF (upper bound of the array)
if (state->keyloader == 1 && state->lasttg != 0 && state->lasttg < 0x1FFFF && state->rkey_array[state->lasttg] != 0)
state->R = state->rkey_array[state->lasttg];
if (alg_id == 0x21 && state->R != 0 && state->payload_mi != 0)
{
//test when we don't have an encryption_mi in the .mbe file for DMR
//TODO: Handle multi keystream creation with a new function
uint8_t ks_bytes[375]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t kiv[32]; memset(kiv, 0, sizeof(kiv));
//load key into key portion of kiv
kiv[0] = ((state->R & 0xFF00000000) >> 32);
kiv[1] = ((state->R & 0xFF000000) >> 24);
kiv[2] = ((state->R & 0xFF0000) >> 16);
kiv[3] = ((state->R & 0xFF00) >> 8);
kiv[4] = ((state->R & 0xFF) >> 0);
//load IV from late entry state->payload_mi
kiv[5] = ((state->payload_mi & 0xFF000000) >> 24);
kiv[6] = ((state->payload_mi & 0xFF0000) >> 16);
kiv[7] = ((state->payload_mi & 0xFF00) >> 8);
kiv[8] = ((state->payload_mi & 0xFF) >> 0);
rc4_block_output (rc4_db, rc4_mod, 200, kiv, ks_bytes);
unpack_byte_array_into_bit_array(ks_bytes, ks, 200);
ks_available = 1;
}
}
//TODO: This
else if (state->forced_alg_id == 1) //Basic Privacy
{
//This is where the key loaded comes into play for moto vs hytera vs scrambler
if (state->K != 0)
{
uint64_t k = 0;
k = BPK[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
k <<= 1;
k += (k >> 48) & 1;
//load that into ks
for (int i = 0; i < 18*49; i++)
ks[i] = (k >> (i%49)) & 1;
}
else if (state->H != 0)
{
//Hytera BP
}
}
if (strncmp ("to", str_buffer, 2) == 0)
{
str_buffer = strtok(NULL, " : \""); //next value after any : "" string
@@ -1400,6 +1640,36 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
key_id = strtol (str_buffer, NULL, 10);
//NXDN Scrambler Setup
if (alg_id == 1 && protocol == 3)
{
state->payload_keyid = key_id;
//Scrambler Key Loading
if (state->keyloader == 1)
keyring(opts, state);
//If Scrambler Key still not available on KID
if (state->R == 0)
{
//Key Load Scrambler Key by TGT or SRC value
if (state->forced_alg_id == 1 && state->keyloader == 1)
{
if (state->rkey_array[target] != 0)
state->R = state->rkey_array[target];
else if (state->rkey_array[source] != 0)
state->R = state->rkey_array[source];
}
}
if (state->R != 0)
{
pdu_scrambler_keystream_creation(ks, state->R, 16*49);
ks_available = 1;
}
}
//debug set value
if (opts->payload == 1)
fprintf (stderr, "\n Key ID: %04X;", key_id);
@@ -1445,9 +1715,9 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
//TODO: Handle multi keystream creation with a new function
uint8_t ks_bytes[375]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t kiv[15]; memset(kiv, 0, sizeof(kiv));
uint8_t kiv[32]; memset(kiv, 0, sizeof(kiv));
//Test: Setup a simple RC4 for now (working)
//RC4 (v1 tested and working on P25, v2 should be okay now)
if ( (alg_id == 0xAA || alg_id == 0x21) && state->R != 0 )
{
@@ -1480,19 +1750,157 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
ks_available = 1;
}
} //end test
//P25 DES (v1 tested and working on P1, v2 should be okay now)
else if (alg_id == 0x81 && state->R != 0)
{
//NOTE: Regarding SDRTrunk .mbe format, the ESS Encryption Sync
//is in the correct location on P25p2, but for P25p1, the ESS
//information preceeds LDU2, but should be AFTER the LDU2 IMBE frames,
//so, for now, we utilize a reverse lfsr function (Crypthings coming in handy)
//and recover the previous LFSR and make two keystreams in order
//to properly decrypt the initial frame, and then juggle the
//keystreams in code to provide a smooth decryption session of P25p1.
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256
//Update: There is a pull request available now, and using a version value,
//we will be able to do either format (ESS out of order vs ESS in correct order)
if (protocol == 1) //Phase 1 IMBE start on 19 for DES-OFB (8 discard + 9 LCW + 2 reserved)
unpack_byte_array_into_bit_array(ks_bytes+19, ks, 256-19); //unpack starting after discard
else if (protocol == 2) //Phase 2 AMBE+2 start on 8 after discard round for DES-OFB
unpack_byte_array_into_bit_array(ks_bytes+8, ks, 256-8); //unpack starting after discard
//reverse lfsr on IV and create keystream with that as well
//due to out of order execution on P25p1 ESS sync.
if (protocol == 1 && version == 1)
{
//load the str_buffer into the IV portion of kiv (just borrowing for DES)
parse_raw_user_string(str_buffer, kiv+5);
reverse_lfsr_64_to_len (opts, kiv+5, 64);
//convert bytes back into value
iv_hex = 0;
for (int i = 0; i < 8; i++)
{
iv_hex <<= 8;
iv_hex |= kiv[i+5];
}
memset(ks_bytes, 0, sizeof(ks_bytes));
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256
unpack_byte_array_into_bit_array(ks_bytes+19, ks_i, 256-19); //unpack starting after discard
}
ks_available = 1;
}
//P25 AES (v1 and v2 tested working on P1)
else if ( (alg_id == 0x84 || alg_id == 0x89) && state->aes_key_loaded[0] == 1 )
{
uint8_t aes_key[32];
memset(aes_key, 0, sizeof(aes_key));
//Load key from A1 - A4
for (int i = 0; i < 8; i++)
{
aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
}
uint8_t aes_iv[16];
memset(aes_iv, 0, sizeof(aes_iv));
//load the str_buffer into the IV portion of aes_iv
parse_raw_user_string(str_buffer, aes_iv);
//backup copy of current IV to reverse and expand, if needed
uint8_t aes_last_iv[16];
memset(aes_last_iv, 0, sizeof(aes_last_iv));
for (int i = 0; i < 8; i++)
aes_last_iv[i] = aes_iv[i];
//Resolve the longer IV from the shorter one
lfsr_64_to_128(aes_iv);
if (alg_id == 0x89) //128, or 256
aes_ofb_keystream_output(aes_iv, aes_key, ks_bytes, 0, 16); //16*16=256
else aes_ofb_keystream_output(aes_iv, aes_key, ks_bytes, 2, 16); //16*16=256
if (protocol == 1) //Phase 1 IMBE start on 27 for AES-OFB (16 discard + 9 LCW + 2 reserved)
unpack_byte_array_into_bit_array(ks_bytes+27, ks, 256-27); //unpack starting after discard
else if (protocol == 2) //Phase 2 AMBE+2 start on 16 after discard round for AES-OFB
unpack_byte_array_into_bit_array(ks_bytes+16, ks, 256-16); //unpack starting after discard
//reverse lfsr on IV and create keystream with that as well
//due to out of order execution on P25p1 ESS sync.
if (protocol == 1 && version == 1)
{
reverse_lfsr_64_to_len (opts, aes_last_iv, 64);
//Resolve the longer IV from the shorter one
lfsr_64_to_128(aes_last_iv);
memset(ks_bytes, 0, sizeof(ks_bytes));
if (alg_id == 0x89) //128, or 256
aes_ofb_keystream_output(aes_last_iv, aes_key, ks_bytes, 0, 16); //16*16=256
else aes_ofb_keystream_output(aes_last_iv, aes_key, ks_bytes, 2, 16); //16*16=256
unpack_byte_array_into_bit_array(ks_bytes+27, ks_i, 256-27); //unpack starting after discard
}
ks_available = 1;
}
//WIP: Add handling for NXDN DES and AES256
//NOTE: This hinges on where SDRTrunk places the IV for the call,
//ideally needs to be after the SACCH4 voice frames
//NOTE: This will NOT WORK on Type-D with its 22-bit IV
//I doubt anybody will ever test that theory out here either
else if (protocol == 3 && alg_id == 2 && state->R != 0)
{
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32);
unpack_byte_array_into_bit_array(ks_bytes+8, ks, 256-8);
ambe2_counter = 0;
ks_idx = 0;
ks_available = 1;
}
else if (protocol == 3 && alg_id == 3 && state->aes_key_loaded[0] == 1)
{
uint8_t aes_key[32];
memset (aes_key, 0, sizeof(aes_key));
//Load key from A1 - A4
for (int i = 0; i < 8; i++)
{
aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
}
//Generate 128-bit IV for AES
LFSR128npdu(state); //npdu variant uses payload_mi here, and loads to aes_ivR
aes_ofb_keystream_output (state->aes_ivR, aes_key, ks_bytes, 2, 16);
unpack_byte_array_into_bit_array(ks_bytes+16, ks, 256-16);
ambe2_counter = 0;
ks_idx = 0;
ks_available = 1;
}
//Pull request: https://github.com/DSheirer/sdrtrunk/pull/2273
//Merged into SDRTrunk nightly so should be able to handle this better now
//reset ks_idx to 0
ks_idx = 0;
@@ -1516,6 +1924,9 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
imbe_counter++;
//debug
// fprintf (stderr, "\n IMBE# %02d; KS_IDX: %d; KS_IDX_I: %d; ", imbe_counter, ks_idx, ks_idx_i);
//36 hex characters on 'hex' which is the IMBE interleaved C codewords
if (version == 1)
ks_idx_i = imbe_str_to_decode(opts, state, str_buffer, ks_i, ks_idx_i, is_enc, ks_available);
@@ -1523,7 +1934,10 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
//skip LSD bits in-between these two IMBE voice frames
if (imbe_counter == 8 || imbe_counter == 17)
{
ks_idx_i += 16;
ks_idx += 16;
}
//juggle keystreams and reset the I counter (if version 1)
if (imbe_counter == 9 && version == 1)
@@ -1544,7 +1958,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
//debug
// fprintf (stderr, " # %02d; KS_IDX_I: %04d;", imbe_counter, ks_idx_i);
// fprintf (stderr, "\n IMBE# %02d; KS_IDX: %d; KS_IDX_I: %d; ", imbe_counter, ks_idx, ks_idx_i);
//debug
// if (is_enc == 1 && ks_available == 0)
@@ -1553,7 +1967,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
// fprintf (stderr, " Enc Play;");
}
else if (protocol == 2) //P25p2 AMBE
else if (protocol == 2) //P25p2 or DMR AMBE+2
{
//debug print current str_buffer
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
@@ -1567,6 +1981,47 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
// else if (is_enc == 1 && ks_available == 1)
// fprintf (stderr, " Enc Play;");
//increment AMBE+2 counter
ambe2_counter++;
//reset if over for DMR 18 AMBE+2 frames
if (dmra_le && ambe2_counter == 18)
{
ambe2_counter = 0;
ks_idx = 0;
}
}
else if (protocol == 3) //NXDN AMBE+2 (EHR)
{
//debug print current str_buffer
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
//18 hex characters on 'hex' which is the AMBE interleaved C codewords
ks_idx = ambe2_str_to_decode(opts, state, str_buffer, ks, ks_idx, is_dmra, is_enc, ks_available);
//debug
// if (is_enc == 1 && ks_available == 0)
// fprintf (stderr, " Enc Mute;");
// else if (is_enc == 1 && ks_available == 1)
// fprintf (stderr, " Enc Play;");
//increment AMBE+2 counter
ambe2_counter++;
//Really actually need the Structure Field (PF values here)
//reset if over for NXDN 16 AMBE+2 frames for Scrambler
if (alg_id == 1 && ambe2_counter == 16)
{
ambe2_counter = 0;
ks_idx = 0;
}
else if (alg_id > 1 && ambe2_counter == 32)
{
ambe2_counter = 0;
ks_idx = 0;
}
}
}

View File

@@ -187,7 +187,7 @@ dmr_filter(short sample)
short
nxdn_filter(short sample)
{
return dsd_input_filter(sample, 4);
return dsd_input_filter(sample, 2);
}
short

View File

@@ -20,8 +20,6 @@
#define NULL 0
#endif
#include "p25p1_check_nid.h"
void
printFrameInfo (dsd_opts * opts, dsd_state * state)
{
@@ -262,21 +260,10 @@ processFrame (dsd_opts * opts, dsd_state * state)
processYSF(opts, state);
return;
}
//M17
else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) ||
(state->synctype == 76) || (state->synctype == 77) || (state->synctype == 86) || (state->synctype == 87) ||
(state->synctype == 99) || (state->synctype == 98) )
//M17 STR
else if (state->synctype == 16)
{
if (state->synctype == 98 || state->synctype == 99) //preamble only
skipDibit(opts, state, 8); //skip dibits to prime the demodulator
else if (state->synctype == 16 || state->synctype == 17)
processM17STR(opts, state);
else if (state->synctype == 76 || state->synctype == 77) {} //Not available yet
// processM17BRT(opts, state); //Not available yet
else if (state->synctype == 86 || state->synctype == 87)
processM17PKT(opts, state);
else
processM17LSF(opts, state);
processM17STR(opts, state);
return;
}
//P25 P2

View File

@@ -162,7 +162,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
char synctest20[21]; //YSF
char synctest48[49]; //EDACS
char synctest8[9]; //M17
char synctest16[17]; //M17 Preamble
char modulation[8];
char *synctest_p;
char synctest_buf[10240]; //what actually is assigned to this, can't find its use anywhere?
@@ -208,9 +207,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
t = 0;
synctest10[10] = 0;
synctest[24] = 0;
synctest8[8] = 0; //M17, wasn't initialized or terminated (source of much pain and frustration in Cygwin)
synctest8[8] = 0;
synctest12[12] = 0;
synctest16[16] = 0; //M17, wasn't initialized or terminated (source of much pain and frustration in Cygwin)
synctest48[48] = 0;
synctest32[32] = 0;
synctest20[20] = 0;
@@ -381,8 +379,19 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
lbuf2[i] = lbuf[i];
}
qsort (lbuf2, t_max, sizeof (int), comp);
lmin = (lbuf2[1] + lbuf2[2] + lbuf2[3]) / 3;
lmax = (lbuf2[t_max - 3] + lbuf2[t_max - 2] + lbuf2[t_max - 1]) / 3;
//on shorter sync patterns, we need to look at the edges since we don't have many to pull from
if (t_max < 13)
{
lmin = (lbuf2[0] + lbuf2[1] + lbuf2[2]) / 3;
lmax = (lbuf2[t_max - 3] + lbuf2[t_max - 2] + lbuf2[t_max - 1]) / 3;
}
//on longer sync patterns, we can get more towards the middle and eliminate outlier values
else
{
lmin = (lbuf2[2] + lbuf2[3] + lbuf2[4]) / 3;
lmax = (lbuf2[t_max - 5] + lbuf2[t_max - 4] + lbuf2[t_max - 3]) / 3;
}
if (state->rf_mod == 1)
{
@@ -419,8 +428,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
//if using an rtl input method, do not look for sync patterns if the pwr value is lower than our 'soft squelch' level
if (opts->audio_in_type == 3 && opts->rtl_pwr < opts->rtl_squelch_level)
//if using an rtl input method, do not look for sync patterns if the rms value is lower than our 'soft squelch' level
if (opts->audio_in_type == 3 && opts->rtl_rms < opts->rtl_squelch_level)
{
if (opts->frame_nxdn48 == 1 || opts->frame_nxdn96 == 1 || opts->frame_dpmr == 1 || opts->frame_m17 == 1)
{
@@ -566,139 +575,24 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
//end YSF sync
//M17 Sync -- Just STR and LSF for now
strncpy(synctest16, (synctest_p - 15), 16);
//M17 Sync -- Just STR
strncpy(synctest8, (synctest_p - 7), 8);
if(opts->frame_m17 == 1)
{
//preambles will skip dibits in an attempt to prime the
//demodulator but not attempt any decoding
if (strcmp(synctest8, M17_PRE) == 0)
if (strcmp(synctest8, M17_STR) == 0)
{
if (opts->inverted_m17 == 0)
printFrameSync (opts, state, "+M17 STR", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 16)
{
printFrameSync (opts, state, "+M17 PREAMBLE", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->lastsynctype = 98;
fprintf (stderr, "\n");
return (98);
}
}
else if (strcmp(synctest8, M17_PIV) == 0)
{
if (opts->inverted_m17 == 1)
{
printFrameSync (opts, state, "-M17 PREAMBLE", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->lastsynctype = 99;
fprintf (stderr, "\n");
return (99);
}
}
else if (strcmp(synctest8, M17_PKT) == 0)
{
if (opts->inverted_m17 == 0)
{
printFrameSync (opts, state, "+M17 PKT", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 86 || state->lastsynctype == 8)
{
state->lastsynctype = 86;
return (86);
}
state->lastsynctype = 86;
fprintf (stderr, "\n");
}
// else //unknown, -BRT?
// {
// printFrameSync (opts, state, "-M17 BRT", synctest_pos + 1, modulation);
// state->carrier = 1;
// state->offset = synctest_pos;
// state->max = ((state->max) + lmax) / 2;
// state->min = ((state->min) + lmin) / 2;
// if (state->lastsynctype == 77)
// {
// state->lastsynctype = 77;
// return (77);
// }
// state->lastsynctype = 77;
// fprintf (stderr, "\n");
// }
}
else if (strcmp(synctest8, M17_STR) == 0)
{
if (opts->inverted_m17 == 0)
{
printFrameSync (opts, state, "+M17 STR", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 16 || state->lastsynctype == 8)
{
state->lastsynctype = 16;
return (16);
}
state->lastsynctype = 16;
fprintf (stderr, "\n");
}
else
{
printFrameSync (opts, state, "-M17 LSF", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 99)
{
state->lastsynctype = 9;
return (9);
}
state->lastsynctype = 9;
fprintf (stderr, "\n");
}
}
else if (strcmp(synctest8, M17_LSF) == 0)
{
if (opts->inverted_m17 == 1)
{
printFrameSync (opts, state, "-M17 STR", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 17 || state->lastsynctype == 9)
{
state->lastsynctype = 17;
return (17);
}
state->lastsynctype = 17;
fprintf (stderr, "\n");
}
else
{
printFrameSync (opts, state, "+M17 LSF", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 98)
{
state->lastsynctype = 8;
return (8);
}
state->lastsynctype = 8;
fprintf (stderr, "\n");
return (16);
}
state->lastsynctype = 16;
fprintf (stderr, "\n");
}
}
//end M17
@@ -1257,8 +1151,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
}
//NXDN
#ifdef NXDN_OLD_SYNC
//NXDN (previous sync, now a cmake option -DNOS=ON ..)
else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
{
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
@@ -1272,11 +1166,14 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
//debug
// fprintf (stderr, " %s ", synctest10);
if (state->lastsynctype == 28)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->last_cc_sync_time = time(NULL);
return (28);
}
@@ -1294,9 +1191,72 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
)
{
//debug
// fprintf (stderr, " %s ", synctest10);
if (state->lastsynctype == 29)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->last_cc_sync_time = time(NULL);
return (29);
}
state->lastsynctype = 29;
}
}
#else //new sync
//NXDN (positive sync only)
else if (opts->inverted_nxdn == 0 && ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) )
{
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
if (
(strcmp (synctest10, "3131331131") == 0 )
|| (strcmp (synctest10, "3331331131") == 0 )
|| (strcmp (synctest10, "3131331111") == 0 )
// || (strcmp (synctest10, "3331331111") == 0 )
// || (strcmp (synctest10, "3131311131") == 0 )
)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
//debug
// fprintf (stderr, " %s ", synctest10);
if (state->lastsynctype == 28)
{
state->last_cc_sync_time = time(NULL);
return (28);
}
state->lastsynctype = 28;
}
}
//NXDN (inverted sync only)
else if (opts->inverted_nxdn == 1 && ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) )
{
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
if (
(strcmp (synctest10, "1313113313") == 0 )
|| (strcmp (synctest10, "1113113313") == 0 )
|| (strcmp (synctest10, "1313113333") == 0 )
// || (strcmp (synctest10, "1113113333") == 0 )
// || (strcmp (synctest10, "1313133313") == 0 )
)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
//debug
// fprintf (stderr, " %s ", synctest10);
if (state->lastsynctype == 29)
{
state->last_cc_sync_time = time(NULL);
@@ -1304,7 +1264,9 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
state->lastsynctype = 29;
}
}
#endif
//Provoice Conventional -- Some False Positives due to shortened frame sync pattern, so use squelch if possible
#ifdef PVCONVENTIONAL

View File

@@ -129,8 +129,8 @@ void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input)
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
char * datestr = getDateS();
char * timestr = getTimeC();
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
@@ -247,8 +247,8 @@ void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
char * datestr = getDateS();
char * timestr = getTimeC();
int s = (int)fkph; //rounded interger format for the log report
int a = 0;
@@ -281,269 +281,146 @@ void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint
}
//restructured the Harris GPS to flow more like the DMR UDT NMEA Format when possible
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot)
//harris location on PTT (MAC) 3.100
void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot, int phase)
{
//GPS working, Time Working (old method), COG and Speed are logical on due west sample
//TODO: Fiddle/Test various ns and ew bits for proper signs in case its located elsewhere
//UPDATE: Found two contiguous bits that always work on all my accumulated samples
//the bits also place it with the 'lone' flagged bit observed prior
//I really wonder about the engineers who laid out this message
uint8_t nmea_ns = input[88]; //north/south (lat sign) //72 previously used bit
uint8_t nmea_ew = input[89]; //east/west (lon sign) //88 previously used bit
uint8_t nmea_q = input[147]; //Quality Indicator (last fix or current fix)
// uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 7); //129,7
uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 6); //this is one bit shorter than called for, but I believe it works
//NOTE: I have only seen the MAC variation of this message description, I cannot 100% confirm
//the two block LCW format is identical, but it does carry the same amount of relevant bytes
//observation shows the GPS coordinates are accurate on LCW format, but I cannot
//vouch for the COG,SPEED,TIME, EMG and GPS Quality elements when in LCW format.
//Latitude Bits
uint8_t nmea_ndeg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //Latitude Degrees
uint8_t nmea_nmin = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //Latitude Minutes
uint16_t nmea_nminf = (uint16_t)ConvertBitIntoBytes(&input[42], 14); //Latitude Fractions of Minutes
//starting at relevant information, bytes 0,1,2,3
uint16_t lat_min_dec = (uint16_t)ConvertBitIntoBytes(input+0, 16);
uint16_t lat_ns = input[16]; //0 == N, 1 == S
uint16_t lat_min_int = (uint16_t)ConvertBitIntoBytes(input+17, 7);
uint16_t lat_deg_int = (uint16_t)ConvertBitIntoBytes(input+24, 8);
//Longitude Bits
uint8_t nmea_edeg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //Longitude Degrees
uint8_t nmea_emin = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //Longitude Minutes
uint16_t nmea_eminf = (uint16_t)ConvertBitIntoBytes(&input[74], 14); //Longitude Fractions of Minutes
//starting at relevant information, bytes 4,5,6,7
uint16_t lon_min_dec = (uint16_t)ConvertBitIntoBytes(input+32, 16);
uint16_t lon_ew = input[48]; //0 == N, 1 == S
uint16_t lon_min_int = (uint16_t)ConvertBitIntoBytes(input+49, 7);
uint16_t lon_deg_int = (uint16_t)ConvertBitIntoBytes(input+56, 8);
//NOTE: Removed UTC hh.mm.ss6 format found in DMR UDT, could not make it work
//with bits already established for time and it works extremely well as is
//starting at relevant information, bytes 8,9
uint32_t time_secs_low = (uint32_t)ConvertBitIntoBytes(input+64, 16);
//Course Over Ground in Degrees (0-360)
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[135], 9) % 360; //course over ground in degrees //working on due west sample without %360
//starting at relevant information, bytes 10,11,12,13
uint16_t course_low = (uint16_t)ConvertBitIntoBytes(input+80, 8);
uint16_t speed_low = (uint16_t)ConvertBitIntoBytes(input+88, 8);
uint16_t course_high = (uint16_t)ConvertBitIntoBytes(input+96+4, 4); //says 3,2,1,0,
uint16_t speed_high = (uint16_t)ConvertBitIntoBytes(input+96+0, 4); //says 7,6,5,4
//timestamp (16-bit value w/ appended 17th bit to MSB)
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16);
rtime |= input[144] << 16; //this bit is flagged on after 0xFFFF rollover in the late afternoon
uint32_t thour = rtime / 3600;
uint32_t tmin = (rtime % 3600) / 60;
uint32_t tsec = (rtime % 3600) % 60;
//starting at relevant information, byte 14
uint32_t time_secs_high = input[104];
uint8_t status_emergency = input[105];
uint8_t gps_quality = (uint16_t)ConvertBitIntoBytes(input+106, 2);
uint8_t gps_sat_num = (uint16_t)ConvertBitIntoBytes(input+108, 4);
//lat and lon conversion
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
float latitude = 0.0f;
float longitude = 0.0f;
float m_unit = 1.0f / 60.0f;
float mm_unit = 1.0f / 600000.0f;
//start calculating things
float lat_DDMMmm = (float)lat_deg_int + ((float)lat_min_int / 60.0f) + ((float)lat_min_dec / 600000.0f);
float lon_DDMMmm = (float)lon_deg_int + ((float)lon_min_int / 60.0f) + ((float)lon_min_dec / 600000.0f);
if (lat_ns)
lat_DDMMmm *= -1.0f;
if (lon_ew)
lon_DDMMmm *= -1.0f;
//course conversion
uint32_t course = (course_high << 4) | course_low; //tenths of degrees
float course_deg = (float)course / 10.0f; //actual value
//speed conversion
uint32_t speed_knots = (speed_high << 4) | speed_low; //tenths of degrees
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
fmps = (float)nmea_speed * 0.514444; UNUSED(fmps);
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
fkph = (float)nmea_speed * 1.852f; UNUSED(fkph);
fmps = ((float)speed_knots / 10.0f) * 0.514444; UNUSED(fmps);
fmph = ((float)speed_knots / 10.0f) * 1.15078f; UNUSED(fmph);
fkph = ((float)speed_knots / 10.0f) * 1.852f; UNUSED(fkph);
//calculate decimal representation of latidude and longitude (need some samples to test)
latitude = ( (float)nmea_ndeg + ((float)nmea_nmin*m_unit) + ((float)nmea_nminf*mm_unit) );
longitude = ( (float)nmea_edeg + ((float)nmea_emin*m_unit) + ((float)nmea_eminf*mm_unit) );
//seconds since midnight as a 17-bit representation and calculation to HHMMSS format
uint32_t sec_m = (time_secs_high << 16) | time_secs_low;
uint32_t thour = sec_m / 3600;
uint32_t tmin = (sec_m % 3600) / 60;
uint32_t tsec = (sec_m % 3600) % 60;
//This is opposite (seemingly) compared to the version specified by DMR UDT NMEA, could still be different bits involved (to be tested)
// if (nmea_ns) latitude *= -1.0f; //1 is South, 0 is North
// if (nmea_ew) longitude *= -1.0f; //1 is West, 0 is East
//DMR UDT NMEA original version (working now with new bit selection 88 and 89 like how DMR UDT NMEA format specifies)
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
//is this from LCW, or MAC?
uint16_t header = (uint16_t)ConvertBitIntoBytes(&input[0], 16);
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
//print results
fprintf (stderr, "\n");
if (header == 0x2AA4)
fprintf (stderr, " SRC: %08d;", src);
else
fprintf (stderr, " VCH: %d - SRC: %08d;", slot, src);
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph);
//Speed in Knots (assuming in knots, and not in mps, or kph)
if (header != 0x2AA4) //can't yet verify this is present or accurate on LCW
if (phase == 2)
{
if (nmea_speed > 126)
fprintf (stderr, " SPD > 126 knots or %f MPH;", fmph); //using MPH here, its Harris, they're in the U.S.
fprintf (stderr, " LCH: %d;", slot);
if (src != 0)
fprintf (stderr, " SRC: %08d;", src);
else
fprintf (stderr, " SPD: %d knots; %f MPH;", nmea_speed, fmph);
fprintf (stderr, " SRC: UNK;");
if (gps_quality < 3) //GPS hardware present
{
fprintf (stderr, " GPS: %f%s, %f%s;", lat_DDMMmm, deg_glyph, lon_DDMMmm, deg_glyph);
if (gps_quality == 0)
fprintf (stderr, " Last Fix;"); //No fix still holds last values
else if (gps_quality == 1)
fprintf (stderr, " Curr Fix;");
else if (gps_quality == 2)
fprintf (stderr, " Diff Fix;"); //presumably either lat or lon only changed, not both?
fprintf (stderr, " #Sats: %d;", gps_sat_num);
}
else if (gps_quality == 3)
fprintf (stderr, " GPS: Not Enabled / No Hardware Present;");
fprintf (stderr, " COG: %f%s;", course_deg, deg_glyph);
fprintf (stderr, " SPD: %f k/h;", fkph);
if (sec_m != 0x1FFFF) // && sec_m != 0 (so says the doc, but midnight is a valid time)
fprintf (stderr, " T: %02d:%02d:%02d;", thour, tmin, tsec);
if (status_emergency)
fprintf (stderr, " Emergency;");
}
else if (phase == 1)
{
if (src != 0)
fprintf (stderr, " SRC: %08d;", src);
else
fprintf (stderr, " SRC: UNK;");
fprintf (stderr, " GPS: %f%s, %f%s;", lat_DDMMmm, deg_glyph, lon_DDMMmm, deg_glyph);
//again, can't vouch 100%, so just going to dump the GPS Coordinates and zero out other values for LRRP file
fkph = 0.0f;
course_deg = 0.0f;
}
//Course Over Ground (COG)
fprintf (stderr, " COG: %03d%s;", nmea_cog, deg_glyph); //%360
//debug speed
// fprintf (stderr, " RSPD: %02X;", nmea_speed);
//debug cog
// fprintf (stderr, " RCOG: %03X;", nmea_cog >> 0);
//UTC Time
fprintf (stderr, " T: %02d:%02d:%02d UTC;", thour, tmin, tsec);
//Fix Indicator
if (nmea_q) fprintf (stderr, " Current Fix;");
else fprintf (stderr, " Last Fix;");
//save to ncurses string
sprintf (state->dmr_embedded_gps[slot], "(%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
sprintf (state->dmr_embedded_gps[slot], "(%f%s, %f%s)", lat_DDMMmm, deg_glyph, lon_DDMMmm, deg_glyph);
//save to event history string
if (state->event_history_s[slot].Event_History_Items[0].source_id == src && src != 0)
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]);
//save to LRRP report for mapping/logging
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
if (opts->lrrp_file_output == 1 && src != 0 && gps_quality != 3)
{
char * datestr = getDate();
char * timestr = getTime();
char * datestr = getDateS();
char * timestr = getTimeC();
//rounded interger formats for the log report
int s = (int)fkph;
int a = nmea_cog;
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr ); //could switch to UTC time on PDU if desired
fprintf (pFile, "%08d\t", src);
fprintf (pFile, "%.6lf\t", latitude);
fprintf (pFile, "%.6lf\t", longitude);
fprintf (pFile, "%d\t ", s);
fprintf (pFile, "%d\t ", a);
fprintf (pFile, "\n");
fclose (pFile);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
//NOTE: There seems to be a few more octets left undecoded in this PDU
//not including the CRC obviously, unsure of their meaning, could relate
//to other GPS functions like precision, etc, unknown.
//NOTE2: All these values are just best effort based on observation and
//making map points match using logic and distance over time for speed and course
//without any documentation, any of these values may still be wrong
}
//fallback version (if desired/required)
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input)
{
uint8_t lat_sign, lat_deg, lat_min = 0;
uint8_t lon_sign, lon_deg, lon_min = 0;
uint16_t lat_mmin = 0;
uint16_t lon_mmin = 0;
//potentially in this PDU, but unverifiable without documentation or reasonable
//mathematical proof vs map points and direction of travel / distance over time (assuming the radio is facing that direction)
uint16_t rspeed = (uint16_t)ConvertBitIntoBytes(&input[136], 8); //MSB //136
rspeed = (rspeed << 8) + (uint16_t)ConvertBitIntoBytes(&input[128], 8); //LSB //128
//this works okay for example of driver driving due west at the speed limit of the road (fluke?)
uint16_t rangle = (uint16_t)ConvertBitIntoBytes(&input[120], 8); //120,8
float fspeed = (float)rspeed;
float fangle = (float)rangle;
fspeed /= 255.0f; //unit value of 1 bit
fspeed *= 1.56f; //this is based on an observation of a driver moving approx 210 meters in about 8 seconds and this makes it just under the speed limit on the road there
// fangle *= 360.0f/255.0f; //unit value of 1 bit
fangle *= 2.0f; //may exceed 360 degrees as is (use %)
int s, a = 0;
s = (int)fspeed * 3.6;
a = (int)fangle % 360;
//fix and quality indicators?
uint8_t fix = input[147]; UNUSED(fix);
uint8_t quality = (uint16_t)ConvertBitIntoBytes(&input[148], 3); UNUSED(quality);
//timestamp (16-bit value w/ appended 17th bit to MSB)
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16); //seconds since midnight since last GPS fix (whatever the radio has as internal time)
rtime |= input[144] << 16; //test appending this as bit 17 (observed this bit set after the afternoon 0xFFFF rollover)
uint32_t thour = rtime / 3600;
uint32_t tmin = (rtime % 3600) / 60;
uint32_t tsec = (rtime % 3600) % 60;
float lat_dec = 0.0f;
float lon_dec = 0.0f;
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
//This appears to be similar to the NMEA GPGGA format (DDmm.mm) but
//octets are ordered in least significant to most significant value
lat_mmin = (uint16_t)ConvertBitIntoBytes(&input[40], 16); //? bits required, but grabbing two octets
lat_min = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //6 bits required to get 60
lat_deg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //7 bits required to get 90
lat_sign = input[72]; //64
lon_mmin = (uint16_t)ConvertBitIntoBytes(&input[72], 16); //? bits required, but grabbing two octets
lon_min = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //6 bits required to get 60
lon_deg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //8 bits required to get 180
lon_sign = input[88]; //88, unsure of a correct location, but on the sample with 0 minutes, this lonely bit was flagged on
int src = 0;
if (slot == 0) src = state->lastsrc;
if (slot == 1) src = state->lastsrcR;
//calculate decimal representation (was a pain to figure out the sub minute values)
lat_dec = ( (float)lat_deg + ((float)lat_min/60.0f) + ((float)lat_mmin/600000.0f) );
lon_dec = ( (float)lon_deg + ((float)lon_min/60.0f) + ((float)lon_mmin/600000.0f) );
if (lat_sign)
lat_dec *= -1.0f;
if (lon_sign)
lon_dec *= -1.0f;
//line break
fprintf (stderr, "\n");
fprintf (stderr, " GPS: %f%s, %f%s;", lat_dec, deg_glyph, lon_dec, deg_glyph);
//gps fix time
// fprintf (stderr, " RT: %05X;", rtime); //debug
fprintf (stderr, " LTS: %02d:%02d:%02d UTC;", thour, tmin, tsec); //last time synced to GPS
//speed and direction
// fprintf (stderr, " RSPD: %04X;", rspeed); //debug
// fprintf (stderr, " RANG: %02X;", rangle); //debug
// fprintf (stderr, " MPS: %06.02f;", fspeed);
// fprintf (stderr, " KPH: %06.02f;", fspeed * 3.6f);
// fprintf (stderr, " MPH: %06.02f;", fspeed * 2.2369f);
fprintf (stderr, " DIR: %03d%s;", a, deg_glyph);
// fprintf (stderr, " FIX: %d", fix);
// fprintf (stderr, " QLT: %02d", quality);
// fprintf (stderr, " SRC: %08d;", src);
//save to array for ncurses
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", lat_dec, deg_glyph, lon_dec, deg_glyph);
//save to LRRP report for mapping/logging
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
int a = (int)course_deg;
//open file by name that is supplied in the ncurses terminal, or cli
FILE * pFile; //file pointer
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr );
fprintf (pFile, "%08d\t", src);
fprintf (pFile, "%.6lf\t", lat_dec);
fprintf (pFile, "%.6lf\t", lon_dec);
fprintf (pFile, "%.6lf\t", lat_DDMMmm);
fprintf (pFile, "%.6lf\t", lon_DDMMmm);
fprintf (pFile, "%d\t ", s);
fprintf (pFile, "%d\t ", a);
fprintf (pFile, "\n");
@@ -562,9 +439,14 @@ void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input)
}
//NOTE: Thanks to DSheirer (SDRTrunk) for helping me work out a few of the things in here
//not entirely convinced on some of these calcs (speed, angle, and TS) but sure these bits
//are actual speed/direction/timestamps judging from what the coordinates show on a map
//debugging dump of this message
// if (opts->payload == 1)
// {
// fprintf (stderr, "\n ");
// for (int i = 0; i < 14; i++)
// fprintf (stderr, "%02X ", (uint8_t)ConvertBitIntoBytes(input+(i*8), 8));
// }
}
//externalize embedded GPS - Confirmed working now on NE, NW, SE, and SW coordinates
@@ -654,8 +536,8 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
char * datestr = getDateS();
char * timestr = getTimeC();
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
@@ -762,8 +644,9 @@ void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1)
{
char * datestr = getDate();
char * timestr = getTime();
char * datestr = getDateS();
char * timestr = getTimeC();
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a");
@@ -827,6 +710,7 @@ void decode_cellocator(dsd_opts * opts, dsd_state * state, uint8_t * input, int
//will need to establish a len value for data and contents
}
void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
{
//TODO: This
@@ -835,3 +719,240 @@ void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
UNUSED(input);
UNUSED(len);
}
//WIP: Based on Denny's always brilliant reverse-engineering of GPS reports
void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src)
{
//Elevation
int16_t elevation = (int16_t)convert_bits_into_output(input+56, 16);
//Speed & Heading (in tenths)
uint16_t speed_raw = (uint16_t)convert_bits_into_output(input+74, 14);
uint16_t heading_raw = (uint16_t)convert_bits_into_output(input+92, 12);
double speed = speed_raw / 10.0; //knots or k/h
double heading = heading_raw / 10.0;
//Date
uint16_t year = (uint16_t)(convert_bits_into_output(input+136,7) + 2000);
uint8_t month = (uint8_t) convert_bits_into_output(input+143,4);
uint8_t day = (uint8_t)(convert_bits_into_output(input+147,5) + 1);
//Longitude (DDMM.mmmm format)
uint16_t lon_degmin = (uint16_t)convert_bits_into_output(input+152, 16);
uint16_t lon_frac = (uint16_t)convert_bits_into_output(input+16, 15);
uint8_t lon_hem = (uint8_t) convert_bits_into_output(input+183, 1);
double lon_minutes = (lon_degmin % 100) + (lon_frac / 10000.0);
double lon_decimal = (lon_degmin / 100) + (lon_minutes / 60.0);
double longitude = (lon_hem == 0) ? lon_decimal : -lon_decimal;
//Latitude (DDMM.mmmm format)
uint16_t lat_degmin = (uint16_t)convert_bits_into_output(input+184,16);
uint16_t lat_frac = (uint16_t)convert_bits_into_output(input+200,15);
uint8_t lat_hem = (uint8_t) convert_bits_into_output(input+215,1);
double lat_minutes = (lat_degmin % 100) + (lat_frac / 10000.0);
double lat_decimal = (lat_degmin / 100) + (lat_minutes / 60.0);
double latitude = (lat_hem == 0) ? lat_decimal : -lat_decimal;
//Time
uint8_t hour = (uint8_t) convert_bits_into_output(input+247,5);
uint8_t minute = (uint8_t) convert_bits_into_output(input+252,6);
char deg_glyph[4];
sprintf (deg_glyph, "%s", "°");
//Report
fprintf (stderr, "\n GPS: (%f%s, %f%s) ", latitude, deg_glyph, longitude, deg_glyph);
fprintf (stderr, "Speed: %lf k/h; ", speed);
fprintf (stderr, "COG: %lf; ", heading);
fprintf (stderr, "Elevation: %i; ", elevation);
fprintf (stderr, "Date: %04d/%02d/%02d ", year, month, day);
fprintf (stderr, "Time: %02d:%02d ", hour, minute);
//dump to event history
sprintf (state->event_history_s[0].Event_History_Items[0].gps_s, "(%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
uint32_t source = state->dmr_lrrp_source[0];
uint32_t target = state->dmr_lrrp_target[0];
char comp_string[500]; memset (comp_string, 0, sizeof(comp_string));
sprintf (comp_string, "GPS SRC: %d; TGT: %d;", source, target);
watchdog_event_datacall (opts, state, source, target, comp_string, 0);
//save to LRRP report for mapping/logging
if (opts->lrrp_file_output == 1 && src != 0)
{
char * datestr = getDateS();
char * timestr = getTimeC();
//rounded interger formats for the log report
int s = (int)speed;
int a = (int)heading;
//open file by name that is supplied in the ncurses terminal, or cli
FILE * pFile; //file pointer
pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr );
fprintf (pFile, "%s\t", timestr );
fprintf (pFile, "%08d\t", src);
fprintf (pFile, "%.6lf\t", latitude);
fprintf (pFile, "%.6lf\t", longitude);
fprintf (pFile, "%d\t ", s);
fprintf (pFile, "%d\t ", a);
fprintf (pFile, "\n");
fclose (pFile);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
//reset source and target from PDU storage
state->dmr_lrrp_source[0] = 0;
state->dmr_lrrp_target[0] = 0;
}
uint8_t nmea_sentence_checker(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t slot, int len)
{
//Check Start and End, and Validity w/ Checksum
int i;
uint8_t start_value = (uint8_t)ConvertBitIntoBytes(input, 8);
uint8_t end_value = 0;
uint8_t checksum_calc = 0;
uint8_t checksum_ext = 0;
char checksum_str[3];
memset(checksum_str, 0, sizeof(checksum_str));
uint8_t valid = 0;
if (start_value == '$' || start_value == '!')
{
//debug
// fprintf (stderr, "Start Okay! ");
for (i = 1; i < len; i++)
{
uint8_t value = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8);
if (value == '*')
{
end_value = value;
checksum_str[0] = (uint8_t)ConvertBitIntoBytes(input+(i*8)+8, 8);
checksum_str[1] = (uint8_t)ConvertBitIntoBytes(input+(i*8)+16, 8);
checksum_str[2] = 0;
sscanf (checksum_str, "%hhX", &checksum_ext);
break;
}
else if (value >= 0x20 && value < 0x7F)
{
checksum_calc ^= value;
continue;
}
else break; //anything else that isn't ascii range values
}
//debug
// if (end_value == '*')
// fprintf (stderr, "Checksum Present! ");
if (checksum_ext == checksum_calc)
{
// fprintf (stderr, "Checksum Okay! (%02X / %02X)", checksum_calc, checksum_ext);
valid = 1;
}
else
{
// fprintf (stderr, "Checksum Err! (%02X / %02X) ", checksum_calc, checksum_ext);
valid = 0;
}
}
//dump, if valid
memset(state->event_history_s[slot].Event_History_Items[0].text_message, 0, sizeof(state->event_history_s[slot].Event_History_Items[0].text_message));
if (valid)
{
uint8_t ascii = 0;
uint16_t crlf = 0;
for (i = 0; i < len; i++)
{
ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8);
crlf = (uint16_t)ConvertBitIntoBytes(input+(i*8), 16);
if (ascii >= 0x20 && ascii < 0x7F)
{
fprintf (stderr, "%c", ascii);
state->event_history_s[slot].Event_History_Items[0].text_message[i] = ascii;
}
else if (crlf == 0x0D0A) //NMEA Sentences end with 0x0D 0x0A (CR/LF)
{
fprintf (stderr, "%s", " ");
state->event_history_s[slot].Event_History_Items[0].text_message[i+0] = 0x20; //Space
state->event_history_s[slot].Event_History_Items[0].text_message[i+1] = 0x20; //Space
i+=2;
break;
}
else break; //anything else that isn't ascii range values
}
//check for supplimentary string i.e., $PP25
ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8);
if (crlf == 0x0D0A && ascii == '$')
{
for (i = i; i < len; i++)
{
ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8);
crlf = (uint16_t)ConvertBitIntoBytes(input+(i*8), 16);
if (ascii >= 0x20 && ascii < 0x7F)
{
fprintf (stderr, "%c", ascii);
state->event_history_s[slot].Event_History_Items[0].text_message[i] = ascii;
}
else if (crlf == 0x0D0A) //Supplimentary Sentences end with 0x0D 0x0A (CR/LF)
{
state->event_history_s[slot].Event_History_Items[0].text_message[i] = 0x20;
break;
}
else break; //anything else that isn't ascii range values
}
}
//debug
// fprintf(stderr, "\n %s", state->event_history_s[slot].Event_History_Items[0].text_message);
//dump to event history
state->event_history_s[slot].Event_History_Items[0].text_message[i] = '\0';
uint32_t source = state->dmr_lrrp_source[slot];
uint32_t target = state->dmr_lrrp_target[slot];
char comp_string[500]; memset (comp_string, 0, sizeof(comp_string));
sprintf (comp_string, "NMEA SRC: %d; TGT: %d;", source, target);
watchdog_event_datacall (opts, state, source, target, comp_string, slot);
}
else
{
if (start_value != '$' && start_value != '!')
fprintf (stderr, " Not an NMEA Sentence Structure;");
else if ( (start_value == '$' || start_value == '!') && end_value != '*')
fprintf (stderr, " Possible NMEA Sentence, Missing Ending *;");
else if ( start_value != '$' && start_value != '!' && end_value == '*')
fprintf (stderr, " Possible NMEA Sentence, Missing Starting $ or !;");
else if (checksum_calc != checksum_ext)
fprintf (stderr, " NMEA Checksum Error (%02X / %02X);", checksum_calc, checksum_ext);
}
//TODO: Convert to LRRP data and report / GPS on Event History
state->dmr_lrrp_source[slot] = 0;
state->dmr_lrrp_target[slot] = 0;
return valid;
}

View File

@@ -7,26 +7,31 @@ 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) {
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
UNUSED(group_number);
int i = 0;
while (fgets(buffer, BSIZE, fp)) {
unsigned 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) {
while (field)
{
if (field_count == 0)
{
@@ -34,23 +39,38 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
state->group_array[i].groupNumber = atol(field);
fprintf (stderr, "%ld, ", state->group_array[i].groupNumber);
}
if (field_count == 1)
else if (field_count == 1)
{
strcpy(state->group_array[i].groupMode, field);
// strcpy(state->group_array[i].groupMode, field);
strncpy(state->group_array[i].groupMode, field, 2); //only copy two letters max A, B, D, DE
fprintf (stderr, "%s, ", state->group_array[i].groupMode);
}
if (field_count == 2)
else if (field_count == 2)
{
strcpy(state->group_array[i].groupName, field);
// strcpy(state->group_array[i].groupName, field);
strncpy(state->group_array[i].groupName, field, 98); //this should also fix potential issues observed in past when there is no comma after the name
uint16_t len = strlen((const char*)state->group_array[i].groupName);
//remove any potential linebreaks at this point
if (state->group_array[i].groupName[len-1] == '\n')
{
// fprintf(stderr, " ** LEN: %d; ** ", len); //debug
state->group_array[i].groupName[len-1] = '\0';
}
fprintf (stderr, "%s ", state->group_array[i].groupName);
}
field = strtok(NULL, ",");
field_count++;
}
fprintf (stderr, "\n");
i++;
state->group_tally++;
}
fclose(fp);
return 0;

View File

@@ -234,7 +234,7 @@
state->dibit_buf_p = state->dibit_buf + 200;
memset (state->dibit_buf, 0, sizeof (int) * 200);
//dmr buffer
state->dmr_payload_p = state->dibit_buf + 200;
state->dmr_payload_p = state->dmr_payload_buf + 200;
memset (state->dmr_payload_buf, 0, sizeof (int) * 200);
memset (state->dmr_stereo_payload, 1, sizeof(int) * 144);
//dmr buffer end
@@ -417,7 +417,7 @@
// state->p25_vc_freq[1] = 0;
//new nxdn stuff
state->nxdn_part_of_frame = 0;
state->nxdn_part_of_frame = 3;
state->nxdn_ran = 0;
state->nxdn_sf = 0;
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
@@ -538,7 +538,6 @@
//M17 Storage
memset (state->m17_lsf, 0, sizeof(state->m17_lsf));
memset (state->m17_pkt, 0, sizeof(state->m17_pkt));
state->m17_pbc_ct = 0;
state->m17_str_dt = 9;
state->m17_dst = 0;
@@ -552,13 +551,18 @@
state->m17_enc = 0;
state->m17_enc_st = 0;
memset(state->m17_meta, 0, sizeof(state->m17_meta));
//misc str storage
// sprintf (state->str50a, "%s", "");
// memset (state->str50b, 0, 50*sizeof(char));
// memset (state->str50c, 0, 50*sizeof(char));
// memset (state->m17sms, 0, 800*sizeof(char));
// sprintf (state->m17dat, "%s", "");
memset(state->m17_aes_iv, 0, sizeof(state->m17_aes_iv));
memset(state->m17_text_string, 0, sizeof(state->m17_text_string));
memset(state->m17_gnss_string, 0, sizeof(state->m17_gnss_string));
memset(state->m17_data_string, 0, sizeof(state->m17_data_string));
memset(state->m17_meta_string, 0, sizeof(state->m17_meta_string));
sprintf (state->m17_text_string, "%s", "");
sprintf (state->m17_gnss_string, "%s", "");
sprintf (state->m17_data_string, "%s", "");
sprintf (state->m17_meta_string, "%s", "");
state->m17_viterbi_err = 0.0f;
//set float temp buffer to baseline
memset (state->audio_out_temp_buf, 0.0f, sizeof(state->audio_out_temp_buf));
@@ -632,6 +636,7 @@
opts->wav_out_file[0] = 0;
opts->wav_out_fileR[0] = 0;
opts->wav_out_file_raw[0] = 0;
opts->wav_custom_tag[0] = 0;
opts->symbol_out_file[0] = 0;
opts->lrrp_out_file[0] = 0;
opts->event_out_file[0] = 0;
@@ -674,29 +679,26 @@
opts->uvquality = 3;
opts->inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
opts->inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
opts->inverted_m17 = 0; //samples from M17_Education seem to all be positive polarity (same from m17-tools programs)
opts->inverted_nxdn = 0; //only seek pos by default to reduce false sync pattern detections
opts->mod_threshold = 26;
opts->ssize = 128; //36 default, max is 128, much cleaner data decodes on Phase 2 cqpsk at max
opts->msize = 1024; //15 default, max is 1024, much cleaner data decodes on Phase 2 cqpsk at max
opts->playfiles = 0;
opts->m17encoder = 0;
opts->m17encoderbrt = 0;
opts->m17encoderpkt = 0;
opts->m17decoderip = 0;
opts->delay = 0;
opts->use_cosine_filter = 1;
opts->unmute_encrypted_p25 = 0;
//all RTL user options -- enabled AGC by default due to weak signal related issues
opts->rtl_dev_index = 0; //choose which device we want by index number
opts->rtl_gain_value = 0; //mid value, 0 - AGC - 0 to 49 acceptable values
opts->rtl_squelch_level = 10000; //default scaled for rtl_pwr (RMS^2 proxy); ~100 RMS equivalent
opts->rtl_gain_actual = -100; //mirror dongle.gain actual values -100 is AUTO_GAIN, else nearest_gain(dongle.dev, dongle.gain);
opts->rtl_squelch_level = 100; //100 by default, but only affects NXDN and dPMR during framesync test, compared to RMS value
opts->rtl_volume_multiplier = 2; //sample multiplier; This multiplies the sample value to produce a higher 'inlvl' for the demodulator
opts->rtl_udp_port = 0; //set UDP port for RTL remote -- 0 by default, will be making this optional for some external/legacy use cases (edacs-fm, etc)
opts->rtl_bandwidth = 12; //default is 12, reverted back to normal on this (no inherent benefit)
opts->rtlsdr_ppm_error = 0; //initialize ppm with 0 value;
opts->rtlsdr_center_freq = 850000000; //set to an initial value (if user is using a channel map, then they won't need to specify anything other than -i rtl if desired)
opts->rtl_started = 0;
opts->rtl_pwr = 0; // mean power approximation level on rtl input signal
opts->rtl_rms = 0; //root means square power level on rtl input signal
//end RTL user options
opts->pulse_raw_rate_in = 48000;
opts->pulse_raw_rate_out = 48000;//
@@ -762,12 +764,6 @@
opts->udp_portno = 23456; //default port, same os OP25's sockaudio.py
sprintf (opts->udp_hostname, "%s", "127.0.0.1");
//M17 UDP Port and hostname
opts->m17_use_ip = 0; //if enabled, open UDP and broadcast IP frame
opts->m17_portno = 17000; //default is 17000
opts->m17_udp_sock = 0; //actual UDP socket for M17 to send to
sprintf (opts->m17_hostname, "%s", "127.0.0.1");
//tcp input options
opts->tcp_sockfd = 0;
opts->tcp_portno = 7355; //default favored by SDR++
@@ -1041,7 +1037,7 @@
state->K2 = 0;
state->K3 = 0;
state->K4 = 0;
state->M = 0; //force key priority over settings from fid/so
state->forced_alg_id = 0; //force key priority over settings from fid/so
state->dmr_stereo = 0; //1, or 0?
state->dmrburstL = 17; //initialize at higher value than possible
@@ -1064,6 +1060,9 @@
state->tyt_ap = 0;
state->tyt_bp = 0;
state->tyt_ep = 0;
state->retevis_ap = 0;
state->baofeng_ap = 0;
state->csi_ee = 0;
state->ken_sc = 0;
state->any_bp = 0;
@@ -1189,7 +1188,9 @@
//trunking
memset (state->trunk_lcn_freq, 0, sizeof(state->trunk_lcn_freq));
memset (state->trunk_chan_map, 0, sizeof(state->trunk_chan_map));
state->group_tally = 0;
//allocate max 24-bit address w/ memory with zero fill (termination) for end of strings, import will only copy 98, leaving the terminator intact
state->group_array = calloc(16778000, sizeof(groupinfo));
state->group_tally = 0; //is an unsigned int
state->lcn_freq_count = 0; //number of frequncies imported as an enumerated lcn list
state->lcn_freq_roll = 0; //needs reset if sync is found?
state->last_cc_sync_time = time(NULL);
@@ -1205,7 +1206,7 @@
state->tg_hold = 0;
//new nxdn stuff
state->nxdn_part_of_frame = 0;
state->nxdn_part_of_frame = 3;
state->nxdn_ran = 0;
state->nxdn_sf = 0;
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
@@ -1213,6 +1214,7 @@
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
state->nxdn_alias_block_number = 0;
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
state->nxdn_pn95_seed = 228; //default value
//site/srv/cch info
state->nxdn_location_site_code = 0;
@@ -1311,22 +1313,16 @@
//M17 Storage
memset (state->m17_lsf, 0, sizeof(state->m17_lsf));
memset (state->m17_pkt, 0, sizeof(state->m17_pkt));
state->m17_pbc_ct = 0;
state->m17_str_dt = 9;
//misc str storage
// sprintf (state->str50a, "%s", "");
memset (state->str50b, 0, 50*sizeof(char));
memset (state->str50c, 0, 50*sizeof(char));
memset (state->m17sms, 0, 800*sizeof(char));
sprintf (state->m17dat, "%s", "");
state->m17_dst = 0;
state->m17_src = 0;
state->m17_can = 0; //can value that was decoded from signal
state->m17_can_en = -1; //can value supplied to the encoding side
state->m17_rate = 48000; //sampling rate for audio input
state->m17_vox = 0; //vox mode enabled on M17 encoder
memset(state->m17_dst_csd, 0, sizeof(state->m17_dst_csd));
memset(state->m17_src_csd, 0, sizeof(state->m17_src_csd));
sprintf (state->m17_dst_str, "%s", "");
@@ -1334,10 +1330,19 @@
state->m17_enc = 0;
state->m17_enc_st = 0;
state->m17encoder_tx = 0;
state->m17encoder_eot = 0;
memset(state->m17_meta, 0, sizeof(state->m17_meta));
memset(state->m17_aes_iv, 0, sizeof(state->m17_aes_iv));
memset(state->m17_text_string, 0, sizeof(state->m17_text_string));
memset(state->m17_gnss_string, 0, sizeof(state->m17_gnss_string));
memset(state->m17_data_string, 0, sizeof(state->m17_data_string));
memset(state->m17_meta_string, 0, sizeof(state->m17_meta_string));
sprintf (state->m17_text_string, "%s", "");
sprintf (state->m17_gnss_string, "%s", "");
sprintf (state->m17_data_string, "%s", "");
sprintf (state->m17_meta_string, "%s", "");
state->m17_viterbi_err = 0.0f;
#ifdef USE_CODEC2
state->codec2_3200 = codec2_create(CODEC2_MODE_3200);
@@ -1395,8 +1400,6 @@
printf (" rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)\n");
printf (" tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)\n");
printf (" tcp:192.168.7.5:7355 for custom address and port \n");
printf (" m17udp for M17 UDP/IP socket bind input (default host 127.0.0.1; default port 17000)\n");
printf (" m17udp:192.168.7.8:17001 for M17 UDP/IP bind input (Binding Address and Port\n");
printf (" filename.bin for OP25/FME capture bin files\n");
printf (" filename.wav for 48K/1 wav files (SDR++, GQRX)\n");
printf (" filename.wav -s 96000 for 96K/1 wav files (DSDPlus)\n");
@@ -1419,8 +1422,6 @@
printf (" null for no audio output\n");
printf (" udp for UDP socket blaster output (default host 127.0.0.1; default port 23456)\n");
printf (" udp:192.168.7.8:23470 for UDP socket blaster output (Target Address and Port\n");
printf (" m17udp for M17 UDP/IP socket blaster output (default host 127.0.0.1; default port 17000)\n");
printf (" m17udp:192.168.7.8:17001 for M17 UDP/IP blaster output (Target Address and Port\n");
printf (" -d <dir> Create mbe data files, use this directory (TDMA version is experimental)\n");
printf (" -r <files> Read/Play saved mbe data from file(s)\n");
printf (" -g <float> Audio Digital Output Gain (Default: 0 = Auto; )\n");
@@ -1436,6 +1437,8 @@
printf (" -w <file> Output synthesized speech to a single static .wav file. (Do not use with -P Per Call Switch)\n");
printf (" -P Enable Per Call WAV file saving. (Do not use with -w filename.wav single wav file switch)\n");
printf (" (Per Call works with everything now and doesn't require ncurses terminal!)\n");
printf (" -j <string> Custom Tag(s), colon delimited, to append to Per Call decoded .wav files.\n");
printf (" -j TGT_LAPD:TAG_FIRE:FREQ_851987500 (Total 3 Tags, Maximum Letter Length = 100)\n");
printf (" -a Enable Call Alert Beep\n");
printf (" (Warning! Might be annoying.)\n");
printf (" -J <file> Specify Filename for Event Log Output.\n");
@@ -1461,34 +1464,12 @@
printf (" gain <num> RTL-SDR Device Gain (0-49)(default = 0; Hardware AGC recommended)\n");
printf (" ppm <num> RTL-SDR PPM Error (default = 0)\n");
printf (" bw <num> RTL-SDR Bandwidth kHz (default = 12)(4, 6, 8, 12, 16, 24) \n");
printf (" sq <num> RTL-SDR Squelch Level vs PWR Value (Optional)\n");
printf (" sq <num> RTL-SDR Squelch Level vs RMS Value (Optional)\n");
// printf (" udp <num> RTL-SDR Legacy UDP Remote Port (Optional -- External Use Only)\n"); //NOTE: This is still available as an option in the ncurses menu
printf (" vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)\n");
printf (" Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T\n");
printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2\n");
printf ("\n");
printf ("Encoder options:\n");
printf (" -fZ M17 Stream Voice Encoder\n");
printf (" Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i pulse -6 m17signal.wav -8 -N 2> m17encoderlog.txt\n");
printf (" Run M17 Encoding, listening to pulse audio server, with internal decode/playback and output to 48k/1 wav file\n");
printf ("\n");
printf (" Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i tcp -o pulse -8 -N 2> m17encoderlog.txt\n");
printf (" Run M17 Encoding, listening to default tcp input, without internal decode/playback and output to 48k/1 analog output device\n");
printf ("\n");
printf (" -fP M17 Packet Encoder\n");
printf (" Example: dsd-fme -fP -M M17:9:DSD-FME:LWVMOBILE -6 m17pkt.wav -8 -S 'Hello World'\n");
printf ("\n");
printf (" -fB M17 BERT Encoder\n");
printf (" Example: dsd-fme -fB -M M17:9:DSD-FME:LWVMOBILE -6 m17bert.wav -8\n");
printf ("\n");
printf (" -M M17 Encoding User Configuration String: M17:CAN:SRC:DST:INPUT_RATE:VOX (see examples above).\n");
printf (" CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.\n");
printf (" BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'\n");
printf (" Input Rate Default is 48000; Use Multiples of 8000 up to 48000.\n");
printf (" VOX Enabled on 1; (Default = 0)\n");
printf (" Values not entered into the M17: string are set to default values.\n");
// printf (" -S M17 Packet Encoder SMS String: No more than 772 chars, use single quotations (see example above).\n");
// printf (" -S M17 Stream Encoder SMS String: No more than 48 chars, activates 1600 voice (best if broken into six 8 char chunks).\n");
printf ("Decoder options:\n");
printf (" -fa Auto Detection\n");
printf (" -fA Passive Analog Audio Monitor\n");
@@ -1500,7 +1481,6 @@
printf (" -fx Decode only X2-TDMA\n");
printf (" -fy Decode only YSF\n");
printf (" -fz Decode only M17*\n");
printf (" -fU Decode only M17 UDP/IP Frame***\n");
printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n");
printf (" -fn Decode only NXDN96* (12.5 kHz)\n");
printf (" -fp Decode only ProVoice*\n");
@@ -1516,15 +1496,20 @@
printf (" -xx Expect non-inverted X2-TDMA signal\n");
printf (" -xr Expect inverted DMR signal\n");
printf (" -xd Expect inverted ICOM dPMR signal\n");
printf (" -xz Expect inverted M17 signal\n");
#ifdef NXDN_OLD_SYNC
//
#else
printf (" -xn Expect inverted NXDN/DCR/IDAS signal\n");
#endif
printf ("\n");
printf (" * denotes frame types that cannot be auto-detected.\n");
printf (" ** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.\n");
printf (" *** configure UDP Input with -i m17:127.0.0.1:17000 \n");
printf ("\n");
printf ("Advanced Decoder options:\n");
printf (" -X <hex> Manually Set P2 Parameters (WACN, SYSID, CC/NAC)\n");
printf (" (-X BEE00ABC123)\n");
printf (" -_ <dec> Manually Set NXDN PN95 Dibit Scrambler Seed (1-511)\n");
printf (" (-* 261)\n");
printf (" -D <dec> Manually Set TIII DMR Location Area n bit len (0-10)(10 max)\n");
printf (" (Value defaults to max n bit value for site model size)\n");
printf (" (Setting 0 will show full Site ID, no area/subarea)\n");
@@ -1539,18 +1524,17 @@
printf (" Use this feature to allow MAC_SIGNAL even if CRC errors.\n");
printf (" -F Relax DMR RAS/CRC CSBK/DATA Pass/Fail\n");
printf (" Enabling on some systems could lead to bad channel assignments/site data decoding if bad or marginal signal\n");
printf (" -F Relax M17 LSF/PKT CRC Error Checking\n");
printf ("\n");
printf (" -b <dec> Manually Enter Basic Privacy Key (Decimal Value of Key Number)\n");
printf (" (NOTE: This used to be the 'K' option! \n");
printf (" (NOTE: This used to be the 'K' option!) \n");
printf ("\n");
printf (" -H <hex> Manually Enter Hytera 10/32/64 Char Basic Privacy Hex Key (see example below)\n");
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
printf (" -H 0B57935150 \n");
printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n");
printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n");
printf ("\n"); //may move to using the rkey_array with an offset for additional key values
printf (" -H <hex> Manually Enter AES-128 or AES-256 Hex Key (see example below)\n");
printf ("\n");
printf (" -H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)\n");
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n");
printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n");
@@ -1561,14 +1545,28 @@
printf (" \n");
printf (" -2 <hex> Manually Enter and Enforce TYT 16-bit BP Key Value (DMR) (Hex Value) \n");
printf (" \n");
printf (" -! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) AP Hex Key (see example below)\n");
printf (" -! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) 128 or 256 bit AP Hex Key (see example below)\n");
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
printf (" -! '736B9A9C5645288B 243AD5CB8701EF8A' \n");
printf (" -! '1111111111111111 1111111111111111 1111111111111111 1111111111111111' \n");
printf (" \n");
printf (" -@ <hex> Manually Enter and Enforce Retevis Advanced Privacy (RC2) 128 or 256 bit AP Hex Key (see example below)\n");
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
printf (" -@ '736B9A9C5645288B 243AD5CB8701EF8A' \n");
printf (" -@ '1122334455667788 99AABBCCDDEEFF11 1122334455667788 99AABBCCDDEEFF11' \n");
printf (" \n");
printf (" -5 <hex> Manually Enter and Enforce TYT Enhanced Privacy (AES-128) EP Hex Key (see example below)\n");
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
printf (" -5 '736B9A9C5645288B 243AD5CB8701EF8A' \n");
printf (" \n");
printf (" -+ <hex> Manually Enter and Enforce Baofeng Advanced Privacy (PC5-128 or PC5-256) AP Hex Key (see example below)\n");
printf (" Encapulate in Single Quotation Marks; Space every 16 chars.\n");
printf (" -+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC' \n");
printf (" -+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC EF01EF01EF01EF01 EF01EF01EF01EF01' \n");
printf (" \n");
printf (" -^ <hex> Manually Enter and Enforce Connect Systems 72-bit (9-byte) Extended Encryption Hex Key (see example below)\n");
printf (" -^ 3BBE782C0430008271\n");
printf (" \n");
printf (" -9 <dec> Manually Enter and Enforce Kenwood 15-bit Scrambler Key Value (DMR) (Dec Value) \n");
printf (" \n");
printf (" -A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) \n");
@@ -1580,6 +1578,8 @@
printf (" -S 49:123456789ABC80\n");
printf (" For Example, enter 49-bit Keystream (MBP 70) as:\n");
printf (" -S 49:ED0AED4AED4AED4A\n");
printf (" For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:\n");
printf (" -S 49:11AA22BB33CC44\n");
printf (" \n");
printf (" -k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.\n");
printf (" Only supports NXDN, DMR Basic Privacy (decimal value). \n");
@@ -1592,6 +1592,8 @@
printf (" \n");
printf (" -0 Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR) \n");
printf (" \n");
printf (" -M <hex> Force Entered Alg ID Value over Missing PI header/LE Encryption Identifiers (DMR) \n");
printf (" \n");
printf (" -3 Disable DMR Late Entry Encryption Identifiers (VC6 Single Burst) \n");
printf (" Note: Disable this if false positives on Voice ENC occur. \n");
printf ("\n");
@@ -1644,8 +1646,8 @@
open_rtlsdr_stream(opts);
opts->rtl_started = 1; //set here so ncurses terminal doesn't attempt to open it again
// #ifdef __arm__
// fprintf (stderr, "WARNING: PWR Function is Disabled on ARM Devices (Raspberry Pi) due to High CPU use. \n");
// fprintf (stderr, "PWR/Squelch Functionality for NXDN, dPMR, EDACS Analog, M17 and Raw Audio Monitor are unavailable and these modes will not function properly. \n");
// fprintf (stderr, "WARNING: RMS Function is Disabled on ARM Devices (Raspberry Pi) due to High CPU use. \n");
// fprintf (stderr, "RMS/Squelch Functionality for NXDN, dPMR, EDACS Analog, M17 and Raw Audio Monitor are unavailable and these modes will not function properly. \n");
// if (opts->monitor_input_audio == 1) opts->monitor_input_audio = 0;
// opts->rtl_squelch_level = 0;
// #endif
@@ -1777,10 +1779,10 @@
if (opts->static_wav_file == 0)
{
if (opts->wav_out_f != NULL)
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
if (opts->wav_out_fR != NULL)
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
}
else if (opts->static_wav_file == 1)
@@ -1819,9 +1821,6 @@
if (opts->udp_sockfdA)
close (opts->udp_sockfdA);
if (opts->m17_udp_sock)
close (opts->m17_udp_sock);
//close MBE out files
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
@@ -1895,11 +1894,11 @@
init_audio_filters(&state); //audio filters
init_rrc_filter_memory(); //initialize input filtering
InitAllFecFunction();
CNXDNConvolution_init();
exitflag = 0;
while ((c = getopt (argc, argv, "~yhaepPqs:t:v:z:i:o:d:c:g:n:w:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:YK:b:H:X:NQ:WrlZTF!:01:2:345:6:7:89:Ek:I:J:O")) != -1)
while ((c = getopt (argc, argv, "~yhaepPqs:t:v:z:i:o:d:c:g:n:w:B:C:R:f:m:u:x:A:S:G:D:L:V:U:YK:b:H:X:M:NQ:WrlZTF@:!:01:2:345:6:^:7:8_:9:Ek:I:J:O+:j:")) != -1)
{
switch (c)
@@ -1913,14 +1912,21 @@
opts.call_alert = 1;
break;
//Free'd up switches include: j, O,
//
//NOTE: Don't use -*, found it exhibits unusual behavior, was setting
//itself to -3 for some reason, may be reading contents of directory
//make sure to put a colon : after each if they need an argument
//or remove colon if no argument required
//NOTE: The 'K' option for single BP key has been swapped to 'b'
//'K' is now used for hexidecimal key.csv imports
case 'j': //custom wav tags appended to per-call wav files
strncpy(opts.wav_custom_tag, optarg, 99);
opts.wav_custom_tag[99] = '\0';
fprintf(stderr, "Appending Tags %s to per-call wav files. \n", opts.wav_custom_tag);
break;
//this is a debug option hidden from users, but use it to replay .bin files on loop
case '~':
@@ -1934,37 +1940,28 @@
exit(0);
break;
//Specify M17 encoder User Data (CAN, DST, SRC values)
//NOTE: Disabled QPSK settings by borrowing these switches (nobody probalby used them anyways)
case 'M':
strncpy(state.m17dat, optarg, 49);
state.m17dat[49] = '\0';
break;
// //Specify M17 encoder SMS Message (truncates at 772)
// case 'S':
// strncpy(state.m17sms, optarg, 772);
// state.m17sms[772] = '\0';
// state.m17_str_dt = 3; //flip this so that STR encoder knows to use 1600 voice + data
// break;
//specify TG Hold value
case 'I':
sscanf (optarg, "%d", &state.tg_hold);
fprintf (stderr, "TG Hold set to %d \n", state.tg_hold);
break;
//experimental audio monitoring
//raw audio monitoring
case '8':
opts.monitor_input_audio = 1;
fprintf (stderr,"Experimental Raw Analog Source Monitoring Enabled (Pulse Audio Only!)\n");
fprintf (stderr,"Raw Analog Source Monitoring Enabled.\n");
break;
//rc4 enforcement on DMR (due to missing the PI header)
case '0':
state.M = 0x21;
state.forced_alg_id = 0x21;
fprintf (stderr,"Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR)\n");
break;
case 'M':
sscanf (optarg, "%hhX", &state.forced_alg_id);
fprintf (stderr,"Force DMR ALG ID 0x%02X over Missing PI header/LE Encryption Identifiers (DMR)\n", state.forced_alg_id);
break;
//load single rc4/des key
case '1':
@@ -1987,12 +1984,24 @@
case '!':
tyt_ap_pc4_keystream_creation(&state, optarg);
break;
//get user Retevis AP Key and Force Its application
case '@':
retevis_rc2_keystream_creation(&state, optarg);
break;
//get user TYT EP Key and Force Its application
case '5':
tyt_ep_aes_keystream_creation(&state, optarg);
break;
//get user CSI EE Key and Force Its application
case '^':
parse_raw_user_string(optarg, state.static_ks_bits[0]);
fprintf (stderr,"DMR CS Extended 72-bit Key with Forced Application\n");
state.csi_ee = 1;
break;
//get user Kenwood DMR Scrambler Key and Force Its application
case '9':
ken_dmr_scrambler_keystream_creation(&state, optarg);
@@ -2003,6 +2012,11 @@
anytone_bp_keystream_creation(&state, optarg);
break;
//get user Baofeng AP Key and Force Its application
case '+':
baofeng_ap_pc5_keystream_creation(&state, optarg);
break;
//Straight KS Generation
case 'S':
straight_mod_xor_keystream_creation(&state, optarg);
@@ -2205,7 +2219,15 @@
if (state.R > 0x7FFF) state.R = 0x7FFF;
//disable keyloader in case user tries to use this and it at the same time
state.keyloader = 0;
fprintf (stderr, "NXDN Scrambler Key set to: %05lld; \n", state.R);
break;
case '_': //was *
sscanf (optarg, "%hu", &state.nxdn_pn95_seed);
if (state.nxdn_pn95_seed > 0x1FF) state.nxdn_pn95_seed = 0x1FF;
else if (state.nxdn_pn95_seed == 0) state.nxdn_pn95_seed = 228;
fprintf (stderr, "NXDN PN95 Seed Value set to: %03d; \n", state.nxdn_pn95_seed);
break;
case 'H':
//new handling for 10/32/64 Char Key
@@ -2237,7 +2259,7 @@
break;
case '4':
state.M = 1;
state.forced_alg_id = 1;
fprintf (stderr,"Force Privacy Key over Encryption Identifiers (DMR BP and NXDN Scrambler) \n");
break;
@@ -2753,6 +2775,11 @@
sprintf (opts.output_name, "NXDN48");
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n");
#ifdef NXDN_OLD_SYNC
//
#else
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
#endif
}
else if (optarg[0] == 'y')
{
@@ -2888,6 +2915,7 @@
// opts.setmod_bw = 12000; //causing issues
sprintf (opts.output_name, "NXDN96");
fprintf (stderr,"Decoding only NXDN 9600 baud frames.\n");
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
}
else if (optarg[0] == 'r')
{
@@ -2974,40 +3002,6 @@
//disable RRC filter for now
opts.use_cosine_filter = 0;
}
else if (optarg[0] == 'Z') //Captial Z to Run the M17 STR encoder
{
opts.m17encoder = 1;
opts.pulse_digi_rate_out = 48000;
opts.pulse_digi_out_channels = 1;
//filters disabled by default, use ncurses VBN switches
opts.use_lpf = 0;
opts.use_hpf = 0;
opts.use_pbf = 0;
opts.dmr_stereo = 0;
sprintf (opts.output_name, "M17 Encoder");
}
else if (optarg[0] == 'B') //Captial B to Run the M17 BRT encoder
{
opts.m17encoderbrt = 1;
opts.pulse_digi_rate_out = 48000;
opts.pulse_digi_out_channels = 1;
sprintf (opts.output_name, "M17 BERT");
}
else if (optarg[0] == 'P') //Captial P to Run the M17 PKT encoder
{
opts.m17encoderpkt = 1;
opts.pulse_digi_rate_out = 48000;
opts.pulse_digi_out_channels = 1;
sprintf (opts.output_name, "M17 Packet");
}
else if (optarg[0] == 'U') //Captial U to Run the M17 UDP IPF decoder
{
opts.m17decoderip = 1;
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 1;
sprintf (opts.output_name, "M17 IP Frame");
fprintf (stderr, "Decoding M17 UDP/IP Frames.\n");
}
break;
//don't mess with the modulations unless you really need to
case 'm':
@@ -3107,10 +3101,14 @@
opts.inverted_dpmr = 1;
fprintf (stderr, "Expecting inverted ICOM dPMR signals.\n");
}
else if (optarg[0] == 'z')
else if (optarg[0] == 'n')
{
opts.inverted_m17 = 1;
fprintf (stderr, "Expecting inverted M17 signals.\n");
opts.inverted_nxdn = 1;
#ifdef NXDN_OLD_SYNC
//
#else
fprintf (stderr, "Expecting inverted NXDN/DCR/IDAS signals.\n");
#endif
}
break;
@@ -3123,7 +3121,6 @@
opts.dmr_stereo = 0;
state.dmr_stereo = 0;
sprintf (opts.output_name, "MBE Playback");
state.optind = optind;
break;
case 'l':
opts.use_cosine_filter = 0;
@@ -3133,54 +3130,16 @@
exit (0);
}
}
// Set optind after getopt completes so -r works regardless of argument order
if (opts.playfiles == 1)
state.optind = optind;
if (opts.resume > 0)
{
openSerial (&opts, &state);
}
if((strncmp(opts.audio_in_dev, "m17udp", 6) == 0)) //M17 UDP Socket Input
{
fprintf (stderr, "M17 UDP IP Frame Input: ");
char * curr;
curr = strtok(opts.audio_in_dev, ":"); //should be 'm17'
if (curr != NULL) ; //continue
else goto M17ENDIN; //end early with preset values
curr = strtok(NULL, ":"); //host address
if (curr != NULL) strncpy (opts.m17_hostname, curr, 1023);
curr = strtok(NULL, ":"); //host port
if (curr != NULL) opts.m17_portno = atoi (curr);
M17ENDIN:
fprintf (stderr, "%s:", opts.m17_hostname);
fprintf (stderr, "%d \n", opts.m17_portno);
}
if((strncmp(opts.audio_out_dev, "m17udp", 6) == 0)) //M17 UDP Socket Output
{
fprintf (stderr, "M17 UDP IP Frame Output: ");
char * curr;
curr = strtok(opts.audio_out_dev, ":"); //should be 'm17'
if (curr != NULL) ; //continue
else goto M17ENDOUT; //end early with preset values
curr = strtok(NULL, ":"); //host address
if (curr != NULL) strncpy (opts.m17_hostname, curr, 1023);
curr = strtok(NULL, ":"); //host port
if (curr != NULL) opts.m17_portno = atoi (curr);
M17ENDOUT:
fprintf (stderr, "%s:", opts.m17_hostname);
fprintf (stderr, "%d \n", opts.m17_portno);
opts.m17_use_ip = 1; //tell the encoder to open the socket
opts.audio_out_type = 9; //set to null device
}
if((strncmp(opts.audio_in_dev, "tcp", 3) == 0)) //tcp socket input from SDR++ and others
{
fprintf (stderr, "TCP Direct Link: ");
@@ -3469,18 +3428,23 @@
//to a variable config, it cannot be the input as well
#ifndef __APPLE__
if((strncmp(opts.audio_in_dev, "/dev/audio", 10) == 0))
{
sprintf (opts.audio_in_dev, "%s", "/dev/dsp");
fprintf (stderr, "Switching to /dev/dsp.\n");
}
#endif
#ifndef __APPLE__
if((strncmp(opts.audio_in_dev, "pa", 2) == 0))
{
sprintf (opts.audio_in_dev, "%s", "/dev/dsp");
fprintf (stderr, "Switching to /dev/dsp.\n");
}
#endif
#ifndef __APPLE__
speed = 48000; //hardset to 48000
if((strncmp(opts.audio_in_dev, "/dev/dsp", 8) == 0))
{
@@ -3514,24 +3478,31 @@
opts.audio_in_type = 5; //5 will become OSS input type
}
#endif
//check for OSS output
#ifndef __APPLE__
if((strncmp(opts.audio_out_dev, "/dev/audio", 10) == 0))
{
sprintf (opts.audio_out_dev, "%s", "/dev/dsp");
fprintf (stderr, "Switching to /dev/dsp.\n");
}
#endif
#ifndef __APPLE__
if((strncmp(opts.audio_out_dev, "pa", 2) == 0))
{
sprintf (opts.audio_out_dev, "%s", "/dev/dsp");
fprintf (stderr, "Switching to /dev/dsp.\n");
}
#endif
//this will only open OSS output if its listed as a type
//changed to this so I could call it freely inside of ncurses terminal
#ifndef __APPLE__
if (opts.playfiles == 0)
openOSSOutput(&opts);
#endif
if (opts.playfiles == 1)
{
@@ -3543,8 +3514,10 @@
opts.pulse_digi_out_channels = 1;
if (opts.audio_out_type == 0)
openPulseOutput(&opts);
#ifndef __APPLE__
else if((strncmp(opts.audio_out_dev, "/dev/dsp", 8) == 0))
openOSSOutput(&opts); //open after split == 1 so it will open and playback at the proper speed
#endif
}
//this particular if-elseif-else could be rewritten to be a lot neater and simpler
@@ -3578,134 +3551,12 @@
signal(SIGINT, handler);
signal(SIGTERM, handler);
//read in any user supplied M17 CAN and/or CSD data
if((strncmp(state.m17dat, "M17", 3) == 0))
{
//read in values
//string in format of M17:can:src_csd:dst_csd:input_rate
//check and capatalize any letters in the CSD
for (int i = 0; state.m17dat[i]!='\0'; i++)
{
if(state.m17dat[i] >= 'a' && state.m17dat[i] <= 'z')
state.m17dat[i] = state.m17dat[i] -32;
}
fprintf (stderr, "M17 User Data: ");
char * curr;
// if((strncmp(state.m17dat, "M17", 3) == 0))
// goto M17END;
curr = strtok(state.m17dat, ":"); //should be 'M17'
if (curr != NULL) ; //continue
else goto M17END; //end early with preset values
curr = strtok(NULL, ":"); //m17 channel access number
if (curr != NULL)
state.m17_can_en = atoi(curr);
curr = strtok(NULL, ":"); //m17 src address
if (curr != NULL)
{
strncpy (state.str50c, curr, 9); //only read first 9
state.str50c[9] = '\0';
}
curr = strtok(NULL, ":"); //m17 dst address
if (curr != NULL)
{
strncpy (state.str50b, curr, 9); //only read first 9
state.str50b[9] = '\0';
}
curr = strtok(NULL, ":"); //m17 input audio rate
if (curr != NULL)
state.m17_rate = atoi(curr);
curr = strtok(NULL, ":"); //m17 vox enable
if (curr != NULL)
state.m17_vox = atoi(curr);
// curr = strtok(NULL, ":"); //moved to in and out methods
// if (curr != NULL)
// opts.m17_use_ip = atoi(curr);
M17END: ; //do nothing
//check to make sure can value is no greater than 15 (4 bit value)
if (state.m17_can_en > 15)
state.m17_can_en = 15;
//if vox is greater than 1, assume user meant 'yes' and set to one
if (state.m17_vox > 1)
state.m17_vox = 1;
//debug print m17dat string
// fprintf (stderr, " %s;", state.m17dat);
fprintf (stderr, " M17:%d:%s:%s:%d;", state.m17_can_en, state.str50c, state.str50b, state.m17_rate);
if (state.m17_vox == 1) fprintf (stderr, "VOX;");
fprintf (stderr, "\n");
}
if (opts.playfiles == 1)
{
playMbeFiles (&opts, &state, argc, argv);
}
else if (opts.m17encoder == 1)
{
//disable RRC filter for now
opts.use_cosine_filter = 0;
opts.pulse_digi_rate_out = 8000;
//open any inputs, if not alread opened, OSS input and output already handled
if (opts.audio_in_type == 0) openPulseInput(&opts);
#ifdef USE_RTLSDR
else if(opts.audio_in_type == 3)
{
open_rtlsdr_stream(&opts);
opts.rtl_started = 1;
}
#endif
//open any outputs, if not already opened
if (opts.audio_out_type == 0) openPulseOutput(&opts);
//All input and output now opened and handled correctly, so let's not break things by tweaking
encodeM17STR(&opts, &state);
}
else if (opts.m17encoderbrt == 1)
{
opts.pulse_digi_rate_out = 8000;
//open any outputs, if not already opened
if (opts.audio_out_type == 0) openPulseOutput(&opts);
encodeM17BRT(&opts, &state);
}
else if (opts.m17encoderpkt == 1)
{
//disable RRC filter for now
opts.use_cosine_filter = 0;
opts.pulse_digi_rate_out = 8000;
//open any outputs, if not already opened
if (opts.audio_out_type == 0) openPulseOutput(&opts);
encodeM17PKT(&opts, &state);
}
else if (opts.m17decoderip == 1)
{
opts.pulse_digi_rate_out = 8000;
//open any outputs, if not already opened
if (opts.audio_out_type == 0) openPulseOutput(&opts);
processM17IPF(&opts, &state);
}
else
{
@@ -3717,5 +3568,5 @@
return (0);
}
//Tag: 2025
//Tag: 2026

View File

@@ -18,6 +18,8 @@
#include "dsd.h"
#include "bp.h"
#include "pc4.h"
#include "rc2.h"
#include "pc5.h"
//NOTE: This set of functions will be reorganized and simplified (hopefully) or at least
//a more logical flow will be established to jive with the new audio handling
@@ -91,7 +93,7 @@ void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
//static wav file only, handled by playSynthesizedVoiceMS
//NOTE: if using -o null, playSynthesizedVoiceMS will not write to static wav file
//Per call will work, but will end up with a single file with no meta info
if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1)
if (opts->wav_out_f != NULL && (opts->dmr_stereo_wav == 1 || opts->static_wav_file == 1))
{
writeSynthesizedVoice (opts, state);
}
@@ -139,7 +141,7 @@ void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
//static wav file only, handled by playSynthesizedVoiceMS
//NOTE: if using -o null, playSynthesizedVoiceMS will not write to static wav file
//Per call will work, but will end up with a single file with no meta info
if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1)
if (opts->wav_out_f != NULL && (opts->dmr_stereo_wav == 1 || opts->static_wav_file == 1))
{
writeSynthesizedVoice (opts, state);
}
@@ -173,6 +175,30 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
unsigned long long int k;
int x;
//keystream and silence conditional items
uint64_t silence = 0xF801A99F8CE080; //AMBE+2 default silence vector expressed as a 56-bit hex value.
char ambe_silence[49];
for (i = 0; i < 49; i++)
ambe_silence[i] = (silence >> (55-i)) & 1;
//zeroed ambe_d can look like 00000000000580(w/ DMRA IV), 000D2C00000000,
//or 00000000000000 look at +24 position for 20 bits (fits all these scenarios)
char zeroes[49]; memset(zeroes, 0, sizeof(zeroes));
size_t zeroes_threshold = 20;
if (state->forced_alg_id > 1 && state->forced_alg_id != 0x16) //1 and 0x16 is saved for BP stuff, so anything higher than that (Kirisun, requires svc opts set as well)
{
if (state->currentslot == 0 && state->dmr_so & 0x40)
{
state->payload_algid = state->forced_alg_id;
state->payload_keyid = 0xFF;
}
if (state->currentslot == 1 && state->dmr_soR & 0x40)
{
state->payload_algidR = state->forced_alg_id;
state->payload_keyidR = 0xFF;
}
}
//these conditions should ensure no clashing with the BP/HBP/Scrambler key loading machanisms already coded in
if (state->currentslot == 0 && state->payload_algid != 0 && state->payload_algid != 0x80 && state->keyloader == 1)
keyring (opts, state);
@@ -448,7 +474,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
state->errs2 += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
if ( (state->nxdn_cipher_type == 0x01 && state->R != 0) ||
(state->M == 1 && state->R > 0) )
(state->forced_alg_id == 1 && state->R > 0) )
{
if (state->payload_miN == 0)
@@ -509,7 +535,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
state->bit_counterL = 0;
//unpack the octets into a bit-wise keystream
unpack_byte_array_into_bit_array(state->ks_octetL+8, state->ks_bitstreamL, 15*8);
unpack_byte_array_into_bit_array(state->ks_octetL+16, state->ks_bitstreamL, 14*16);
//reset flag to 0
state->nxdn_new_iv = 0;
@@ -520,7 +546,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->bit_counterL > (1568-49))
state->bit_counterL = (1568-49);
//Keystream creation is currently inside of the NXDN_decode_VCALL_IV function
//Keystream Application (no +7 discard like DMRA)
for (i = 0; i < 49; i++)
ambe_d[i] ^= state->ks_bitstreamL[state->bit_counterL++];
@@ -556,7 +582,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
//EXPERIMENTAL!!
//load basic privacy key number from array by the tg value (if not forced)
//currently only Moto BP and Hytera 10 Char BP
if (state->M == 0 && state->payload_algid == 0)
if (state->forced_alg_id == 0 && state->payload_algid == 0)
{
//see if we need to hash a value larger than 16-bits
hash = state->lasttg & 0xFFFFFF;
@@ -582,7 +608,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->M == 1) )
(state->K > 0 && state->forced_alg_id == 1) )
{
k = BPK[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
@@ -594,7 +620,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->K1 > 0 && state->M == 1) )
(state->K1 > 0 && state->forced_alg_id == 1) )
{
int pos = 0;
@@ -648,10 +674,15 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
pos = state->DMRvcL * 49;
for(i = 0; i < 49; i++)
if (memcmp(ambe_d, ambe_silence, 49) == 0)
pos += 49;
else
{
ambe_d[i] ^= pN[pos];
pos++;
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
}
state->DMRvcL++;
}
@@ -712,6 +743,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
(state->currentslot == 0 && state->payload_algid == 0x25 && state->aes_key_loaded[0] == 1 ) || //DMR AES256
(state->currentslot == 0 && state->payload_algid == 0x89 && state->aes_key_loaded[0] == 1 ) || //P25 AES128
(state->currentslot == 0 && state->payload_algid == 0x84 && state->aes_key_loaded[0] == 1 ) || //P25 AES256
(state->currentslot == 0 && state->payload_algid == 0x36 && state->aes_key_loaded[0] == 1 ) || //KIRI ADV
(state->currentslot == 0 && state->payload_algid == 0x37 && state->aes_key_loaded[0] == 1 ) || //KIRI UNI
(state->currentslot == 0 && state->payload_algid == 0x02 && state->R != 0 ) ) //HYT ENHANCED
{
@@ -746,6 +779,16 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
n = 0;
hytera_enhanced_rc4_setup(opts, state, state->R, state->payload_mi);
}
if (state->payload_algid == 0x36)
{
n = 0;
kirisun_adv_keystream_creation(state);
}
if (state->payload_algid == 0x37)
{
n = 0;
kirisun_uni_keystream_creation(state);
}
//Load Keystream Octet Bytes directly into keystream array //TODO: Convert to unpack function
for (i = 0; i < 9 * 16; i++) //9 rounds at 16 octets
@@ -759,18 +802,17 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
}
//now we do the bit by bit xor depending on the frame and position of the state bit counter
//run 6 instead of 7 so we can just do bit 49 outside of loop to keep extra bits overloading the array
z = 0;
for (i = 0; i < 6; i++)
//skip keystream if silence or zeroes (some CCR), else apply keystream directly and increment counter
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->bit_counterL += 49;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->bit_counterL += 49;
else
{
for (j = 0; j < 8; j++)
ambe_d[z++] ^= state->ks_bitstreamL[state->bit_counterL++];
for (i = 0; i < 49; i++)
ambe_d[i] ^= state->ks_bitstreamL[state->bit_counterL++];
}
//last bit
ambe_d[48] ^= state->ks_bitstreamL[state->bit_counterL++];
//skip the next 7 bits of the array (if not Hytera Enhanced)
if(state->payload_algid != 0x02)
state->bit_counterL += 7;
@@ -808,15 +850,24 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
//that may occur on some systems that preempt VC6 voice for a RC opportuninity (TXI)
//this occurs because we are supposed to either have a a 'repeat' frame, or 'silent' frame play
//due to the error, but the keystream application makes it random 'pfft pop' sound instead
if (state->errs < 3)
rc4_voice_decrypt(state->dropL, 9, 7, rckey, cipher, plain);
else memcpy (plain, cipher, sizeof(plain));
state->dropL += 7;
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->dropL += 7;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->dropL += 7;
else
{
if (state->errs < 3)
rc4_voice_decrypt(state->dropL, 9, 7, rckey, cipher, plain);
else memcpy (plain, cipher, sizeof(plain));
//unpack deciphered plain array back into ambe_d bit array
memset (ambe_d, 0, 49*sizeof(char));
unpack_ambe(plain, ambe_d);
state->dropL += 7;
//unpack deciphered plain array back into ambe_d bit array
memset (ambe_d, 0, 49*sizeof(char));
unpack_ambe(plain, ambe_d);
}
}
@@ -857,49 +908,115 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
unpack_ambe(plain, ambe_d);
}
//DMR Retevis AP, Either Slot (static single key'd enforced KS)
if (state->retevis_ap == 1)
{
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
else
{
uint8_t frame1_cipher[49];
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
decrypt_rc2((CryptoContext *)state->rc2_context, frame1_cipher);
memset (ambe_d, 0, 49*sizeof(char));
for (int i = 0; i < 49; i++) ambe_d[i] = frame1_cipher[i];
}
}
//DMR TYT AP, Either Slot (static single key'd enforced KS)
if (state->tyt_ap == 1)
{
short frame1_cipher[49];
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
decrypt_frame_49(frame1_cipher);
memset (ambe_d, 0, 49*sizeof(char));
for (int i = 0; i < 49; i++) ambe_d[i] = ctx.bits[i];
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
else
{
short frame1_cipher[49];
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
decrypt_frame_49(frame1_cipher);
memset (ambe_d, 0, 49*sizeof(char));
for (int i = 0; i < 49; i++) ambe_d[i] = ctx.bits[i];
}
}
//DMR BAOFENG AP, Either Slot (static single key'd enforced KS)
if (state->baofeng_ap == 1)
{
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
else
{
short frame1_cipher[49];
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
decrypt_frame_49_pc5(frame1_cipher);
memset (ambe_d, 0, 49*sizeof(char));
for (int i = 0; i < 49; i++) ambe_d[i] = ctxpc5.bits[i];
}
}
//DMR TYT EP, Either Slot (static single key'd enforced KS)
if (state->tyt_ep == 1)
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 1);
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
else
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 1);
}
}
//DMR Kenwood Scrambler, Either Slot (static single key'd enforced KS) //should probably break this up, but this is a test for now
//DMR Kenwood Scrambler, Either Slot (static single key'd enforced KS)
if (state->ken_sc == 1)
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%882] & 1); //Yikes!
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 49;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->static_ks_counter[state->currentslot] += 49;
else
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%882] & 1); //Yikes!
}
}
//DMR Anytone BP, Either Slot (static single key'd enforced KS)
if (state->any_bp == 1)
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%16] & 1); //Yikes!
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 49;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->static_ks_counter[state->currentslot] += 49;
else
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%16] & 1); //Yikes!
}
}
//Generic Straight Static Keystream
if (state->straight_ks == 1)
{
//disable enc identifiers, if present
state->dmr_so = 0;
state->payload_algid = 0;
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 49;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->static_ks_counter[state->currentslot] += 49;
else
{
//disable enc identifiers, if present
state->dmr_so = 0;
state->payload_algid = 0;
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
}
}
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
@@ -932,7 +1049,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
//EXPERIMENTAL!!
//load basic privacy key number from array by the tg value (if not forced)
//currently only Moto BP and Hytera 10 Char BP
if (state->M == 0 && state->payload_algidR == 0)
if (state->forced_alg_id == 0 && state->payload_algidR == 0)
{
//see if we need to hash a value larger than 16-bits
hash = state->lasttgR & 0xFFFFFF;
@@ -958,7 +1075,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
(state->K > 0 && state->M == 1) )
(state->K > 0 && state->forced_alg_id == 1) )
{
k = BPK[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
@@ -970,7 +1087,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
(state->K1 > 0 && state->M == 1))
(state->K1 > 0 && state->forced_alg_id == 1))
{
int pos = 0;
@@ -1024,10 +1141,15 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
pos = state->DMRvcR * 49;
for(i = 0; i < 49; i++)
if (memcmp(ambe_d, ambe_silence, 49) == 0)
pos += 49;
else
{
ambe_d[i] ^= pN[pos];
pos++;
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
}
state->DMRvcR++;
}
@@ -1088,6 +1210,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
(state->currentslot == 1 && state->payload_algidR == 0x25 && state->aes_key_loaded[1] == 1 ) || //DMR AES256
(state->currentslot == 1 && state->payload_algidR == 0x89 && state->aes_key_loaded[1] == 1 ) || //P25 AES128
(state->currentslot == 1 && state->payload_algidR == 0x84 && state->aes_key_loaded[1] == 1 ) || //P25 AES256
(state->currentslot == 1 && state->payload_algidR == 0x36 && state->aes_key_loaded[1] == 1 ) || //KIRI ADV
(state->currentslot == 1 && state->payload_algidR == 0x37 && state->aes_key_loaded[1] == 1 ) || //KIRI UNI
(state->currentslot == 1 && state->payload_algidR == 0x02 && state->RR != 0 ) ) //HYT ENHANCED
{
@@ -1122,6 +1246,16 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
n = 0;
hytera_enhanced_rc4_setup(opts, state, state->RR, state->payload_miR);
}
if (state->payload_algidR == 0x36)
{
n = 0;
kirisun_adv_keystream_creation(state);
}
if (state->payload_algidR == 0x37)
{
n = 0;
kirisun_uni_keystream_creation(state);
}
//Load Keystream Octet Bytes directly into keystream array
for (i = 0; i < 9 * 16; i++) //9 rounds at 16 octets //TODO: Convert to unpack function
@@ -1135,18 +1269,17 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
}
//now we do the bit by bit xor depending on the frame and position of the state bit counter
//run 6 instead of 7 so we can just do bit 49 outside of loop to keep extra bits overloading the array
z = 0;
for (i = 0; i < 6; i++)
//skip keystream if silence or zeroes (some CCR), else apply keystream directly and increment counter
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->bit_counterR += 49;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->bit_counterR += 49;
else
{
for (j = 0; j < 8; j++)
ambe_d[z++] ^= state->ks_bitstreamR[state->bit_counterR++];
for (i = 0; i < 49; i++)
ambe_d[i] ^= state->ks_bitstreamR[state->bit_counterR++];
}
//last bit
ambe_d[48] ^= state->ks_bitstreamR[state->bit_counterR++];
//skip the next 7 bits of the array (if not Hytera Enhanced)
if(state->payload_algidR != 0x02)
state->bit_counterR += 7;
@@ -1184,14 +1317,22 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
//that may occur on some systems that preempt VC6 voice for a RC opportuninity (TXI)
//this occurs because we are supposed to either have a a 'repeat' frame, or 'silent' frame play
//due to the error, but the keystream application makes it random 'pfft pop' sound instead
if (state->errsR < 3)
rc4_voice_decrypt(state->dropR, 9, 7, rckey, cipher, plain);
else memcpy (plain, cipher, sizeof(plain));
state->dropR += 7;
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->dropR += 7;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->dropR += 7;
else
{
if (state->errsR < 3)
rc4_voice_decrypt(state->dropR, 9, 7, rckey, cipher, plain);
else memcpy (plain, cipher, sizeof(plain));
state->dropR += 7;
//unpack deciphered plain array back into ambe_d bit array
memset (ambe_d, 0, 49*sizeof(char));
unpack_ambe(plain, ambe_d);
//unpack deciphered plain array back into ambe_d bit array
memset (ambe_d, 0, 49*sizeof(char));
unpack_ambe(plain, ambe_d);
}
}
@@ -1233,48 +1374,115 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
//DMR Retevis AP, Either Slot (static single key'd enforced KS)
if (state->retevis_ap == 1)
{
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
else
{
uint8_t frame1_cipher[49];
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
decrypt_rc2((CryptoContext *)state->rc2_context, frame1_cipher);
memset (ambe_d, 0, 49*sizeof(char));
for (int i = 0; i < 49; i++) ambe_d[i] = frame1_cipher[i];
}
}
//DMR TYT AP, Either Slot (static single key'd enforced KS)
if (state->tyt_ap == 1)
{
short frame1_cipher[49];
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
decrypt_frame_49(frame1_cipher);
memset (ambe_d, 0, 49*sizeof(char));
for (int i = 0; i < 49; i++) ambe_d[i] = ctx.bits[i];
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
else
{
short frame1_cipher[49];
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
decrypt_frame_49(frame1_cipher);
memset (ambe_d, 0, 49*sizeof(char));
for (int i = 0; i < 49; i++) ambe_d[i] = ctx.bits[i];
}
}
//DMR BAOFENG AP, Either Slot (static single key'd enforced KS)
if (state->baofeng_ap == 1)
{
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
else
{
short frame1_cipher[49];
for (int i = 0; i < 49; i++) frame1_cipher[i] = ambe_d[i];
decrypt_frame_49_pc5(frame1_cipher);
memset (ambe_d, 0, 49*sizeof(char));
for (int i = 0; i < 49; i++) ambe_d[i] = ctxpc5.bits[i];
}
}
//DMR TYT EP, Either Slot (static single key'd enforced KS)
if (state->tyt_ep == 1)
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 1);
if (memcmp(ambe_d, ambe_silence, 49) == 0) {}
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0) {}
else
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(ctx.bits[i] & 1);
}
}
//DMR Kenwood Scrambler, Either Slot (static single key'd enforced KS) //should probably break this up, but this is a test for now
if (state->ken_sc == 1)
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%882] & 1); //Yikes!
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 49;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->static_ks_counter[state->currentslot] += 49;
else
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%882] & 1); //Yikes!
}
}
//DMR Anytone BP, Either Slot (static single key'd enforced KS)
if (state->any_bp == 1)
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%16] & 1); //Yikes!
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 49;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->static_ks_counter[state->currentslot] += 49;
else
{
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%16] & 1); //Yikes!
}
}
//Generic Straight Static Keystream
if (state->straight_ks == 1)
{
//disable enc identifiers, if present
state->dmr_soR = 0;
state->payload_algidR = 0;
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 49;
else if (memcmp(ambe_d+24, zeroes+24, zeroes_threshold) == 0)
state->static_ks_counter[state->currentslot] += 49;
else
{
//disable enc identifiers, if present
state->dmr_soR = 0;
state->payload_algidR = 0;
for (int i = 0; i < 49; i++)
ambe_d[i] ^= (uint8_t)(state->static_ks_bits[state->currentslot][(state->static_ks_counter[state->currentslot]++)%state->straight_mod] & 1); //Yikes!
}
}
mbe_processAmbe2450Dataf (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR,

View File

@@ -8,322 +8,6 @@
#include "dsd.h"
static const int PARITY[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};
// trellis_1_2 encode: source is in bits, result in bits
void trellis_encode(uint8_t result[], const uint8_t source[], int result_len, int reg)
{
for (int i=0; i<result_len; i+=2) {
reg = (reg << 1) | source[i>>1];
result[i] = PARITY[reg & 0x19];
result[i+1] = PARITY[reg & 0x17];
}
}
// simplified trellis 2:1 decode; source and result in bits
// assumes that encoding was done with NTEST trailing zero bits
// result_len should be set to the actual number of data bits
// in the original unencoded message (excl. these trailing bits)
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len)
{
int reg = 0;
int min_d;
int min_bt;
static const int NTEST = 4;
static const int NTESTC = 1 << NTEST;
uint8_t bt[NTEST];
uint8_t tt[NTEST*2];
int dstats[4];
int sum;
for (int p=0; p < 4; p++)
dstats[p] = 0;
for (int p=0; p < result_len; p++) {
for (int i=0; i<NTESTC; i++) {
bt[0] = (i&8)>>3;
bt[1] = (i&4)>>2;
bt[2] = (i&2)>>1;
bt[3] = (i&1);
trellis_encode(tt, bt, NTEST*2, reg);
sum=0;
for (int j=0; j<NTEST*2; j++) {
sum += tt[j] ^ source[p*2+j];
}
if (i == 0 || sum < min_d) {
min_d = sum;
min_bt = bt[0];
}
}
result[p] = min_bt;
reg = (reg << 1) | min_bt;
dstats[(min_d > 3) ? 3 : min_d] += 1;
}
//debug output
// fprintf (stderr, "\n stats\t%d %d %d %d\n", dstats[0], dstats[1], dstats[2], dstats[3]);
}
//Original Copyright/License
/* -*- c++ -*- */
/*
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
*
* This file is part of OP25
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
//Ripped from libM17
#define K 5 //constraint length
#define NUM_STATES (1 << (K - 1)) //number of states
static uint32_t prevMetrics[NUM_STATES];
static uint32_t currMetrics[NUM_STATES];
static uint32_t prevMetricsData[NUM_STATES];
static uint32_t currMetricsData[NUM_STATES];
static uint16_t viterbi_history[244];
/**
* @brief Decode unpunctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param len Input length in bits.
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
{
if(len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
viterbi_reset();
size_t pos = 0;
for(size_t i = 0; i < len; i += 2)
{
uint16_t s0 = in[i];
uint16_t s1 = in[i + 1];
viterbi_decode_bit(s0, s1, pos);
pos++;
}
uint32_t err = viterbi_chainback(out, pos, len/2);
//debug
// fprintf (stderr, "\n vcb: \n");
// for (size_t i = 0; i < len; i++)
// fprintf (stderr, "%02X", out[i]);
return err;
}
/**
* @brief Decode punctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param punct Puncturing matrix.
* @param in_len Input data length.
* @param p_len Puncturing matrix length (entries).
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
{
if(in_len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
uint16_t umsg[244*2]; //unpunctured message
uint8_t p=0; //puncturer matrix entry
uint16_t u=0; //bits count - unpunctured message
uint16_t i=0; //bits read from the input message
while(i<in_len)
{
if(punct[p])
{
umsg[u]=in[i];
i++;
}
else
{
umsg[u]=0x7FFF;
}
u++;
p++;
p%=p_len;
}
//debug
// fprintf (stderr, " p: %d, u: %d; p_len: %d; len: %d;", p, u, p_len, (u-in_len)*0x7FFF);
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
}
/**
* @brief Decode one bit and update trellis.
*
* @param s0 Cost of the first symbol.
* @param s1 Cost of the second symbol.
* @param pos Bit position in history.
*/
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos)
{
static const uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
static const uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0, 0, 0xFFFF, 0xFFFF, 0};
for(uint8_t i = 0; i < NUM_STATES/2; i++)
{
uint32_t metric = q_abs_diff(COST_TABLE_0[i], s0)
+ q_abs_diff(COST_TABLE_1[i], s1);
uint32_t m0 = prevMetrics[i] + metric;
uint32_t m1 = prevMetrics[i + NUM_STATES/2] + (0x1FFFE - metric);
uint32_t m2 = prevMetrics[i] + (0x1FFFE - metric);
uint32_t m3 = prevMetrics[i + NUM_STATES/2] + metric;
uint8_t i0 = 2 * i;
uint8_t i1 = i0 + 1;
if(m0 >= m1)
{
viterbi_history[pos]|=(1<<i0);
currMetrics[i0] = m1;
}
else
{
viterbi_history[pos]&=~(1<<i0);
currMetrics[i0] = m0;
}
if(m2 >= m3)
{
viterbi_history[pos]|=(1<<i1);
currMetrics[i1] = m3;
}
else
{
viterbi_history[pos]&=~(1<<i1);
currMetrics[i1] = m2;
}
}
//swap
uint32_t tmp[NUM_STATES];
for(uint8_t i=0; i<NUM_STATES; i++)
{
tmp[i]=currMetrics[i];
}
for(uint8_t i=0; i<NUM_STATES; i++)
{
currMetrics[i]=prevMetrics[i];
prevMetrics[i]=tmp[i];
}
}
/**
* @brief History chainback to obtain final byte array.
*
* @param out Destination byte array for decoded data.
* @param pos Starting position for the chainback.
* @param len Length of the output in bits.
* @return Minimum Viterbi cost at the end of the decode sequence.
*/
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
{
uint8_t state = 0;
size_t bitPos = len+4;
memset(out, 0, (len-1)/8+1);
while(pos > 0)
{
bitPos--;
pos--;
uint16_t bit = viterbi_history[pos]&((1<<(state>>4)));
state >>= 1;
if(bit)
{
state |= 0x80;
out[bitPos/8]|=1<<(7-(bitPos%8));
}
}
uint32_t cost = prevMetrics[0];
for(size_t i = 0; i < NUM_STATES; i++)
{
uint32_t m = prevMetrics[i];
if(m < cost) cost = m;
}
//debug
// fprintf (stderr, "\n cb: \n");
// for (size_t i = 0; i < len; i++)
// fprintf (stderr, "%02X", out[i]);
return cost;
}
/**
* @brief Reset the decoder state. No args.
*
*/
void viterbi_reset(void)
{
memset((uint8_t*)viterbi_history, 0, 2*244);
memset((uint8_t*)currMetrics, 0, 4*NUM_STATES);
memset((uint8_t*)prevMetrics, 0, 4*NUM_STATES);
memset((uint8_t*)currMetricsData, 0, 4*NUM_STATES);
memset((uint8_t*)prevMetricsData, 0, 4*NUM_STATES);
}
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
{
if(v2 > v1) return v2 - v1;
return v1 - v2;
}
//original sources
//--------------------------------------------------------------------
// M17 C library - decode/viterbi.c
//
// This file contains:
// - the Viterbi decoder
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// M17 C library - math/math.c
//
// This file contains:
// - absolute difference value
// - Euclidean norm (L2) calculation for n-dimensional vectors (float)
// - soft-valued arrays to integer conversion (and vice-versa)
// - fixed-valued multiplication and division
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------
//audio filter stuff sourced from: https://github.com/NedSimao/FilteringLibrary
//no license / information provided in source code
#define PI 3.141592653
@@ -337,7 +21,6 @@ uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
// #define HFC 50
// #define LFC 100
void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
{
@@ -351,7 +34,6 @@ void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
}
float LPFilter_Update(LPFilter *filter, float v_in)
{
@@ -382,7 +64,6 @@ void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS)
}
float HPFilter_Update(HPFilter *filter, float v_in)
{
@@ -592,27 +273,3 @@ long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as
if (rms < 0) rms = 150;
return rms;
}
/*
* Mean power (RMS^2 proxy) without sqrt, modeled after mean_power() in rtl_sdr_fm.cpp.
* Computes a DC-corrected average of squares to avoid costly sqrt operations.
*/
long int raw_pwr(int16_t *samples, int len, int step)
{
int64_t p = 0;
int64_t t = 0;
for (int i = 0; i < len; i += step) {
int64_t s = (int64_t)samples[i];
t += s;
p += s * s;
}
/* DC-corrected energy ≈ p - (t^2)/len with rounded division */
int64_t dc_corr = 0;
if (len > 0) {
int64_t tt = t * t;
dc_corr = (tt + (len / 2)) / len;
}
int64_t energy = p - dc_corr;
if (energy < 0) energy = 0;
return (long int)(energy / (len > 0 ? len : 1));
}

View File

@@ -34,10 +34,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
//keyboard shortcuts - codes same as ascii codes
if (c == 10) //Return / Enter key, open menu
{
if (opts->m17encoder == 0) //don't allow menu if using M17 encoder
ncursesMenu (opts, state);
}
ncursesMenu (opts, state);
//use k and l keys to test tg hold toggles on slots 1 and slots 2
if (c == 107) //'k' key, hold tg on slot 1 for trunking purposes, or toggle clear
@@ -214,14 +211,14 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
if (c == 52) // '4' key, toggle force privacy key over fid and svc (dmr)
{
if (state->M == 1 || state->M == 0x21) state->M = 0;
else state->M = 1;
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0;
else state->forced_alg_id = 1;
}
if (c == 54) // '6' key, toggle force rc4 key over missing pi header/late entry
{
if (state->M == 1 || state->M == 0x21) state->M = 0;
else state->M = 0x21;
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21) state->forced_alg_id = 0;
else state->forced_alg_id = 0x21;
}
if (c == 105) //'i' key, toggle signal inversion on inverted types
@@ -233,7 +230,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
opts->inverted_dpmr = 1;
opts->inverted_x2tdma = 1;
opts->inverted_ysf = 1;
opts->inverted_m17 = 1;
opts->inverted_nxdn = 1;
}
else
{
@@ -241,7 +238,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
opts->inverted_dpmr = 0;
opts->inverted_x2tdma = 0;
opts->inverted_ysf = 0;
opts->inverted_m17 = 0;
opts->inverted_nxdn = 0;
}
}
@@ -382,8 +379,8 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
if (c == 112) //'p' key - stop all per call wav files //TODO: Fix
{
//TODO: Add Closing of RAW files as well?
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling
opts->wav_out_fileR[0] = 0;
opts->dmr_stereo_wav = 0;
@@ -597,7 +594,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
else opts->trunk_tune_enc_calls = 1;
}
if (opts->p25_trunk == 1 && c == 103) //'g' key, toggle tune group calls
if (opts->p25_trunk == 1 && c == 84) //'T' key, toggle tune group calls
{
if (opts->trunk_tune_group_calls == 1) opts->trunk_tune_group_calls = 0;
else opts->trunk_tune_group_calls = 1;
@@ -888,16 +885,6 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
else opts->use_hpf_d = 0;
}
if (opts->m17encoder == 1 && c == 92) //'\' key - toggle M17 encoder Encode + TX
{
if (state->m17encoder_tx == 0) state->m17encoder_tx = 1;
else state->m17encoder_tx = 0;
//flag on the EOT marker to send last frame after toggling encoder to zero
if (state->m17encoder_tx == 0) state->m17encoder_eot = 1;
}
if(opts->frame_provoice == 1 && c == 65) //'A' Key, toggle ESK mask 0xA0
{
if (state->esk_mask == 0) state->esk_mask = 0xA0;
@@ -931,6 +918,26 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
if (c == 123)
opts->rtlsdr_ppm_error--;
//RTL Gain Manual Adjustment
if (c == 103)
{
if (opts->rtl_gain_value > 0)
{
opts->rtl_gain_value--;
opts->rtl_gain_actual = 0;
}
}
if (c == 71)
{
if (opts->rtl_gain_value < 49)
{
opts->rtl_gain_value++;
opts->rtl_gain_actual = 0;
}
}
//anything with an entry box will need the inputs and outputs stopped first
//so probably just write a function to handle c input, and when c = certain values
//needing an entry box, then stop all of those

View File

@@ -130,11 +130,13 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
closePulseOutput (opts);
}
#ifndef __APPLE__
//close OSS output
if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
{
close (opts->audio_out_fd);
}
#endif
if (opts->audio_in_type == 0) //close pulse input if it is the specified input method
{
@@ -417,7 +419,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
entry_win = newwin(8, WIDTH+22, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " RTL Power Squelch Level (NXDN/dPMR/Analog/Raw only): ");
mvwprintw(entry_win, 2, 2, " RTL RMS Squelch Level (NXDN/dPMR/Analog/Raw only): ");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
@@ -463,7 +465,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
mvwprintw(entry_win, 8, 2, " RTL Volume Multiplier: %d", opts->rtl_volume_multiplier);
mvwprintw(entry_win, 9, 2, " RTL Device UDP Port: %d", opts->rtl_udp_port);
mvwprintw(entry_win, 10, 2, " RTL Device PPM: %d", opts->rtlsdr_ppm_error);
mvwprintw(entry_win, 11, 2, " RTL PWR Squelch: %d", opts->rtl_squelch_level);
mvwprintw(entry_win, 11, 2, " RTL RMS Squelch: %d", opts->rtl_squelch_level);
mvwprintw(entry_win, 13, 2, " Are You Sure?");
mvwprintw(entry_win, 14, 2, " 1 = Yes, 2 = No ");
mvwprintw(entry_win, 15, 3, " ");
@@ -692,8 +694,8 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
if (choicec == 12)
{
//TODO: Add Closing of RAW files as well?
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling
opts->wav_out_fileR[0] = 0;
opts->dmr_stereo_wav = 0;
@@ -921,11 +923,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
//toggle enforcement of basic privacy key over enc bit set on traffic
if (option == 4)
{
if (state->M == 1 || state->M == 0x21)
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21)
{
state->M = 0;
state->forced_alg_id = 0;
}
else state->M = 1;
else state->forced_alg_id = 1;
}
if (option == 5)
@@ -951,11 +953,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
//toggle enforcement of rc4 key over missing pi header/le on DMR
// if (option == 6)
// {
// if (state->M == 1 || state->M == 0x21)
// if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21)
// {
// state->M = 0;
// state->forced_alg_id = 0;
// }
// else state->M = 0x21;
// else state->forced_alg_id = 0x21;
// }
//load AES keys
@@ -1415,7 +1417,6 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
opts->inverted_dpmr = 1;
opts->inverted_x2tdma = 1;
opts->inverted_ysf = 1;
opts->inverted_m17 = 1;
}
else
{
@@ -1423,7 +1424,6 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
opts->inverted_dpmr = 0;
opts->inverted_x2tdma = 0;
opts->inverted_ysf = 0;
opts->inverted_m17 = 0;
}
}
@@ -1587,10 +1587,12 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
openPulseOutput (opts);
}
#ifndef __APPLE__
if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
{
openOSSOutput (opts);
}
#endif
if (opts->audio_in_type == 0) //reopen pulse input if it is the specified input method

View File

@@ -223,9 +223,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("| Symbol Float Input: %s \n", opts->audio_in_dev);
}
if (opts->m17decoderip == 1 && opts->udp_sockfd)
printw ("| M17 UDP IP Frame Input: %s:%d \n", opts->m17_hostname, opts->m17_portno);
if (opts->audio_in_type == 8)
{
printw ("| TCP Signal Input: %s:%d; %d kHz; 1 Ch; ", opts->tcp_hostname, opts->tcp_portno, opts->wav_sample_rate/1000);
@@ -248,14 +245,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->audio_in_type == 3)
{
printw ("| RTL: %d;", opts->rtl_dev_index);
if (opts->rtl_gain_value == 0)
if (opts->rtl_gain_actual == -100)
printw (" G: AGC;");
else
printw (" G: %idB;", opts->rtl_gain_value);
printw (" G: %i/%i;", opts->rtl_gain_value, opts->rtl_gain_actual);
printw (" V: %iX;", opts->rtl_volume_multiplier);
printw (" PPM: %i;", opts->rtlsdr_ppm_error); //Adjust manually now with { and }
printw (" SQ: %i;", opts->rtl_squelch_level);
printw (" PWR: %04li;", opts->rtl_pwr);
printw (" RMS: %04li;", opts->rtl_rms);
printw (" BW: %i;", opts->rtl_bandwidth);
printw (" FRQ: %i;", opts->rtlsdr_center_freq);
if (opts->rtl_udp_port != 0) printw ("\n| External RTL Tuning on UDP Port: %i", opts->rtl_udp_port);
@@ -282,7 +279,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("| Pulse Analog Output: %i kHz; %i Ch; G: %02.0f%% (/|*) ", opts->pulse_raw_rate_out/1000, opts->pulse_raw_out_channels, opts->audio_gainA);
if (opts->audio_gainA == 0.0f) printw ("Auto ");
else printw ("Manual ");
if (opts->audio_in_type != 3) printw ("PWR: %04ld; ", opts->rtl_pwr);
if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms);
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
if (opts->use_hpf == 1) printw ("HP|"); else printw (" |");
if (opts->use_pbf == 1) printw ("PB|"); else printw (" |");
@@ -302,7 +299,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) )
{
printw ("\n| Analog Monitor PWR: %04ld; G: %02.0f%% (/|*) ", opts->rtl_pwr, opts->audio_gainA);
printw ("\n| Analog Monitor RMS: %04ld; G: %02.0f%% (/|*) ", opts->rtl_rms, opts->audio_gainA);
if (opts->audio_gainA == 0.0f) printw ("Auto ");
else printw ("Manual ");
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
@@ -321,14 +318,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->use_hpf_d == 1) printw (" HPF");
if (opts->call_alert == 1) printw (" *CA!"); //Call Alert
if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) )
printw (" - Monitor PWR: %04ld ", opts->rtl_pwr);
printw (" - Monitor RMS: %04ld ", opts->rtl_rms);
printw (" \n");
if (opts->udp_sockfdA != 0) //Analog Output on udp port +2
{
printw ("| UDP Analog Output: %s:%d; 48 kHz 1 Ch; G: %02.0f%% (/|*) ", opts->udp_hostname, opts->udp_portno+2, opts->audio_gainA);
if (opts->audio_gainA == 0.0f) printw ("A ");
else printw ("M ");
if (opts->audio_in_type != 3) printw ("PWR: %04ld; ", opts->rtl_pwr);
if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms);
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
if (opts->use_hpf == 1) printw ("HP|"); else printw (" |");
if (opts->use_pbf == 1) printw ("PB|"); else printw (" |");
@@ -336,25 +333,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
}
if (opts->m17encoder == 1)
{
printw ("| M17 Encoder:");
if (state->m17encoder_tx == 1 && state->m17_vox == 0) printw (" Toggle TX (\\) ON ;");
if (state->m17encoder_tx == 0 && state->m17_vox == 0) printw (" Toggle TX (\\) OFF;");
if (state->m17_vox == 1) printw (" Vox Mode;");
printw (" Input Gain (/|*): %02.0f%% ", opts->audio_gainA);
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
if (opts->use_hpf == 1) printw ("HP|"); else printw (" |");
if (opts->use_pbf == 1) printw ("PB|"); else printw (" |");
if (opts->audio_in_type != 3 && state->m17_vox == 1) printw ( " SQL: %04ld : %04d;", opts->rtl_pwr, opts->rtl_squelch_level);
printw ("\n");
}
if (opts->m17_use_ip == 1)
printw ("| M17 UDP IP Frame Output: %s:%d \n", opts->m17_hostname, opts->m17_portno);
if (opts->mbe_out_dir[0] != 0 && opts->dmr_stereo == 0)
{
printw ("| Writing MBE data files to directory %s\n", opts->mbe_out_dir);
@@ -385,7 +363,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
{
printw ("| Trunking -");
if (opts->trunk_tune_group_calls == 0) attron(COLOR_PAIR(2));
printw (" Group(g)");
printw (" Group(T)");
attron(COLOR_PAIR(4));
if (opts->trunk_tune_private_calls == 0) attron(COLOR_PAIR(2));
printw (" Private(u)");
@@ -404,8 +382,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->p25_trunk == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3) )
{
printw ("| Trunking -");
if (opts->trunk_tune_group_calls == 0) printw (" group(g)");
else printw (" GROUP(g)");
if (opts->trunk_tune_group_calls == 0) printw (" group(T)");
else printw (" GROUP(T)");
if (opts->trunk_tune_private_calls == 0) printw (" private(u)");
else printw (" PRIVATE(u)");
if (opts->trunk_tune_data_calls == 0) printw (" data(d)");
@@ -517,18 +495,18 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
#endif
// if (opts->aggressive_framesync == 0) printw ("| Selective CRC ERR Bypass Enabled (RAS) \n");
if (state->M == 1)
if (state->forced_alg_id == 1)
{
if (state->R != 0) printw ("| Forcing Key Priority -- NXDN Sc Key: %05lld \n", state->R);
if (state->K != 0) printw ("| Forcing Key Priority -- Moto BP Key: %03lld \n", state->K);
if (state->K1 != 0) printw ("| Forcing Key Priority -- Hytera BP Key: %016llX \n", state->K1);
if (state->K != 0 && state->K1 != 0) printw ("| Warning! Multiple DMR Key Types Loaded! \n"); //warning may not be required
}
if (state->M == 0x21)
if (state->forced_alg_id == 0x21)
{
if (state->R != 0) printw ("| Forcing Key Priority -- RC4 Key: %010llX \n", state->R);
}
if (state->M == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H);
if (state->forced_alg_id == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H);
if (opts->scanner_mode == 1)
{
@@ -561,7 +539,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->mod_c4fm == 1) printw ("[C4FM]");
if (opts->mod_gfsk == 1) printw ("[GFSK]");
printw ( "[%d] \n", (48000*opts->wav_interpolator)/state->samplesPerSymbol);
if (opts->m17encoder == 1) printw ("| Encoding: [%s] \n", opts->output_name);
printw ("| Decoding: [%s] ", opts->output_name);
if (opts->aggressive_framesync == 0) printw ("CRC/(RAS) ");
//debug -- troubleshoot voice tuning after grant on DMR CC, subsequent grant may not tune because tuner isn't available
@@ -654,20 +631,22 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("\n");
printw ("| ");
//fill in any extra info, like Meta (IV, etc)
if (state->m17_enc == 1)
{
printw (" Scrambler - Type: %d", state->m17_enc_st);
attron(COLOR_PAIR(1));
printw ("Scrambler - Type: %d", state->m17_enc_st);
attron(COLOR_PAIR(3));
}
if (state->m17_enc == 2)
{
attron(COLOR_PAIR(1));
printw ("AES-CTR - IV: ");
//display packed meta as IV
printw ("AES-CTR IV: ");
//display AES IV
for (i = 0; i < 16; i++)
printw ("%02X", state->m17_meta[i]);
printw ("%02X", state->m17_aes_iv[i]);
attron(COLOR_PAIR(3));
}
if (state->m17_enc == 3)
@@ -676,6 +655,30 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
printw ("\n");
printw ("| ");
//decoded elements from stream
char shortm17[72];
memset(shortm17, 0, sizeof(shortm17));
strncpy (shortm17, state->m17_text_string, 70);
shortm17[71] = 0;
printw ("META TEXT: %s", shortm17);
printw ("\n");
printw ("| ");
memset(shortm17, 0, sizeof(shortm17));
strncpy (shortm17, state->m17_gnss_string, 70);
shortm17[71] = 0;
printw ("META GNSS: %s", shortm17);
printw ("\n");
printw ("| ");
memset(shortm17, 0, sizeof(shortm17));
strncpy (shortm17, state->m17_data_string, 70);
shortm17[71] = 0;
printw ("MISC DATA: %s", shortm17);
printw ("\n");
}
@@ -1109,6 +1112,26 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw(" Key: %010llX", state->R);
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x36)
{
attron(COLOR_PAIR(1));
printw("Kirisun Advanced ");
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x37)
{
attron(COLOR_PAIR(1));
printw("Kirisun Universal ");
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x35)
{
attron(COLOR_PAIR(1));
printw("Kirisun Encryption "); //0x35 is a placeholder if gained through late-entry method and alg is not known
attron(COLOR_PAIR(3));
}
printw ("\n");
@@ -1284,6 +1307,26 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw(" Key: %010llX", state->RR);
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x36)
{
attron(COLOR_PAIR(1));
printw("Kirisun Advanced ");
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x37)
{
attron(COLOR_PAIR(1));
printw("Kirisun Universal ");
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x35)
{
attron(COLOR_PAIR(1));
printw("Kirisun Encryption "); //0x35 is a placeholder if gained through late-entry method and alg is not known
attron(COLOR_PAIR(3));
}
printw ("\n");

View File

@@ -32,7 +32,6 @@ void error(char *msg) {
struct sockaddr_in address;
struct sockaddr_in addressA;
struct sockaddr_in addressM17;
//
// Connect
@@ -324,18 +323,6 @@ void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void *
if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible
}
int m17_socket_receiver(dsd_opts * opts, void * data)
{
size_t err = 0;
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
//receive data from socket
err = recvfrom(opts->udp_sockfd, data, 1000, 0, (struct sockaddr * ) & address, &len); //was MSG_WAITALL, but that seems to be = 256
return err;
}
//Analog UDP port on +2 of normal open socket
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
{
@@ -353,20 +340,6 @@ void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void *
if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible
}
int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
{
UNUSED(state);
unsigned long long int err = 0;
//See notes in m17.c on line ~3395 regarding usage
//send audio or data to socket
err = sendto(opts->m17_udp_sock, data, nsam, 0, (const struct sockaddr * ) & addressM17, sizeof(struct sockaddr_in));
//RETURN Value should be ACKN or NACK, or PING, or PONG
return (err);
}
int udp_socket_connect(dsd_opts * opts, dsd_state * state)
{
UNUSED(state);
@@ -447,46 +420,6 @@ int udp_socket_connectA(dsd_opts * opts, dsd_state * state)
return 0;
}
int udp_socket_connectM17(dsd_opts * opts, dsd_state * state)
{
UNUSED(state);
long int err = 0;
err = opts->m17_udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (err < 0)
{
fprintf (stderr, " UDP Socket Error %ld\n", err);
return (err);
}
// Don't think this is needed, but doesn't seem to hurt to keep it here either
int broadcastEnable = 1;
err = setsockopt(opts->m17_udp_sock, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
if (err < 0)
{
fprintf (stderr, " UDP Broadcast Set Error %ld\n", err);
return (err);
}
memset((char * ) & addressM17, 0, sizeof(addressM17));
addressM17.sin_family = AF_INET;
err = addressM17.sin_addr.s_addr = inet_addr(opts->m17_hostname);
if (err < 0) //error in this context reports back 32-bit inet_addr reversed order byte pairs
{
fprintf (stderr, " UDP inet_addr Error %ld\n", err);
return (err);
}
addressM17.sin_port = htons(opts->m17_portno);
if (err < 0)
{
fprintf (stderr, " UDP htons Error %ld\n", err);
return (err);
}
return 0;
}
void return_to_cc (dsd_opts * opts, dsd_state * state)
{
//extra safeguards due to sync issues with NXDN

View File

@@ -132,7 +132,7 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
if (get_rtlsdr_sample(&sample, opts, state) < 0)
cleanupAndExit(opts, state);
//update root means square power level
opts->rtl_pwr = rtl_return_pwr();
opts->rtl_rms = rtl_return_rms();
sample *= opts->rtl_volume_multiplier;
#endif
@@ -268,9 +268,9 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
if (state->analog_sample_counter == 960)
{
//get an pwr value if not using the rtl built in version
//get an rms value if not using the rtl built in version
if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1)
opts->rtl_pwr = raw_pwr(state->analog_out, 960, 1);
opts->rtl_rms = raw_rms(state->analog_out, 960, 1);
//raw wav file saving -- only write when not NXDN, dPMR, or M17 due to noise that can cause tons of false positives when no sync
if (opts->wav_out_raw != NULL && opts->frame_nxdn48 == 0 && opts->frame_nxdn96 == 0 && opts->frame_dpmr == 0 && opts->frame_m17 == 0)
@@ -299,13 +299,13 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
else
agsm(opts, state, state->analog_out, 960);
//Running PWR after filtering does remove the analog spike from the PWR value
//Running RMS after filtering does remove the analog spike from the RMS value
//but noise floor noise will still produce higher values
// if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1)
// opts->rtl_pwr = raw_pwr(state->analog_out, 960, 1);
// opts->rtl_rms = raw_rms(state->analog_out, 960, 1);
//seems to be working now, but PWR values are lower on actual analog signal than on no signal but noise
if ( (opts->rtl_pwr > opts->rtl_squelch_level) && opts->monitor_input_audio == 1 && state->carrier == 0 ) //added carrier check here in lieu of disabling it above
//seems to be working now, but RMS values are lower on actual analog signal than on no signal but noise
if ( (opts->rtl_rms > opts->rtl_squelch_level) && opts->monitor_input_audio == 1 && state->carrier == 0 ) //added carrier check here in lieu of disabling it above
{
if (opts->audio_out_type == 0)
pa_simple_write(opts->pulse_raw_dev_out, state->analog_out, 960*2, NULL);

View File

@@ -179,7 +179,7 @@ unsigned long long int edacsVoteFr(unsigned long long int fr_1_4, unsigned long
void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn)
{
int i, result;
int count = 5; //PWR has a 5 count (5 * 180ms) now before cutting off;
int count = 5; //RMS has a 5 count (5 * 180ms) now before cutting off;
short analog1[960];
short analog2[960];
short analog3[960];
@@ -202,7 +202,7 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
memset (d2, 0, sizeof(d2));
memset (d3, 0, sizeof(d3));
long int pwr = opts->rtl_squelch_level + 1; //one more for the initial loop phase
long int rms = opts->rtl_squelch_level + 1; //one more for the initial loop phase
long int sql = opts->rtl_squelch_level;
fprintf (stderr, "\n");
@@ -229,8 +229,8 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
analog3[i] = sample;
}
//this pwr will only work properly (for now) with squelch enabled in SDR++ or other
pwr = raw_pwr(analog3, 960, 1);
//this rms will only work properly (for now) with squelch enabled in SDR++ or other
rms = raw_rms(analog3, 960, 1);
}
//NOTE: The core dumps observed previously were due to SDR++ Remote Server connection dropping due to Internet/Other issues
@@ -287,8 +287,8 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
analog3[i] = sample;
}
//this pwr will only work properly (for now) with squelch enabled in SDR++
pwr = raw_pwr(analog3, 960, 1);
//this rms will only work properly (for now) with squelch enabled in SDR++
rms = raw_rms(analog3, 960, 1);
}
//RTL Input
@@ -315,8 +315,8 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
sample *= opts->rtl_volume_multiplier;
analog3[i] = sample;
}
//the rtl pwr value works properly without needing a 'hard' squelch value
pwr = rtl_return_pwr();
//the rtl rms value works properly without needing a 'hard' squelch value
rms = rtl_return_rms();
}
#endif
@@ -387,12 +387,12 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
agsm (opts, state, analog3, 960);
}
//NOTE: Ideally, we would run raw_pwr for TCP/VS here, but the analog spike on EDACS (STM)
//NOTE: Ideally, we would run raw_rms for TCP/VS here, but the analog spike on EDACS (STM)
//system gets filtered out, and when they hold the radio open and don't talk,
//it counts against the squelch hit as no audio, so we will just have to use
//the squelch checkbox in SDR++ and similar when using those input methods
// if (opts->audio_in_type != 3)
// pwr = raw_pwr(analog3, 960, 1);
// rms = raw_rms(analog3, 960, 1);
//reconfigured to use seperate audio out stream that is always 48k short
if (opts->audio_out_type == 0 && opts->slot1_on == 1)
@@ -425,18 +425,18 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
write (opts->audio_out_fd, analog3, 960*2);
}
opts->rtl_pwr = pwr;
opts->rtl_rms = rms;
printFrameSync (opts, state, " EDACS", 0, "A");
if (pwr < sql) count--;
if (rms < sql) count--;
else count = 5;
if (pwr > sql) fprintf(stderr, "%s", KGRN);
if (rms > sql) fprintf(stderr, "%s", KGRN);
else fprintf(stderr, "%s", KRED);
fprintf (stderr, " Analog PWR: %04ld SQL: %ld", pwr, sql);
fprintf (stderr, " Analog RMS: %04ld SQL: %ld", rms, sql);
if (state->ea_mode == 0)
{
int a = (afs >> state->edacs_a_shift) & state->edacs_a_mask;
@@ -1098,7 +1098,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1214,7 +1214,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1321,7 +1321,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1488,7 +1488,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
//NOTE: Restructured below so that analog and digital are handled the same, just that when
//its analog, it will now start edacs_analog which will while loop analog samples until
//signal level drops (PWR, or a dotting sequence is detected)
//signal level drops (RMS, or a dotting sequence is detected)
//this is working now with the new import setup
if (opts->trunk_tune_group_calls == 1 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
@@ -1501,7 +1501,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1743,7 +1743,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
//NOTE: Restructured below so that analog and digital are handled the same, just that when
//its analog, it will now start edacs_analog which will while loop analog samples until
//signal level drops (PWR, or a dotting sequence is detected)
//signal level drops (RMS, or a dotting sequence is detected)
//this is working now with the new import setup
if (((is_individual == 0 && opts->trunk_tune_group_calls == 1) || (is_individual == 1 && opts->trunk_tune_private_calls == 1)) &&
@@ -1757,7 +1757,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1857,7 +1857,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
//NOTE: Restructured below so that analog and digital are handled the same, just that when
//its analog, it will now start edacs_analog which will while loop analog samples until
//signal level drops (PWR, or a dotting sequence is detected)
//signal level drops (RMS, or a dotting sequence is detected)
//this is working now with the new import setup
if ((opts->trunk_tune_private_calls == 1) && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
@@ -1870,7 +1870,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -2153,7 +2153,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
//NOTE: Restructured below so that analog and digital are handled the same, just that when
//its analog, it will now start edacs_analog which will while loop analog samples until
//signal level drops (PWR, or a dotting sequence is detected)
//signal level drops (RMS, or a dotting sequence is detected)
//this is working now with the new import setup
if ((opts->trunk_tune_group_calls == 1) && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
@@ -2166,7 +2166,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -2299,6 +2299,10 @@ void eot_cc(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "EOT; \n");
//skip an arbitrary number of dibits so that the CC can cancel the grant after a dotting sequence
//prior to returning to the control channel and getting sent in a loopback to the call channel
skipDibit(opts, state, 240*8);
//watchdog event at this point
state->lastsynctype = 38;
watchdog_event_history(opts, state, 0);
@@ -2308,7 +2312,7 @@ void eot_cc(dsd_opts * opts, dsd_state * state)
if (opts->dmr_stereo_wav == 1)
{
if (opts->wav_out_f != NULL)
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
}

View File

@@ -1479,7 +1479,7 @@ bool QR_16_7_6_decode(unsigned char *rxBits)
unsigned int syndromeI = 0; // syndrome index
int is = 0;
int i = 0;
int corrections = 0;
int corrections = 0; UNUSED(corrections);
for (is = 0; is < 9; is++)
{
syndromeI += (((rxBits[0] * QR_16_7_6_m_H[16*is + 0])

3918
src/m17.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,173 +0,0 @@
/*
* Copyright (C) 2009-2016,2018 by Jonathan Naylor G4KLX
*
* Copyright (C) 2018 by Edouard Griffiths F4EXB:
* - Cosmetic changes to integrate with DSDcc
*
* Copyright (C) 2018 by Louis HERVE F4HUZ:
* - Transform C++ lib into C lib to integrate with DSD
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Include ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
/* Define -------------------------------------------------------------------*/
/* Global variables ---------------------------------------------------------*/
static const uint8_t CNXDNConvolution_BIT_MASK_TABLE[8] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | CNXDNConvolution_BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
#define READ_BIT1(p,i) (p[(i)>>3] & CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
static const uint8_t CNXDNConvolution_BRANCH_TABLE1[8] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U};
static const uint8_t CNXDNConvolution_BRANCH_TABLE2[8] = {0U, 2U, 2U, 0U, 0U, 2U, 2U, 0U};
static const unsigned int CNXDNConvolution_NUM_OF_STATES_D2 = 8U;
static const uint32_t CNXDNConvolution_M = 4U;
static const unsigned int CNXDNConvolution_K = 5U;
//NOTE:
uint16_t m_metrics1[16];
uint16_t m_metrics2[16];
uint64_t m_decisions[8*300];
uint16_t * m_oldMetrics = NULL;
uint16_t * m_newMetrics = NULL;
uint64_t * m_dp = NULL;
/* Functions ----------------------------------------------------------------*/
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
{
uint8_t i = 0;
uint8_t j = 0;
uint8_t decision0 = 0;
uint8_t decision1 = 0;
uint16_t metric = 0;
uint16_t m0 = 0;
uint16_t m1 = 0;
uint16_t * tmp = NULL;
*m_dp = 0U;
for (i = 0U; i < CNXDNConvolution_NUM_OF_STATES_D2; i++)
{
j = i * 2U;
metric = abs(CNXDNConvolution_BRANCH_TABLE1[i] - s0) + abs(CNXDNConvolution_BRANCH_TABLE2[i] - s1);
m0 = m_oldMetrics[i] + metric;
m1 = m_oldMetrics[i + CNXDNConvolution_NUM_OF_STATES_D2] + (CNXDNConvolution_M - metric);
decision0 = (m0 >= m1) ? 1U : 0U;
m_newMetrics[j + 0U] = decision0 != 0U ? m1 : m0;
m0 = m_oldMetrics[i] + (CNXDNConvolution_M - metric);
m1 = m_oldMetrics[i + CNXDNConvolution_NUM_OF_STATES_D2] + metric;
decision1 = (m0 >= m1) ? 1U : 0U;
m_newMetrics[j + 1U] = decision1 != 0U ? m1 : m0;
*m_dp |= ((uint64_t)(decision1) << (j + 1U)) | ((uint64_t)(decision0) << (j + 0U));
}
++m_dp;
//assert((m_dp - m_decisions) <= 300);
tmp = m_oldMetrics;
m_oldMetrics = m_newMetrics;
m_newMetrics = tmp;
}
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits)
{
//assert(out != 0);
uint32_t state = 0U;
uint32_t i = 0;
uint8_t bit = 0;
while (nBits-- > 0)
{
--m_dp;
i = state >> (9 - CNXDNConvolution_K);
bit = (uint8_t)(*m_dp >> i) & 1;
state = (bit << 7) | (state >> 1);
WRITE_BIT1(out, nBits, bit != 0U);
}
}
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits)
{
//assert(in != 0);
//assert(out != 0);
//assert(nBits > 0U);
uint8_t g1 = 0U;
uint8_t g2 = 0U;
uint8_t d = 0U;
uint8_t d1 = 0U;
uint8_t d2 = 0U;
uint8_t d3 = 0U;
uint8_t d4 = 0U;
uint32_t k = 0U;
unsigned int i = 0U;
for (i = 0U; i < nBits; i++)
{
d = READ_BIT1(in, i) ? 1U : 0U;
g1 = (d + d3 + d4) & 1;
g2 = (d + d1 + d2 + d4) & 1;
d4 = d3;
d3 = d2;
d2 = d1;
d1 = d;
WRITE_BIT1(out, k, g1 != 0U);
k++;
WRITE_BIT1(out, k, g2 != 0U);
k++;
}
}
void CNXDNConvolution_start(void)
{
m_oldMetrics = m_metrics1;
m_newMetrics = m_metrics2;
m_dp = m_decisions;
}
void CNXDNConvolution_init()
{
memset(m_metrics1, 0x0, 16*sizeof(uint16_t));
memset(m_metrics2, 0x0, 16*sizeof(uint16_t));
memset(m_decisions,0x0, 8*300*sizeof(uint64_t));
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,32 @@
//NXDN frame handler
//Reworked portions from Osmocom OP25 rx_sync.cc
/* -*- c++ -*- */
/*
/*-------------------------------------------------------------------------------
* nxdn_frame.c
* NXDN Frame Handling
*
* Reworked portions from Osmocom OP25 rx_sync.cc
* 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.
*/
* LWVMOBILE
* 2026-03 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "nxdn_const.h"
// #define NXDN_DEBUG_LICH //print LICH debug info on err on payload == 1
#define NXDN_LICH_OFFBITS //use the offbits to help determine sync status (disable if bad signal / bad sample)
#define LICH_ERR_THRESHOLD 7 //Threshold for lich dibit "dividing" errors (8 is perfect, 7 is 1 bit error)
// #define NXDN_DEBUG_LICH //print LICH debug info on err on payload == 1
// #define NXDN_LICH_OFFBITS //use the offbits to help determine sync status (disable if bad signal / bad sample)
//NXDN Type Enablement (handled by cmake -DTYPEC=OFF , -DTYPED=OFF, -DDCR=OFF)
// #define JPNDCR_DECODE //Include Japanese DCR Decoding
// #define TYPE_D_DECODE //Include Type-D Decoding
// #define TYPE_C_DECODE //Include Type-C and Conventional Decoding (Basically the "normal" NXDN stuff)
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;
uint8_t answer[32];
uint8_t sacch_answer[32];
uint8_t lich = 0;
int lich_parity_received;
int lich_parity_computed;
int voice = 0;
@@ -71,12 +63,21 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//nxdn bit buffer, for easy assignment handling
int nxdn_bit_buffer[364];
int nxdn_dibit_buffer[182];
#ifdef NXDN_DEBUG_LICH
/*
//this is how many bad syncs with polarity flipping occurred when allowed both pos and inv polarity on sync test
Sync: no sync
[+] [+] [-] [+] [-] [+] [-] [-] [-] [+] [+] [-] [+] (+) 17:36:02 Sync: NXDN48 RDCH Voice RAN 01 PF X/4
*/
//debug polarity (purely illustative, shows how frequently false sync can be attributed to wrong polarity)
if (state->lastsynctype == 28)
fprintf (stderr, "[+] ");
else fprintf (stderr, "[-] ");
#endif
//init all arrays
memset (dbuf, 0, sizeof(dbuf));
memset (answer, 0, sizeof(answer));
memset (sacch_answer, 0, sizeof(sacch_answer));
memset (lich_dibits, 0, sizeof(lich_dibits));
memset (sacch_bits, 0, sizeof(sacch_bits));
memset (facch_bits_b, 0, sizeof(facch_bits_b));
@@ -85,15 +86,18 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
memset (facch2_bits, 0, sizeof(facch2_bits));
memset (nxdn_bit_buffer, 0, sizeof(nxdn_bit_buffer));
memset (nxdn_dibit_buffer, 0, sizeof(nxdn_dibit_buffer));
//collect lich bits first, if they are good, then we can collect the rest of them
for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state);
nxdn_descramble (lich_dibits, 8);
nxdn_pn95_dibit_scrambler (state, lich_dibits, 8);
lich = 0;
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
//divide lich_dibits to get the lich code w/ parity bit on LSB
for (int i = 0; i < 8; i++)
{
lich <<= 1;
lich |= (lich_dibits[i] >> 1) & 1;
}
//debug lich as a 16-bit value (with encoding "dividing")
uint8_t lich_bits[16]; memset(lich_bits, 0, sizeof(lich_bits));
@@ -107,18 +111,17 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//debug look at the "off bits" of the encoded lich, should be all 1's (8)
//disble this code if sync issues arise, this may not be ideal of marginal signal
uint8_t lich_off_hex = 0;
uint8_t lich_off_hex = 0; UNUSED(lich_off_hex);
for (int i=0; i<8; i++)
lich_off_hex += lich_bits[(i*2)+1];
#ifdef NXDN_LICH_OFFBITS
if (lich_off_hex < 7) //allow up to 1 bit error
if (lich_off_hex < LICH_ERR_THRESHOLD)
{
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1)
fprintf(stderr, " Lich Off Bit Fill Error: %d / 8; \n", lich_off_hex);
#endif
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
goto END;
}
#endif
@@ -127,9 +130,11 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
lich_parity_computed = ((lich_full >> 7) + (lich_full >> 6) + (lich_full >> 5) + (lich_full >> 4)) & 1;
lich = lich_full >> 1;
#ifdef JPNDCR_DECODE
//special cases on DCR where parity is computed over 7 bits, and not 4 bits
if (lich == 0x4A || lich == 0x48 || lich == 0x46)
if (lich == 0x08 || lich == 0x4A || lich == 0x48 || lich == 0x46)
lich_parity_computed = ((lich_full >> 7) + (lich_full >> 6) + (lich_full >> 5) + (lich_full >> 4) + (lich_full >> 3) + (lich_full >> 2) + (lich_full >> 1)) & 1;
#endif
if (lich_parity_received != lich_parity_computed)
{
@@ -137,8 +142,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (opts->payload == 1)
fprintf(stderr, " Lich Parity Error %02X / %04X\n", lich_full, lich_bits_hex);
#endif
state->lastsynctype = -1;
goto END;
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
}
voice = 0;
@@ -156,150 +160,179 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
fprintf(stderr, " Simplex/Inbound NXDN lich on trunking system - type 0x%02X\n", lich);
#endif
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
goto END;
}
switch(lich) { //cases without breaks continue to flow downwards until they hit the break
case 0x01: // CAC types
case 0x05:
cac = 1;
break;
case 0x28: //facch2 types
case 0x29:
// case 0x48: //removing from here, moving to DCR as pich_tch
case 0x49:
facch2 = 1;
break;
case 0x2e: //udch types
case 0x2f:
case 0x4e:
case 0x4f:
udch = 1;
break;
case 0x32: //facch in 1, vch in 2
case 0x33:
case 0x52:
case 0x53:
voice = 2;
facch = 1;
sacch = 1;
break;
case 0x34: //vch in 1, facch in 2
case 0x35:
case 0x54:
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
voice = 1;
facch = 2;
sacch = 1;
break;
case 0x36: //vch in both
case 0x37:
case 0x56:
case 0x57:
voice = 3;
facch = 0;
sacch = 1;
break;
case 0x20: //facch in both
case 0x21:
case 0x30:
case 0x31:
case 0x40:
case 0x41:
case 0x50:
case 0x51:
voice = 0;
facch = 3;
sacch = 1;
break;
case 0x38: //sacch only (NULL?)
case 0x39:
sacch = 1;
break;
switch(lich)
{
//DCR Voice
case 0x46:
voice = 3;
sacch2 = 1;
break;
#ifdef TYPE_C_DECODE
case 0x01: // CAC types
case 0x05:
cac = 1;
break;
case 0x28: //facch2 types
case 0x29:
// case 0x48: //removing from here, moving to DCR as pich_tch
case 0x49:
facch2 = 1;
break;
case 0x2e: //udch types
case 0x2f:
case 0x4e:
case 0x4f:
udch = 1;
break;
case 0x32: //facch in 1, vch in 2
case 0x33:
case 0x52:
case 0x53:
voice = 2;
facch = 1;
sacch = 1;
break;
case 0x34: //vch in 1, facch in 2
case 0x35:
case 0x54:
case 0x55:
voice = 1;
facch = 2;
sacch = 1;
break;
case 0x36: //vch in both
case 0x37:
case 0x56:
case 0x57:
voice = 3;
facch = 0;
sacch = 1;
break;
case 0x20: //facch in both
case 0x21:
case 0x30:
case 0x31:
case 0x40:
case 0x41:
case 0x50:
case 0x51:
voice = 0;
facch = 3;
sacch = 1;
break;
case 0x38: //sacch only (NULL?)
case 0x39:
sacch = 1;
break;
#endif
//DCR Data or End Frame
case 0x48:
pich_tch = 3;
case 0x4A:
sacch2 = 1;
break;
#ifdef JPNDCR_DECODE
//DCR Voice
case 0x46:
voice = 3;
sacch2 = 1;
break;
//NXDN "Type-D" or "IDAS" Specific Lich Codes
case 0x76: //normal vch voice (in one and two)
case 0x77:
idas = 1;
scch = 1;
voice = 3;
break;
// case 0x74: //False Positive on DCR, keep disabled, or revert this line
case 0x75: //vch in 1, facch1 in 2 (facch 2 steal)
idas = 1;
scch = 1;
voice = 1;
facch = 2;
break;
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
case 0x73:
idas = 1;
scch = 1;
voice = 2;
facch = 1;
break;
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
case 0x71:
idas = 1;
scch = 1;
facch = 3;
break;
case 0x6E: //udch2
case 0x6F:
idas = 1;
scch = 1;
udch2 = 1;
break;
case 0x68:
case 0x69: //facch3
idas = 1;
scch = 1;
facch3 = 1;
break;
case 0x62:
case 0x63: //facch1 in 1, null data and post field in 2
idas = 1;
scch = 1;
facch = 1;
break;
case 0x60:
case 0x61: //facch1 in both (non vch)
idas = 1;
scch = 1;
facch = 3;
break;
//DCR SB0, Data or End Frame
case 0x08: //SB0 w/ CSM 9 digit BCD found here
sacch2 = 1;
pich_tch = 1; //observed 2nd PICH is zero fill
break;
case 0x48:
pich_tch = 3; //may be 1, or 2 TCH (observed possibly both)
case 0x4A:
sacch2 = 1;
break;
#endif
default:
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1)
fprintf(stderr, " false sync or unsupported NXDN lich type L: %02X / LH: %04X\n", lich, lich_bits_hex);
#endif
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
voice = 0;
goto END;
break;
#ifdef TYPE_D_DECODE
//NXDN "Type-D" or "IDAS" Specific Lich Codes
case 0x76: //normal vch voice (in one and two)
case 0x77:
idas = 1;
scch = 1;
voice = 3;
break;
// case 0x74: //False Positive on DCR, keep disabled, or revert this line
case 0x75: //vch in 1, facch1 in 2 (facch 2 steal)
idas = 1;
scch = 1;
voice = 1;
facch = 2;
break;
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
case 0x73:
idas = 1;
scch = 1;
voice = 2;
facch = 1;
break;
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
case 0x71:
idas = 1;
scch = 1;
facch = 3;
break;
case 0x6E: //udch2
case 0x6F:
idas = 1;
scch = 1;
udch2 = 1;
break;
case 0x68:
case 0x69: //facch3
idas = 1;
scch = 1;
facch3 = 1;
break;
case 0x62:
case 0x63: //facch1 in 1, null data and post field in 2
idas = 1;
scch = 1;
facch = 1;
break;
case 0x60:
case 0x61: //facch1 in both (non vch)
idas = 1;
scch = 1;
facch = 3;
break;
#endif
default:
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1)
fprintf(stderr, " false sync or unsupported NXDN lich type L: %02X / LH: %04X\n", lich, lich_bits_hex);
#endif
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
voice = 0;
break;
} // end of switch(lich)
// //collect remaining dibits at this point (before bad frame skip)
// for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
// dbuf[i+8] = getDibit(opts, state);
//go to end if bad returns from earlier
if (state->lastsynctype == -1)
goto END;
//collect remaining dibits at this point (after bad frame skip)
for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
dbuf[i+8] = getDibit(opts, state);
//enable these after good lich parity and known lich value
state->carrier = 1;
state->last_cc_sync_time = time(NULL);
//debug polarity (purely illustative, shows how frequently false sync can be attributed to wrong polarity)
#ifdef NXDN_DEBUG_LICH
if (state->lastsynctype == 28)
fprintf (stderr, "(+) ");
else fprintf (stderr, "(-) ");
#endif
//printframesync after determining we have a good lich and it has something in it
if (idas)
{
@@ -336,14 +369,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
#endif
}
//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)
nxdn_pn95_dibit_scrambler (state, dbuf, 182);
//seperate our dbuf (dibit_buffer) into individual bit array
for (int i = 0; i < 182; i++)
@@ -354,9 +380,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//sacch or scch bits
for (int i = 0; i < 60; i++)
{
sacch_bits[i] = nxdn_bit_buffer[i+16];
}
//facch
for (int i = 0; i < 144; i++)
@@ -367,21 +391,15 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//cac
for (int i = 0; i < 300; i++)
{
cac_bits[i] = nxdn_bit_buffer[i+16];
}
//udch or facch2
for (int i = 0; i < 348; i++)
{
facch2_bits[i] = nxdn_bit_buffer[i+16];
}
//udch2 or facch3
for (int i = 0; i < 288; i++)
{
facch3_bits[i] = nxdn_bit_buffer[i+16+60];
}
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
//sacch needs extra handling depending on superframe or non-superframe variety
@@ -465,7 +483,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (state->rkey_array[limazulu] != 0)
state->R = state->rkey_array[limazulu];
if (state->R != 0 && state->M == 1) state->nxdn_cipher_type = 0x1;
if (state->R != 0 && state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1;
//add additional time to last_sync_time for LimaZulu to hold on current frequency
//a little longer without affecting normal scan time on trunk_hangtime variable
@@ -521,7 +539,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (voice && facch == 1) //facch steal 1 -- before voice
{
//force scrambler here, but with unspecified key (just use what's loaded)
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
if (state->forced_alg_id == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
//roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
{
@@ -547,32 +565,38 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
else state->nxdn_sacch_non_superframe = FALSE;
//TODO Later: Add Direction and/or LICH to all decoding functions
if (scch) nxdn_deperm_scch (opts, state, sacch_bits, direction);
if (udch2) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 0);
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
//Type-D "IDAS"
if (scch) idas_scch(opts, state, sacch_bits, direction);
if (udch2) idas_facch3_udch2(opts, state, facch3_bits, 0);
if (facch3) idas_facch3_udch2(opts, state, facch3_bits, 1);
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
//NXDN Conventional and Type-C
if (sacch) nxdn_sacch(opts, state, sacch_bits);
if (cac) nxdn_cac(opts, state, cac_bits);
//Seperated UDCH user data from facch2 data
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
//DCR
if (sacch2) nxdn_deperm_sacch2(opts, state, sacch_bits);
if (pich_tch & 1) nxdn_deperm_pich_tch(opts, state, facch_bits_a);
if (pich_tch & 2) nxdn_deperm_pich_tch(opts, state, facch_bits_b);
//only run facch in second slot if its not equal to the first one
//ideally, this would work better AFTER decoding/FEC
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
if (facch & 2)
//Duplicate facch check inside decoder. store and compare for facch message, depending on passed frame value
if (facch == 3)
{
if (memcmp (facch_bits_a, facch_bits_b, 144) != 0)
nxdn_deperm_facch(opts, state, facch_bits_b);
if (facch & 1) nxdn_facch1(opts, state, facch_bits_a, 1);
if (facch & 2) nxdn_facch1(opts, state, facch_bits_b, 2);
}
else //facch1 steals, or single facch only messages
{
if (facch & 1) nxdn_facch1(opts, state, facch_bits_a, 0);
if (facch & 2) nxdn_facch1(opts, state, facch_bits_b, 0);
}
//Seperated UDCH user data from facch2 data
if (udch) nxdn_facch2_udch(opts, state, facch2_bits, 0);
if (facch2) nxdn_facch2_udch(opts, state, facch2_bits, 1);
//Japanese DCR
if (sacch2) dcr_sacch(opts, state, sacch_bits);
if (pich_tch & 1) dcr_pich_tch(opts, state, facch_bits_a, lich);
if (pich_tch & 2) dcr_pich_tch(opts, state, facch_bits_b, lich);
//EHR only (TODO: Make an EFR mode? never observed)
if (voice)
{
//restore MBE file open here
@@ -580,7 +604,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//update last voice sync time
state->last_vc_sync_time = time(NULL);
//turn on scrambler if forced by user option
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
if (state->forced_alg_id == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
//process voice frame
nxdn_voice (opts, state, voice, dbuf);
}
@@ -628,7 +652,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//if rejected sync, reset carrier and synctype as well
if (state->lastsynctype == -1)
{
state->carrier = 0;
state->carrier = 0; //this is why the "Sync: no sync" doesn't show in terminal if we have a bad sync (not an issue, just not consistent)
state->synctype = -1;
}
}

View File

@@ -53,7 +53,7 @@ const int nZ[36] = { 5, 3, 4, 2, 3, 1,
13, 2, 12, 1, 11, 0
};
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182])
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf)
{
int i;
int start, stop;

View File

@@ -1,22 +1,24 @@
/*-------------------------------------------------------------------------------
* p25_12.c
* P25p1 1/2 Rate Simple Trellis Decoder
* P25p1 1/2 Rate Viterbi Trellis Decoder (Improved)
*
* LWVMOBILE
* 2023-10 DSD-FME Florida Man Edition
* 2026-02 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include <limits.h> // For UINT8_MAX as infinity proxy
uint8_t p25_interleave[98] = {
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95
};
//this is a convertion table for converting the dibit pairs into constellation points
uint8_t p25_constellation_map[16] = {
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
};
//digitized dibit to OTA symbol conversion for reference
@@ -25,19 +27,21 @@ uint8_t p25_constellation_map[16] = {
//finite state machine values
uint8_t p25_fsm[16] = {
0,15,12,3,
4,11,8,7,
13,2,1,14,
9,6,5,10 };
0,15,12,3,
4,11,8,7,
13,2,1,14,
9,6,5,10
};
//this is a dibit-pair to trellis dibit transition matrix (SDRTrunk and Ossmann)
//when evaluating hamming distance, we want to use this xor the dibit-pair nib,
//and not the fsm xor the constellation point
uint8_t p25_dtm[16] = {
2,12,1,15,
14,0,13,3,
9,7,10,4,
5,11,6,8 };
2,12,1,15,
14,0,13,3,
9,7,10,4,
5,11,6,8
};
int count_bits(uint8_t b, int slen)
{
@@ -50,37 +54,13 @@ int count_bits(uint8_t b, int slen)
return i;
}
uint8_t find_min(uint8_t list[4], int len)
{
int min = list[0];
uint8_t index = 0;
int i;
for (i = 1; i < len; i++)
{
if (list[i] < min)
{
min = list[i];
index = (uint8_t)i;
}
//NOTE: Disqualifying result on uniqueness can impact decoding
//let the CRC determine if the result is good or bad
//its not uncommon for two values of the same min
//distance to emerge, so its 50/50 each time
}
return index;
}
int p25_12(uint8_t * input, uint8_t treturn[12])
{
int i, j;
int irr_err = 0;
int i, t;
int total_err = 0; UNUSED(total_err);
uint8_t deinterleaved_dibits[98];
memset (deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
memset(deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
//deinterleave our input dibits
for (i = 0; i < 98; i++)
@@ -88,80 +68,86 @@ int p25_12(uint8_t * input, uint8_t treturn[12])
//pack the input into nibbles (dibit pairs)
uint8_t nibs[49];
memset (nibs, 0, sizeof(nibs));
memset(nibs, 0, sizeof(nibs));
for (i = 0; i < 49; i++)
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
//convert our dibit pairs into constellation point values
uint8_t point[49];
memset (point, 0xFF, sizeof(point));
memset(point, 0xFF, sizeof(point));
for (i = 0; i < 49; i++)
point[i] = p25_constellation_map[nibs[i]];
//debug view points
// fprintf (stderr, "\n P =");
// for (i = 0; i < 49; i++)
// fprintf (stderr, " %02d", point[i]);
// Viterbi setup: 4 states, 49 stages
// Use UINT8_MAX / 2 as proxy for infinity (max possible errors per path ~196)
const uint8_t INF = UINT8_MAX / 2;
uint8_t metric[2][4]; // Ping-pong: [0] prev, [1] curr
uint8_t path[49][4]; // path[t][s] = best prev_state to reach state s at stage t
uint8_t tdibits[49]; // Decoded trellis dibits (states along best path)
//convert constellation points into tdibit values using the FSM
uint8_t state = 0;
uint8_t tdibits[49]; //trellis dibits 1/2 rate
memset (tdibits, 0xF, sizeof(tdibits));
uint8_t hd[4]; //array of the four fsm words hamming distance
memset (hd, 0, sizeof(hd));
uint8_t min = 0;
for (i = 0; i < 49; i++)
{
for (j = 0; j < 4; j++)
{
if ( p25_fsm[(state*4)+j] == point[i] )
{
//return our tdibit value and state for the next point
tdibits[i] = state = (uint8_t)j;
break;
}
}
//if tdibit value is greater than 3, then we have a decoding error
if (tdibits[i] == 0xF)
{
irr_err++; //increment number of errors
//debug position of error and state value
// fprintf (stderr, " %d:%d;", i, state);
//this method only seems to be reliable up to 1-2 bit errors...sometimes
for (j = 0; j < 4; j++)
hd[j] = count_bits ( ((nibs[i] ^ p25_dtm[(state*4)+j]) & 0xF ), 4 );
min = find_min (hd, 4);
tdibits[i] = state = min;
memset (hd, 0, sizeof(hd));
min = 0;
}
// Initialize: Start from state 0 before first symbol, metric 0
int curr = 0, prev = 1;
memset(metric, 0xFF, sizeof(metric)); // Set to high value
memset(path, 0, sizeof(path)); // Zero-init paths
// Stage 0 (first symbol): From initial state 0 to each possible next_s
memset(metric[curr], INF, sizeof(metric[0]));
for (int next_s = 0; next_s < 4; next_s++) {
int branch = count_bits(nibs[0] ^ p25_dtm[0 * 4 + next_s], 4);
metric[curr][next_s] = (uint8_t)branch;
path[0][next_s] = 0; // Prev state is initial 0
total_err += branch; // Accumulate for return (total path errors)
}
//debug view tdibits/states
// fprintf (stderr, "\n T =");
// for (i = 0; i < 49; i++)
// fprintf (stderr, " %02d", tdibits[i]);
// Stages 1 to 48
for (t = 1; t < 49; t++) {
// Swap buffers
prev = curr;
curr = 1 - curr;
memset(metric[curr], INF, sizeof(metric[0]));
//pack tdibits into return payload bytes
for (int next_s = 0; next_s < 4; next_s++) {
uint8_t min_dist = INF;
uint8_t best_prev = 0;
for (int prev_s = 0; prev_s < 4; prev_s++) {
if (metric[prev][prev_s] == INF) continue;
int branch = count_bits(nibs[t] ^ p25_dtm[prev_s * 4 + next_s], 4);
uint8_t cand = (uint8_t)(metric[prev][prev_s] + branch);
if (cand < min_dist) {
min_dist = cand;
best_prev = (uint8_t)prev_s;
}
}
metric[curr][next_s] = min_dist;
path[t][next_s] = best_prev;
}
}
// Find final state with minimum metric
uint8_t min_final = 0;
uint8_t min_metric = metric[curr][0];
for (int s = 1; s < 4; s++) {
if (metric[curr][s] < min_metric) {
min_metric = metric[curr][s];
min_final = (uint8_t)s;
}
}
// Traceback: Reconstruct state sequence (tdibits[t] = state after symbol t)
tdibits[48] = min_final;
for (t = 48; t >= 1; t--) {
tdibits[t - 1] = path[t][tdibits[t]];
}
//pack tdibits into return payload bytes (use first 48 as original)
for (i = 0; i < 12; i++)
treturn[i] = (tdibits[(i*4)+0] << 6) | (tdibits[(i*4)+1] << 4) | (tdibits[(i*4)+2] << 2) | tdibits[(i*4)+3];
//trellis point/state err tally
// if (irr_err != 0)
// fprintf (stderr, " P_ERR = %d", irr_err);
return (irr_err);
}
//debug what was the cost metric on this
// fprintf (stderr, " R12 Min Metric = %d; ", min_metric);
// Return total Hamming errors in best path
return (int)min_metric;
}

View File

@@ -26,7 +26,7 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
*/
//return 0 if channel value is 0 or 0xFFFF
if (channel == 0) return 0;
// if (channel == 0) return 0; //this is a valid use-case on P25 (observed) this has been in code since Feb 2024, how was this issue not discovered before now
if (channel == 0xFFFF) return 0;
//Note: Base Frequency is calculated as (Base Frequency) x (0.000005 MHz) from the IDEN_UP message.

View File

@@ -310,6 +310,18 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
{
//clear stale keys if loaded
if (state->keyloader == 1)
{
state->R = 0;
state->A1[0] = 0;
state->A2[0] = 0;
state->A3[0] = 0;
state->A4[0] = 0;
state->aes_key_loaded[0] = 0;
// state->H = 0; //shim for above (this apply here?)
}
//Will we need to check for a symbolrate change here, can a P25p2 TDMA-CC system
//revert back to a phase 1 traffic channel or carry a phase 1 traffic channel?
if (state->p25_cc_is_tdma == 1)
@@ -494,16 +506,16 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
fprintf (stderr, " MFIDA4 (Harris) GPS Block 1");
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
memcpy(state->dmr_pdu_sf[0], LCW_bits, 16*sizeof(uint8_t)); //opcode and mfid for check
memcpy(state->dmr_pdu_sf[0]+40, LCW_bits+16, 56*sizeof(uint8_t)); //+40 offset to match the vPDU decoder
memcpy(state->dmr_pdu_sf[0]+16, LCW_bits+16, 56*sizeof(uint8_t)); //contents of message in this block
}
else if (lc_mfid == 0xA4 && lc_opcode == 0x2B)
{
fprintf (stderr, " MFIDA4 (Harris) GPS Block 2");
memcpy(state->dmr_pdu_sf[0]+40+56, LCW_bits+16, 56*sizeof(uint8_t)); //+40 +56 to offset first block
memcpy(state->dmr_pdu_sf[0]+16+56, LCW_bits+16, 56*sizeof(uint8_t)); //+16 +56 to offset first block
uint16_t check = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[0][0], 16);
if (check == 0x2AA4)
nmea_harris(opts, state, state->dmr_pdu_sf[0], (uint32_t)state->lastsrc, 0);
harris_lptt(opts, state, state->dmr_pdu_sf[0]+16, (uint32_t)state->lastsrc, 0, 1);
else fprintf (stderr, " Missing GPS Block 1");
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
}
@@ -526,6 +538,15 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
tait_iso7_embedded_alias_decode(opts, state, 0, 8, LCW_bits);
}
//observed alongside Tait Talker Alias ID: 10T
else if (lc_mfid == 0xD8 && lc_format == 0x01)
{
uint32_t wacn = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 20);
uint32_t sys = (uint32_t)ConvertBitIntoBytes(&LCW_bits[36], 12);
uint32_t rid = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
fprintf (stderr, " MFIDD8 (Tait) Subscriber FQ-SUID: %05X.%03X.%d", wacn, sys, rid);
}
//not a duplicate, this one will print if not MFID 0 or 1
else
{

View File

@@ -1,17 +1,138 @@
#include <itpp/itcomm.h>
// BCH decoder implementation from OP25 (Copyright 2010, KA1RBI, GPL license)
#include "dsd.h"
#include "p25p1_check_nid.h"
#include <vector>
typedef std::vector<bool> bit_vector;
static const int bchGFexp[64] = {
1, 2, 4, 8, 16, 32, 3, 6, 12, 24, 48, 35, 5, 10, 20, 40,
19, 38, 15, 30, 60, 59, 53, 41, 17, 34, 7, 14, 28, 56, 51, 37,
9, 18, 36, 11, 22, 44, 27, 54, 47, 29, 58, 55, 45, 25, 50, 39,
13, 26, 52, 43, 21, 42, 23, 46, 31, 62, 63, 61, 57, 49, 33, 0
};
static const int bchGFlog[64] = {
-1, 0, 1, 6, 2, 12, 7, 26, 3, 32, 13, 35, 8, 48, 27, 18,
4, 24, 33, 16, 14, 52, 36, 54, 9, 45, 49, 38, 28, 41, 19, 56,
5, 62, 25, 11, 34, 31, 17, 47, 15, 23, 53, 51, 37, 44, 55, 40,
10, 61, 46, 30, 50, 22, 39, 43, 29, 60, 42, 21, 20, 59, 57, 58
};
using namespace itpp;
int bchDec(bit_vector& Codeword)
{
int elp[24][22], S[23];
int D[23], L[24], uLu[24];
int locn[11], reg[12];
int i, j, U, q, count;
int SynError, CantDecode;
// Ideas taken from http://op25.osmocom.org/trac/wiki.png/browser/op25/gr-op25/lib/decoder_ff_impl.cc
// See also p25_training_guide.pdf page 48.
// See also tia-102-baaa-a-project_25-fdma-common_air_interface.pdf page 40.
// BCH encoder/decoder implementation from IT++. GNU GPL 3 license.
SynError = 0; CantDecode = 0;
itpp::BCH bch(63, 16, 11, "6 3 3 1 1 4 1 3 6 7 2 3 5 4 5 3", true);
for (i = 1; i <= 22; i++) {
S[i] = 0;
for (j = 0; j <= 62; j++) {
if (Codeword[j]) {
S[i] = S[i] ^ bchGFexp[(i * j) % 63];
}
}
if (S[i]) { SynError = 1; }
S[i] = bchGFlog[S[i]];
}
if (SynError) { // if there are errors, try to correct them
L[0] = 0; uLu[0] = -1; D[0] = 0; elp[0][0] = 0;
L[1] = 0; uLu[1] = 0; D[1] = S[1]; elp[1][0] = 1;
for (i = 1; i <= 21; i++) {
elp[0][i] = -1; elp[1][i] = 0;
}
U = 0;
do {
U = U + 1;
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] = bchGFlog[elp[U][i]];
}
} else {
q = U - 1;
while ((D[q] == -1) && (q > 0)) { q = q - 1; }
if (q > 0) {
j = q;
do { j = j - 1; if ((D[j] != -1) && (uLu[q] < uLu[j])) { q = j; }
} while (j > 0);
}
if (L[U] > L[q] + U - q) {
L[U + 1] = L[U];
} else {
L[U + 1] = L[q] + U - q;
}
for (i = 0; i <= 21; i++) {
elp[U + 1][i] = 0;
}
for (i = 0; i <= L[q]; i++) {
if (elp[q][i] != -1) {
elp[U + 1][i + U - q] = bchGFexp[(D[U] + 63 - D[q] + elp[q][i]) % 63];
}
}
for (i = 0; i <= L[U]; i++) {
elp[U + 1][i] = elp[U + 1][i] ^ elp[U][i];
elp[U][i] = bchGFlog[elp[U][i]];
}
}
uLu[U + 1] = U - L[U + 1];
if (U < 22) {
if (S[U + 1] != -1) { D[U + 1] = bchGFexp[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] = D[U + 1] ^ bchGFexp[(S[U + 1 - i] + bchGFlog[elp[U + 1][i]]) % 63];
}
}
D[U + 1] = bchGFlog[D[U + 1]];
}
} while ((U < 22) && (L[U + 1] <= 11));
U = U + 1;
if (L[U] <= 11) {
for (i = 0; i <= L[U]; i++) {
elp[U][i] = bchGFlog[elp[U][i]];
}
for (i = 1; i <= L[U]; i++) {
reg[i] = elp[U][i];
}
count = 0;
for (i = 1; i <= 63; i++) {
q = 1;
for (j = 1; j <= L[U]; j++) {
if (reg[j] != -1) {
reg[j] = (reg[j] + j) % 63;
q = q ^ bchGFexp[reg[j]];
}
}
if (q == 0) {
locn[count] = 63 - i;
count = count + 1;
}
}
if (count == L[U]) {
for (i = 0; i <= L[U] - 1; i++) {
Codeword[locn[i]] = Codeword[locn[i]] ^ 1;
}
CantDecode = count;
} else {
CantDecode = -1;
}
} else {
CantDecode = -2;
}
}
return CantDecode;
}
/**
* Convenience class to calculate the parity of the DUID values. Keeps a table with the expected outcomes
@@ -24,13 +145,13 @@ private:
unsigned char get_index(unsigned char x, unsigned char y)
{
return (x<<2) + y;
return (x << 2) + y;
}
public:
ParityTable()
{
for (unsigned int i=0; i<sizeof(table); i++) {
for (unsigned int i = 0; i < sizeof(table); i++) {
table[i] = 0;
}
table[get_index(1,1)] = 1;
@@ -39,9 +160,8 @@ public:
unsigned char get_value(unsigned char x, unsigned char y)
{
return table[get_index(x,y)];
return table[get_index(x, y)];
}
} parity_table;
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity)
@@ -49,31 +169,38 @@ int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity
int result;
// Fill up with the given input
bvec input(63);
for(unsigned int i=0; i<63; i++) {
input[i] = bch_code[i];
bit_vector codeword(63);
for (unsigned int i = 0; i < 63; i++) {
codeword[i] = bch_code[62-i];
}
// Decode it
bvec decoded, cw_isvalid;
bool ok = bch.decode(input, decoded, cw_isvalid);
int errors = bchDec(codeword);
bool ok = (errors >= 0);
if (!ok) {
// Decode failed
// fprintf (stderr, "NID ERR: %d; ", errors);
result = 0;
} else {
//reload the codeword back into correct bit order
for (unsigned int i = 0; i < 63; i++) {
bch_code[i] = codeword[62-i];
}
for (unsigned int i = 0; i < 63; i++) {
codeword[i] = bch_code[i];
}
// Take the NAC from the decoded output. It's a 12 bit number starting from position 0.
// Just convert the output from a binary sequence to an integer.
int nac = 0;
for (int i=0; i<12; i++) {
for (int i = 0; i < 12; i++) {
nac <<= 1;
nac |= (int)decoded[i];
nac |= (int)codeword[i];
}
*new_nac = nac;
// Take the fixed DUID from the encoded output. 4 bit value starting at position 12.
unsigned char new_duid_0 = (((int)decoded[12])<<1) + ((int)decoded[13]);
unsigned char new_duid_1 = (((int)decoded[14])<<1) + ((int)decoded[15]);
unsigned char new_duid_0 = (((int)codeword[12]) << 1) + ((int)codeword[13]);
unsigned char new_duid_1 = (((int)codeword[14]) << 1) + ((int)codeword[15]);
new_duid[0] = new_duid_0 + '0';
new_duid[1] = new_duid_1 + '0';
new_duid[2] = 0; // Null terminate
@@ -82,12 +209,12 @@ int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity
unsigned char expected_parity = parity_table.get_value(new_duid_0, new_duid_1);
if (expected_parity != parity) {
// Ignore, not sure what to do
//fprintf (stderr, "Error in parity detected?");
// Parity mismatch – optional handling
// fprintf(stderr, "Error in parity detected?\n");
}
result = 1;
}
return result;
}
}

View File

@@ -143,7 +143,7 @@ void processMPDU(dsd_opts * opts, dsd_state * state)
//debug
// fprintf (stderr, " J:%d;", j); //use this with the P_ERR inside of 34 rate decoder to see where the failures occur
ec[j] = dmr_34 (tsbk_dibit, r34byte_b);
ec[j] = viterbi_r34 (tsbk_dibit, r34byte_b);
//shuffle 34 rate data into array
if (j != 0) //should never happen, but just in case

View File

@@ -457,8 +457,18 @@ void p25_decode_pdu_data(dsd_opts * opts, dsd_state * state, uint8_t * input, in
if (sap == 0 || sap == 4) //User Data or Packet Data (both are UDP typically, same format dmr UDP/IP data)
decode_ip_pdu (opts, state, len+1, input+ptr);
else if (sap == 48) //Tier 1 Location Service (or does it depend on the io bit?)
utf8_to_text(state, 1, len-ptr+1, input+ptr); //TODO, read initial string, i.e., $GPRMC and properly decode
//NMEA Tier 1 Location Reporting w/ Supplimentary $PP25 string
else if (sap == 48)
{
len += 12; //not sure how we ended up off by up to 12 on this, above negation too much?
state->dmr_lrrp_source[state->currentslot] = state->lastsrc;
state->dmr_lrrp_source[state->currentslot] = state->lasttg;
uint8_t input_bits[(len+1)*8];
memset(input_bits, 0, sizeof(input_bits));
unpack_byte_array_into_bit_array(input, input_bits, len);
fprintf (stderr, "\n ");
nmea_sentence_checker(opts, state, input_bits+ptr*8, state->currentslot, len*8);
}
// else //default catch all (debug only)
// {

View File

@@ -489,11 +489,53 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s",KYEL);
fprintf (stderr, " MFID 90 (Moto) Acknoledge Response: ");
fprintf (stderr, " MFID 90 (Moto) Acknowledge Response: ");
for (i = 2; i < 10; i++)
fprintf (stderr, "%02X", tsbk_byte[i]);
}
else if ( (tsbk_byte[0] & 0x3F) == 0x09 )
{
fprintf (stderr, "\n");
fprintf (stderr, " MFID 90 (Moto) Scan Marker: ");
for (i = 2; i < 10; i++)
fprintf (stderr, "%02X", tsbk_byte[i]);
}
else if ( (tsbk_byte[0] & 0x3F) == 0x0B )
{
uint16_t channel = (tsbk_byte[8] << 8) | tsbk_byte[9];
fprintf (stderr, "\n");
fprintf (stderr, " MFID 90 (Moto) ID Channel: %04X;", channel);
char sid_string[10];
memset(sid_string, 0, sizeof(sid_string));
int k = 0;
if (tsbk_byte[2] != 0)
{
fprintf (stderr, " Station ID: ");
uint8_t sid_bits[42];
memset(sid_bits, 0, sizeof(sid_bits));
unpack_byte_array_into_bit_array(tsbk_byte+2, sid_bits, 6);
for (i = 0; i < 42; i+=6)
{
uint8_t bsi_char = convert_bits_into_output(sid_bits+i, 6) + 43;
if (bsi_char > 0x1F && bsi_char < 0x7F)
{
fprintf (stderr, "%c", bsi_char);
sid_string[k++] = bsi_char;
}
else break;
}
}
//debug, may assign this string to something else later on
// fprintf (stderr, " - Str: %s;", sid_string);
process_channel_to_freq(opts, state, channel);
}
//Some of these Opcodes that aren't found in any TIA manual come from SDRTrunk (or other sources),
//but can't verify the accuracy of their meaning/context
@@ -505,22 +547,6 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
// fprintf (stderr, "%02X", tsbk_byte[i]);
// }
// else if ( (tsbk_byte[0] & 0x3F) == 0x09 )
// {
// fprintf (stderr, "\n");
// fprintf (stderr, " MFID 90 (Moto) Channel Loading: "); //don't understand the context for this one, waiting on units to arrive on channel?
// for (i = 2; i < 10; i++)
// fprintf (stderr, "%02X", tsbk_byte[i]);
// }
// else if ( (tsbk_byte[0] & 0x3F) == 0x0B )
// {
// fprintf (stderr, "\n");
// fprintf (stderr, " MFID 90 (Moto) Control Channel: "); //this appears to echo the 16-bit channel number for the main control channel/RFSS
// for (i = 2; i < 10; i++)
// fprintf (stderr, "%02X", tsbk_byte[i]);
// }
// else if ( (tsbk_byte[0] & 0x3F) == 0x0E )
// {
// fprintf (stderr, "\n");
@@ -537,14 +563,6 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
// fprintf (stderr, "%02X", tsbk_byte[i]);
// }
// else if ( (tsbk_byte[0] & 0x3F) == 0x15 ) //noted on RR, but not observed as of yet, this may actualy be a LCW and not a TSBK?
// {
// fprintf (stderr, "\n");
// fprintf (stderr, " MFID 90 (Moto) Talker Alias: ");
// for (i = 2; i < 10; i++)
// fprintf (stderr, "%02X", tsbk_byte[i]);
// }
// else if ( (tsbk_byte[0] & 0x3F) == 0x16 )
// {
// fprintf (stderr, "\n");

View File

@@ -1628,10 +1628,79 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
}
//Extended Function Command - Abbreviated (Generic non MFID variant)
if (MAC[1+len_a] == 0x64)
{
uint8_t class = MAC[2+len_a];
uint8_t operand = MAC[3+len_a];
uint32_t argument = (MAC[4+len_a] << 16) | (MAC[5+len_a] << 8) | MAC[6+len_a];
uint32_t target = (MAC[7+len_a] << 16) | (MAC[8+len_a] << 8) | MAC[9+len_a];
fprintf (stderr, "\n Extended Function Command: Class: %02X; Operand: %02X; Arg/Src: %06X; Target: %d; ", class, operand, argument, target);
//WIP: Further Decode
if (class == 0)
{
if ((operand & 0x7F) == 0x00) fprintf (stderr, "Radio Check; ");
else if ((operand & 0x7F) == 0x7D) fprintf (stderr, "Radio Detach; ");
else if ((operand & 0x7F) == 0x7E) fprintf (stderr, "Radio Uninhibit; ");
else if ((operand & 0x7F) == 0x7F) fprintf (stderr, "Radio Inhibit; ");
else fprintf (stderr, "Reserved; ");
//MSB is an ack of the above command
if ((operand >> 7) == 1) fprintf (stderr, "Ack; ");
}
else fprintf (stderr, "Other Command; ");
}
//MFID90 Group Regroup Add Command
if (MAC[1+len_a] == 0x81 && MAC[2+len_a] == 0x90) //needs MAC message len update, may work same as explicit enc regroup?
if (MAC[1+len_a] == 0x81 && MAC[2+len_a] == 0x90)
{
fprintf (stderr, "\n MFID90 Group Regroup Add Command ");
uint8_t len = MAC[3+len_a];
len_b = len;
}
//MFID90 Unknown Message <--was causing an issue on ABQ system w/ false GPS, SCCB and RFSS_STS_BCST MAC messages
if (MAC[1+len_a] == 0x8F && MAC[2+len_a] == 0x90)
{
uint8_t len = MAC[3+len_a]; //observed len values are 0x04, 0x08, and 0x0B (12)
//constraints on message len
if (len > 12)
len = 12;
// else if (len < 4)
// len = 4;
fprintf (stderr, "%s",KCYN);
fprintf (stderr, "\n MFID 90 (Moto); Opcode: %02llX; ", MAC[1+len_a] & 0x3F); //mask, or not?
for (int i = 4; i < len+1; i++)
fprintf (stderr, "%02llX", MAC[i+len_a]);
fprintf (stderr, " %s",KNRM);
len_b = len;
}
//MFID90 Unknown Message 2 (usually in second message, unknown, maybe just filler?)
if (MAC[1+len_a] == 0xBF && MAC[2+len_a] == 0x90)
{
uint8_t len = MAC[3+len_a]; //observed len values are 0x03
//constraints on message len
if (len > 3)
len = 3;
else if (len < 3)
len = 3;
fprintf (stderr, "%s",KCYN);
fprintf (stderr, "\n MFID 90 (Moto); Opcode: %02llX; ", MAC[1+len_a] & 0x3F); //mask, or not?
for (int i = 4; i < len+1; i++)
fprintf (stderr, "%02llX", MAC[i+len_a]);
fprintf (stderr, " %s",KNRM);
len_b = len;
}
//the len on these indicate they are always a single messages, foregoing the +len_a index pointer
@@ -1772,9 +1841,15 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
if (MAC[len_a+1] == 0x80 && MAC[len_a+2] != 0xA4 && MAC[len_a+2] != 0x90)
{
int unk1 = MAC[len_a+1]; //assuming this is the octet set for the 'manufacturer specific' message, may only be the MSBit
int unk2 = MAC[len_a+2]; //This field is observed as 0xAA, unknown if this is an opcode, or other MFID
int unk2 = MAC[len_a+2]; //This field is observed as 0xAA, its possible its a derived message of 0x2A from P25p1 LCW format
int mfid = MAC[len_a+3]; //This is where the 0xA4 (Harris) Identifier is found in this message, as opposed to +2
int len = MAC[len_a+4] & 0x3F;; //0x11 or 17 dec sounds reasonable, but cannot verify
int len = MAC[len_a+4] & 0x3F; //len of this message (should always be 17)
//bugfix observed on random errant second MAC message on Phase 2
//confirmed this MAC message is always 17 (0x11)
if (len != 0x11)
goto END_PDU;
fprintf (stderr, "\n MFID %02X (Harris); Len: %d; Opcode: %02X/%02X;", mfid, len, unk1, unk2);
//convert bytes to bits, may move this up top
@@ -1791,8 +1866,7 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
if (slot == 0 && state->lastsrc != 0) tsrc = state->lastsrc;
if (slot == 1 && state->lastsrcR != 0) tsrc = state->lastsrcR;
// harris_gps (opts, state, slot, mac_bits); //fallback
nmea_harris (opts, state, mac_bits+0, tsrc, slot); //new
harris_lptt (opts, state, mac_bits+40, tsrc, slot, 2);
//debug - just dump payload
// for (i = 0; i < 24; i++)

View File

@@ -317,6 +317,18 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
if (opts->floating_point == 1)
state->aout_gain = opts->audio_gain;
//clear stale keys if loaded
if (state->keyloader == 1)
{
state->R = 0;
state->A1[0] = 0;
state->A2[0] = 0;
state->A3[0] = 0;
state->A4[0] = 0;
state->aes_key_loaded[0] = 0;
// state->H = 0; //shim for above (this apply here?)
}
}
if (state->currentslot == 0)
{
@@ -346,6 +358,18 @@ void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]
if (opts->floating_point == 1)
state->aout_gainR = opts->audio_gain;
//clear stale keys if loaded
if (state->keyloader == 1)
{
state->RR = 0;
state->A1[1] = 0;
state->A2[1] = 0;
state->A3[1] = 0;
state->A4[1] = 0;
state->aes_key_loaded[1] = 0;
// state->H = 0; //shim for above (this apply here?)
}
}
//Return to CC on MAC_END_PTT if other slot is idle
@@ -760,6 +784,18 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
if (opts->floating_point == 1)
state->aout_gain = opts->audio_gain; //reset
//clear stale keys if loaded
if (state->keyloader == 1)
{
state->R = 0;
state->A1[0] = 0;
state->A2[0] = 0;
state->A3[0] = 0;
state->A4[0] = 0;
state->aes_key_loaded[0] = 0;
// state->H = 0; //shim for above (this apply here?)
}
}
if (state->currentslot == 1)
{
@@ -785,6 +821,19 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
//reset gain
if (opts->floating_point == 1)
state->aout_gainR = opts->audio_gain;
//clear stale keys if loaded
if (state->keyloader == 1)
{
state->RR = 0;
state->A1[1] = 0;
state->A2[1] = 0;
state->A3[1] = 0;
state->A4[1] = 0;
state->aes_key_loaded[1] = 0;
// state->H = 0; //shim for above (this apply here?)
}
}
//Return to CC on MAC_END_PTT if other slot is idle

File diff suppressed because it is too large Load Diff

4632
src/sjis_unicode.c Normal file

File diff suppressed because it is too large Load Diff

541
src/soft_viterbi_k5.c Normal file
View File

@@ -0,0 +1,541 @@
/*-------------------------------------------------------------------------------
* soft_viterbi_k5.c
* Soft Decision Viterbi K5 Contraint for M17, NXDN, and YSF Fusion
*
* Viterbi Code from or Modified from libM17 (see bottom)
* Wojciech Kaczmarski, SP5WWP
*
* LWVMOBILE
* 2026-01 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//Ripped from libM17
#define K 5 //constraint length
#define NUM_STATES (1 << (K - 1)) //number of states
static uint32_t prevMetrics[NUM_STATES];
static uint32_t currMetrics[NUM_STATES];
static uint32_t prevMetricsData[NUM_STATES];
static uint32_t currMetricsData[NUM_STATES];
static uint16_t viterbi_history[600*2];
/**
* @brief Decode unpunctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param len Input length in bits.
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
{
viterbi_reset();
size_t pos = 0;
for(size_t i = 0; i < len; i += 2)
{
uint16_t s0 = in[i];
uint16_t s1 = in[i + 1];
viterbi_decode_bit(s0, s1, pos);
pos++;
}
uint32_t err = viterbi_chainback(out, pos, len/2);
//debug
// fprintf (stderr, "\n vcb: \n");
// for (size_t i = 0; i < len; i++)
// fprintf (stderr, "%02X", out[i]);
return err;
}
/**
* @brief Decode punctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param punct Puncturing matrix.
* @param in_len Input data length.
* @param p_len Puncturing matrix length (entries).
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
{
uint16_t umsg[600*2]; //unpunctured message
uint16_t p=0; //puncturer matrix entry
uint16_t u=0; //bits count - unpunctured message
uint16_t i=0; //bits read from the input message
while(i<in_len)
{
if(punct[p])
{
umsg[u]=in[i];
i++;
}
else
{
umsg[u]=0x7FFF;
}
u++;
p++;
p%=p_len;
}
//debug
// fprintf (stderr, " p: %d, u: %d; p_len: %d; ", p, u, p_len); //
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
}
/**
* @brief Decode one bit and update trellis.
*
* @param s0 Cost of the first symbol.
* @param s1 Cost of the second symbol.
* @param pos Bit position in history.
*/
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos)
{
static const uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
static const uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0, 0, 0xFFFF, 0xFFFF, 0};
for(uint8_t i = 0; i < NUM_STATES/2; i++)
{
uint32_t metric = q_abs_diff(COST_TABLE_0[i], s0)
+ q_abs_diff(COST_TABLE_1[i], s1);
uint32_t m0 = prevMetrics[i] + metric;
uint32_t m1 = prevMetrics[i + NUM_STATES/2] + (0x1FFFE - metric);
uint32_t m2 = prevMetrics[i] + (0x1FFFE - metric);
uint32_t m3 = prevMetrics[i + NUM_STATES/2] + metric;
uint8_t i0 = 2 * i;
uint8_t i1 = i0 + 1;
if(m0 >= m1)
{
viterbi_history[pos]|=(1<<i0);
currMetrics[i0] = m1;
}
else
{
viterbi_history[pos]&=~(1<<i0);
currMetrics[i0] = m0;
}
if(m2 >= m3)
{
viterbi_history[pos]|=(1<<i1);
currMetrics[i1] = m3;
}
else
{
viterbi_history[pos]&=~(1<<i1);
currMetrics[i1] = m2;
}
}
//swap
uint32_t tmp[NUM_STATES];
for(uint8_t i=0; i<NUM_STATES; i++)
{
tmp[i]=currMetrics[i];
}
for(uint8_t i=0; i<NUM_STATES; i++)
{
currMetrics[i]=prevMetrics[i];
prevMetrics[i]=tmp[i];
}
}
/**
* @brief History chainback to obtain final byte array.
*
* @param out Destination byte array for decoded data.
* @param pos Starting position for the chainback.
* @param len Length of the output in bits.
* @return Minimum Viterbi cost at the end of the decode sequence.
*/
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
{
uint8_t state = 0;
size_t bitPos = len+4;
while(pos > 0)
{
bitPos--;
pos--;
uint16_t bit = viterbi_history[pos]&((1<<(state>>4)));
state >>= 1;
if(bit)
{
state |= 0x80;
out[bitPos/8]|=1<<(7-(bitPos%8));
}
}
uint32_t cost = prevMetrics[0];
for(size_t i = 0; i < NUM_STATES; i++)
{
uint32_t m = prevMetrics[i];
if(m < cost) cost = m;
}
//debug
// fprintf (stderr, "\n cb: \n");
// for (size_t i = 0; i < len; i++)
// fprintf (stderr, "%02X", out[i]);
return cost;
}
/**
* @brief Reset the decoder state. No args.
*
*/
void viterbi_reset(void)
{
memset(viterbi_history, 0, sizeof(viterbi_history));
memset(currMetrics, 0, sizeof(currMetrics));
memset(prevMetrics, 0, sizeof(prevMetrics));
memset(currMetricsData, 0, sizeof(currMetricsData));
memset(prevMetricsData, 0, sizeof(prevMetricsData));
}
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
{
if(v2 > v1) return v2 - v1;
return v1 - v2;
}
//same as symbol levels in m17.h (m17-fme version)
const int8_t symbol_list[4]={-3, -1, +1, +3};
/**
* @brief Slice payload symbols into soft dibits.
* Input (RRC filtered baseband sampled at symbol centers)
* should be already normalized to {-3, -1, +1 +3}.
* @param out Soft valued dibits (type-4).
* @param inp Array of 184 floats (1 sample per symbol).
*/
void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD])
{
for(uint8_t i=0; i<SYM_PER_PLD; i++)
{
//bit 0
if(inp[i]>=symbol_list[3])
{
out[i*2+1]=0xFFFF;
}
else if(inp[i]>=symbol_list[2])
{
out[i*2+1]=-(float)0xFFFF/(symbol_list[3]-symbol_list[2])*symbol_list[2]+inp[i]*(float)0xFFFF/(symbol_list[3]-symbol_list[2]);
}
else if(inp[i]>=symbol_list[1])
{
out[i*2+1]=0x0000;
}
else if(inp[i]>=symbol_list[0])
{
out[i*2+1]=(float)0xFFFF/(symbol_list[1]-symbol_list[0])*symbol_list[1]-inp[i]*(float)0xFFFF/(symbol_list[1]-symbol_list[0]);
}
else
{
out[i*2+1]=0xFFFF;
}
//bit 1
if(inp[i]>=symbol_list[2])
{
out[i*2]=0x0000;
}
else if(inp[i]>=symbol_list[1])
{
out[i*2]=0x7FFF-inp[i]*(float)0xFFFF/(symbol_list[2]-symbol_list[1]);
}
else
{
out[i*2]=0xFFFF;
}
}
}
/**
* @brief Slice payload symbols into soft dibits.
* Input (RRC filtered baseband sampled at symbol centers)
* should be already normalized to {-3, -1, +1 +3}.
* @param out Soft valued dibits (type-4).
* @param inp Array of len floats (1 sample per symbol).
* @param len legnth value to run this at
*/
void slice_symbols_to_len(uint16_t * out, const float * inp, int len)
{
for(int i=0; i<len; i++)
{
//bit 0
if(inp[i]>=symbol_list[3])
{
out[i*2+1]=0xFFFF;
}
else if(inp[i]>=symbol_list[2])
{
out[i*2+1]=-(float)0xFFFF/(symbol_list[3]-symbol_list[2])*symbol_list[2]+inp[i]*(float)0xFFFF/(symbol_list[3]-symbol_list[2]);
}
else if(inp[i]>=symbol_list[1])
{
out[i*2+1]=0x0000;
}
else if(inp[i]>=symbol_list[0])
{
out[i*2+1]=(float)0xFFFF/(symbol_list[1]-symbol_list[0])*symbol_list[1]-inp[i]*(float)0xFFFF/(symbol_list[1]-symbol_list[0]);
}
else
{
out[i*2+1]=0xFFFF;
}
//bit 1
if(inp[i]>=symbol_list[2])
{
out[i*2]=0x0000;
}
else if(inp[i]>=symbol_list[1])
{
out[i*2]=0x7FFF-inp[i]*(float)0xFFFF/(symbol_list[2]-symbol_list[1]);
}
else
{
out[i*2]=0xFFFF;
}
}
}
/**
* @brief Soft logic NOT.
*
* @param a Input A.
* @return uint16_t Output = not A.
*/
uint16_t soft_bit_NOT(const uint16_t a)
{
return 0xFFFFU-a;
}
//randomizing pattern for M17
const uint8_t rand_seq[46]=
{
0xD6, 0xB5, 0xE2, 0x30, 0x82, 0xFF, 0x84, 0x62, 0xBA, 0x4E, 0x96, 0x90, 0xD8, 0x98, 0xDD, 0x5D, 0x0C, 0xC8, 0x52, 0x43, 0x91, 0x1D, 0xF8,
0x6E, 0x68, 0x2F, 0x35, 0xDA, 0x14, 0xEA, 0xCD, 0x76, 0x19, 0x8D, 0xD5, 0x80, 0xD1, 0x33, 0x87, 0x13, 0x57, 0x18, 0x2D, 0x29, 0x78, 0xC3
};
/**
* @brief Randomize type-4 soft bits.
*
* @param inp Input 368 soft type-4 bits.
*/
void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2])
{
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
{
if((rand_seq[i/8]>>(7-(i%8)))&1) //flip bit if '1'
{
inp[i]=soft_bit_NOT(inp[i]);
}
}
}
//interleaver pattern for M17
const uint16_t intrl_seq[SYM_PER_PLD*2]=
{
0, 137, 90, 227, 180, 317, 270, 39, 360, 129, 82, 219, 172, 309, 262, 31,
352, 121, 74, 211, 164, 301, 254, 23, 344, 113, 66, 203, 156, 293, 246, 15,
336, 105, 58, 195, 148, 285, 238, 7, 328, 97, 50, 187, 140, 277, 230, 367,
320, 89, 42, 179, 132, 269, 222, 359, 312, 81, 34, 171, 124, 261, 214, 351,
304, 73, 26, 163, 116, 253, 206, 343, 296, 65, 18, 155, 108, 245, 198, 335,
288, 57, 10, 147, 100, 237, 190, 327, 280, 49, 2, 139, 92, 229, 182, 319,
272, 41, 362, 131, 84, 221, 174, 311, 264, 33, 354, 123, 76, 213, 166, 303,
256, 25, 346, 115, 68, 205, 158, 295, 248, 17, 338, 107, 60, 197, 150, 287,
240, 9, 330, 99, 52, 189, 142, 279, 232, 1, 322, 91, 44, 181, 134, 271,
224, 361, 314, 83, 36, 173, 126, 263, 216, 353, 306, 75, 28, 165, 118, 255,
208, 345, 298, 67, 20, 157, 110, 247, 200, 337, 290, 59, 12, 149, 102, 239,
192, 329, 282, 51, 4, 141, 94, 231, 184, 321, 274, 43, 364, 133, 86, 223,
176, 313, 266, 35, 356, 125, 78, 215, 168, 305, 258, 27, 348, 117, 70, 207,
160, 297, 250, 19, 340, 109, 62, 199, 152, 289, 242, 11, 332, 101, 54, 191,
144, 281, 234, 3, 324, 93, 46, 183, 136, 273, 226, 363, 316, 85, 38, 175,
128, 265, 218, 355, 308, 77, 30, 167, 120, 257, 210, 347, 300, 69, 22, 159,
112, 249, 202, 339, 292, 61, 14, 151, 104, 241, 194, 331, 284, 53, 6, 143,
96, 233, 186, 323, 276, 45, 366, 135, 88, 225, 178, 315, 268, 37, 358, 127,
80, 217, 170, 307, 260, 29, 350, 119, 72, 209, 162, 299, 252, 21, 342, 111,
64, 201, 154, 291, 244, 13, 334, 103, 56, 193, 146, 283, 236, 5, 326, 95,
48, 185, 138, 275, 228, 365, 318, 87, 40, 177, 130, 267, 220, 357, 310, 79,
32, 169, 122, 259, 212, 349, 302, 71, 24, 161, 114, 251, 204, 341, 294, 63,
16, 153, 106, 243, 196, 333, 286, 55, 8, 145, 98, 235, 188, 325, 278, 47
};
/**
* @brief Reorder (interleave) 368 soft bits.
*
* @param outp Reordered soft bits.
* @param inp Input soft bits.
*/
void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_PLD*2])
{
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
outp[i]=inp[intrl_seq[i]];
}
//NXDN all-in-one function to go from punctured bits to valid output, return viterbi error
uint32_t nxdn_soft_decision_viterbi(uint8_t * bits, const uint16_t * interleave, uint8_t * puncture, int d_len, int p_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes)
{
uint32_t error = 0;
uint16_t soft_bit[1000];
uint8_t temp_bits[500];
memset(temp_bits, 0, sizeof(temp_bits));
memset(soft_bit, 0, sizeof(soft_bit));
//deinterleaved bits
uint8_t deinterleaved_bits[500];
memset(deinterleaved_bits, 0, sizeof(deinterleaved_bits));
//deinterleaving the bits
for (int i = 0; i < d_len; i++)
deinterleaved_bits[interleave[i]] = bits[i];
//converting back to dibits
uint8_t dbuf[500];
memset(dbuf, 0, sizeof(dbuf));
for (int i = 0; i < d_len/2; i++)
dbuf[i] = (deinterleaved_bits[(i*2)+0] << 1) | deinterleaved_bits[(i*2)+1];
float sbuf[500];
memset(sbuf, 0.0f, sizeof(sbuf));
//convert dbuf into a symbol array
for (int i = 0; i < d_len/2; i++)
{
if (dbuf[i] == 0) sbuf[i] = +1.0f;
else if (dbuf[i] == 1) sbuf[i] = +3.0f;
else if (dbuf[i] == 2) sbuf[i] = -1.0f;
else if (dbuf[i] == 3) sbuf[i] = -3.0f;
else sbuf[i] = +0.0f;
}
//slice symbols to soft dibits
slice_symbols_to_len(soft_bit, sbuf, d_len/2);
//viterbi
error = viterbi_decode_punctured(viterbi_bytes, soft_bit, puncture, d_len, p_len);
//debug
// fprintf (stderr, " Ve: %1.1f; ", (float)error/(float)0xFFFF);
//debug
// for (int i = 0; i < num_bytes+1; i++)
// fprintf(stderr, "%02X", viterbi_bytes[i]);
//load viterbi_bytes into bits
unpack_byte_array_into_bit_array(viterbi_bytes, temp_bits, num_bytes+1);
//rearrange bits to get correct offset
for (int i = 0; i < num_bytes*8; i++)
viterbi_bits[i] = temp_bits[i+offset];
//zero out viterbi_bytes to reload packed appropriately
for (int i = 0; i < num_bytes; i++)
viterbi_bytes[i] = 0;
pack_bit_array_into_byte_array(viterbi_bits, viterbi_bytes, num_bytes);
return error;
}
//ysf fake puncture
uint8_t ysf_puncture[4] = {1,1,1,1};
//YSF all-in-one function to go from dibits to valid output, return viterbi error
uint32_t ysf_soft_decision_viterbi(uint8_t * dbuf, int d_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes)
{
uint32_t error = 0;
uint16_t soft_bit[1000];
uint8_t temp_bits[500];
memset(temp_bits, 0, sizeof(temp_bits));
memset(soft_bit, 0, sizeof(soft_bit));
float sbuf[500];
memset(sbuf, 0.0f, sizeof(sbuf));
//convert dbuf into a symbol array
for (int i = 0; i < d_len/2; i++)
{
if (dbuf[i] == 0) sbuf[i] = +1.0f;
else if (dbuf[i] == 1) sbuf[i] = +3.0f;
else if (dbuf[i] == 2) sbuf[i] = -1.0f;
else if (dbuf[i] == 3) sbuf[i] = -3.0f;
else sbuf[i] = +0.0f;
}
//slice symbols to soft dibits
slice_symbols_to_len(soft_bit, sbuf, d_len/2);
//viterbi
error = viterbi_decode_punctured(viterbi_bytes, soft_bit, ysf_puncture, d_len, 4);
//debug
// fprintf (stderr, " Ve: %1.1f; ", (float)error/(float)0xFFFF);
//debug
// for (int i = 0; i < num_bytes+1; i++)
// fprintf(stderr, "%02X", viterbi_bytes[i]);
//load viterbi_bytes into bits
unpack_byte_array_into_bit_array(viterbi_bytes, temp_bits, num_bytes+1);
//rearrange bits to get correct offset
for (int i = 0; i < num_bytes*8; i++)
viterbi_bits[i] = temp_bits[i+offset];
//zero out viterbi_bytes to reload packed appropriately
for (int i = 0; i < num_bytes; i++)
viterbi_bytes[i] = 0;
pack_bit_array_into_byte_array(viterbi_bits, viterbi_bytes, num_bytes);
return error;
}
//original sources for viterbi code and misc math related
//--------------------------------------------------------------------
// M17 C library - decode/viterbi.c
//
// This file contains:
// - the Viterbi decoder
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// M17 C library - math/math.c
//
// This file contains:
// - absolute difference value
// - Euclidean norm (L2) calculation for n-dimensional vectors (float)
// - soft-valued arrays to integer conversion (and vice-versa)
// - fixed-valued multiplication and division
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------

205
src/ysf.c
View File

@@ -228,7 +228,12 @@ void ysf_dch_decode (dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint
}
if (fn == ft) //last frame
fprintf (stderr, " %s", state->event_history_s[0].Event_History_Items[0].text_message);
{
char text_string[15]; memset (text_string, 0, sizeof(text_string));
sprintf (text_string, "%s", "BUMBLEBEETUNA");
if (strncmp(text_string, state->event_history_s[0].Event_History_Items[0].event_string, 13) != 0)
fprintf (stderr, " %s", state->event_history_s[0].Event_History_Items[0].text_message);
}
break;
@@ -377,15 +382,14 @@ static inline uint16_t crc16ysf(const uint8_t buf[], int len)
int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint8_t ft, uint8_t cm, uint8_t input[])
{
int i, j, k, err;
uint8_t s0, s1;
uint8_t trellis_buf[100];
int i, j, err;
uint8_t viterbi_bits[100];
uint8_t temp[210];
uint8_t m_data[100];
uint8_t viterbi_bytes[100];
uint8_t bits[210];
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (viterbi_bits, 0, sizeof(viterbi_bits));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
memset (bits, 0, sizeof(bits));
err = 0;
@@ -398,56 +402,27 @@ int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, u
}
}
k = 0;
//convert dibits to bits
for (i = 0; i < 100; i++)
{
bits[k++] = (buf[i] >> 1) & 1;
bits[k++] = (buf[i] >> 0) & 1;
}
uint32_t v_error =
ysf_soft_decision_viterbi(buf, 200, 13, 8, viterbi_bits, viterbi_bytes);
//setup for the convolutional decoder
for (i = 0; i < 200; i++)
temp[i] = bits[i] << 1;
//debug
if (opts->payload == 1)
fprintf (stderr, " D2-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
CNXDNConvolution_start();
for (i = 0; i < 100; i++)
{
s0 = temp[(2*i)+0];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 96);
//96/8 = 12, last 4 (96-100) are trailing zeroes
for(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;
}
uint16_t crc = crc16ysf(trellis_buf, 96);
uint16_t crc = crc16ysf(viterbi_bits, 96);
if (crc != 0) err = -2; // crc failure
for (i = 0; i < 80; i++)
trellis_buf[i] = trellis_buf[i] ^ pn95[i];
viterbi_bits[i] = viterbi_bits[i] ^ pn95[i];
//reload after de-whitening
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
for (i = 0; i < 12; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
viterbi_bytes[i] = (uint8_t)ConvertBitIntoBytes(&viterbi_bits[i*8], 8);
//decode the callsign, etc, found in the DCH when no errors
if (err == 0)
ysf_dch_decode2 (state, bn, bt, fn, ft, cm, trellis_buf);
ysf_dch_decode2 (state, bn, bt, fn, ft, cm, viterbi_bits);
else
{
fprintf (stderr, "%s", KRED);
@@ -460,25 +435,24 @@ int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, u
fprintf (stderr, "\n ");
fprintf (stderr, "DCH2: ");
for (i = 0; i < 12; i++)
fprintf (stderr, "[%02X]", m_data[i]);
fprintf (stderr, "[%02X]", viterbi_bytes[i]);
}
return err;
}
//modified version of nxdn_deperm_facch1 -- this one for Full Rate, Type 1 CC, Headers and Terminators DCH (180 dibit version)
//Soft Decision Full Rate, Type 1 CC, Headers and Terminators DCH (180 dibit version)
int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint8_t ft, uint8_t cm, uint8_t input[])
{
int i, j, k, err;
uint8_t s0, s1;
uint8_t trellis_buf[190];
int i, j, err;
uint8_t viterbi_bits[190];
uint8_t temp[370];
uint8_t m_data[100];
uint8_t viterbi_bytes[100];
uint8_t bits[370];
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (viterbi_bits, 0, sizeof(viterbi_bits));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
memset (bits, 0, sizeof(bits));
err = 0;
@@ -491,56 +465,27 @@ int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, ui
}
}
k = 0;
//convert dibits to bits
for (i = 0; i < 180; i++)
{
bits[k++] = (buf[i] >> 1) & 1;
bits[k++] = (buf[i] >> 0) & 1;
}
uint32_t v_error =
ysf_soft_decision_viterbi(buf, 360, 23, 8, viterbi_bits, viterbi_bytes);
//setup for the convolutional decoder
for (i = 0; i < 360; i++)
temp[i] = bits[i] << 1;
//debug
if (opts->payload == 1)
fprintf (stderr, " D1-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
CNXDNConvolution_start();
for (i = 0; i < 180; i++)
{
s0 = temp[(2*i)+0];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 176);
//176/8 = 22, last 4 (176-180) are trailing zeroes
for(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;
}
uint16_t crc = crc16ysf(trellis_buf, 176);
uint16_t crc = crc16ysf(viterbi_bits, 176);
if (crc != 0) err = -2; // crc failure
for (i = 0; i < 160; i++)
trellis_buf[i] = trellis_buf[i] ^ pn95[i];
viterbi_bits[i] = viterbi_bits[i] ^ pn95[i];
//reload after de-whitening
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
for (i = 0; i < 22; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
viterbi_bytes[i] = (uint8_t)ConvertBitIntoBytes(&viterbi_bits[i*8], 8);
//decode the callsign, etc, found in the DCH when no errors
if (err == 0)
ysf_dch_decode (state, bn, bt, fn, ft, cm, trellis_buf);
ysf_dch_decode (state, bn, bt, fn, ft, cm, viterbi_bits);
else
{
fprintf (stderr, "%s", KRED);
@@ -553,25 +498,24 @@ int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, ui
fprintf (stderr, "\n ");
fprintf (stderr, "DCH1: ");
for (i = 0; i < 22; i++)
fprintf (stderr, "[%02X]", m_data[i]);
fprintf (stderr, "[%02X]", viterbi_bytes[i]);
}
return err;
}
//modified version of nxdn_deperm_facch1
int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
//FICH Soft Decision Viterbi
int ysf_conv_fich (dsd_opts * opts, uint8_t input[], uint8_t dest[32])
{
int i, j, k, err;
uint8_t s0, s1;
uint8_t trellis_buf[100];
int i, j, err;
uint8_t viterbi_bits[100];
uint8_t temp[210];
uint8_t m_data[100];
uint8_t viterbi_bytes[100];
uint8_t bits[210];
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (viterbi_bits, 0, sizeof(viterbi_bits));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
memset (bits, 0, sizeof(bits));
err = 0;
@@ -584,41 +528,12 @@ int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
}
}
k = 0;
//convert dibits to bits
for (i = 0; i < 100; i++)
{
bits[k++] = (buf[i] >> 1) & 1;
bits[k++] = (buf[i] >> 0) & 1;
}
uint32_t v_error =
ysf_soft_decision_viterbi(buf, 200, 13, 8, viterbi_bits, viterbi_bytes);
//setup for the convolutional decoder
for (i = 0; i < 200; i++) //192
temp[i] = bits[i] << 1;
CNXDNConvolution_start();
for (i = 0; i < 100; i++)
{
s0 = temp[(2*i)+0];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 96);
//96/8 = 12, last 4 (96-100) are trailing zeroes
for(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;
}
//debug
if (opts->payload == 1)
fprintf (stderr, " F-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
uint8_t fich_bits[12*4];
uint8_t temp_b[24];
@@ -631,31 +546,27 @@ int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
g[i] = FALSE;
for (j = 0; j < 24; j++)
temp_b[j] = (char) trellis_buf[(i*24)+j];
temp_b[j] = (char) viterbi_bits[(i*24)+j];
g[i] = Golay_24_12_decode(temp_b);
if(g[i] == FALSE) err = -1;
for (j = 0; j < 24; j++)
trellis_buf[(i*24)+j] = (uint8_t) temp_b[j];
viterbi_bits[(i*24)+j] = (uint8_t) temp_b[j];
}
//load corrected bits
for (i = 0; i < 12; i++)
{
fich_bits[(12*0)+i] = trellis_buf[i+0];
fich_bits[(12*1)+i] = trellis_buf[i+24];
fich_bits[(12*2)+i] = trellis_buf[i+48];
fich_bits[(12*3)+i] = trellis_buf[i+72];
fich_bits[(12*0)+i] = viterbi_bits[i+0];
fich_bits[(12*1)+i] = viterbi_bits[i+24];
fich_bits[(12*2)+i] = viterbi_bits[i+48];
fich_bits[(12*3)+i] = viterbi_bits[i+72];
}
uint16_t crc = crc16ysf(fich_bits, 48);
if (crc != 0) err = -2; // crc failure
memset (m_data, 0, sizeof (m_data));
for (i = 0; i < 12; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
memcpy(dest, fich_bits, 32); //copy minus the crc16
return err;
}
@@ -808,7 +719,7 @@ void processYSF(dsd_opts * opts, dsd_state * state)
fichrawdibits[i] = getDibit(opts, state);
//from nxdn_deperm_facch1 w/ nxdn convolutional decoder
err = ysf_conv_fich (fichrawdibits, fich_decode);
err = ysf_conv_fich (opts, fichrawdibits, fich_decode);
//if errors decoding fich, then just treat it like the last frame that came in
if (err == 0)