82 Commits

Author SHA1 Message Date
lwvmobile
34753963b8 DMR: Add Keyloading for Hytera PDU Decryption based on Data Header Key ID first, then manually set key. #372 2026-11-07 20:22:23 -05:00
lwvmobile
544d576551 DMR: Fix Hytera OTA Encryption VLC Pseudo Call Information TGT Value on Slot 2. Verbage Tweaks; #373
DMR: Fix Kirisun "Ultra" Cipher Name in Code (Ultra, not Ultimate). Remove Unvalidated Code Bits regarding Kirisun; #319
2026-11-07 19:35:47 -05:00
lwvmobile
72f8c1aff7 Merge branch 'kirisun3' into aw_dev 2026-11-06 22:14:02 -05:00
lwvmobile
bfa4891965 DMR: Hytera Over the Air Encryption (Signalling Encryption) Support; #373
DMR: Fix Hytera PDU Decryption to operate at Block Level; (Fixes confirmed data); #372

DMR: Hytera Enhanced RC4 allows variable len output for OTA, PDU or Voice;

DMR: Fix Segfault in RS12,9 encoder function where freeing static memory caused a crash;

Misc Code tweaks to accomodate new features;
2026-11-06 21:22:38 -05:00
lwvmobile
daa47069bf DMR: Add More Kirisun Encryption Support; #319
Added Ultimate Cipher;
2026-10-30 10:42:34 -04:00
lwvmobile
587181f4de DMR: Add Hytera PDU Encryption Support; #372
DMR: UDP Comp Data Calls use Data header SRC and TGT values for Event History; Fixes Text Messages not populating Event History; Simplify Event String;
2026-10-21 11:48:59 -04:00
lwvmobile
eb8048f1d9 Merge branch 'cygwin_bat_fix' into aw_dev 2026-10-20 19:31:41 -04:00
lwvmobile
d8f9bd190b Merge pull request #371 from ryandrake08/rtltcp-input
Fix divide-by-zero and buffer overruns in getFrameSync()
2026-10-19 16:25:31 -04:00
Ryan Drake
6880087afe Fix buffer underruns in getFrameSync() 2026-10-19 12:52:19 -07:00
Ryan Drake
6106a608e3 Fix divide-by-zero when result_len is zero 2026-10-19 12:52:19 -07:00
lwvmobile
1aae6d34c9 CYGWIN: Fix default bat file date and time string to work independent of locale settings (uses robocopy); #370 2026-10-19 10:45:03 -04:00
lwvmobile
be57b5149b RTL_TCP: Add Ncurses Input Print; #368
Add Usage to docs;
2026-10-18 12:07:41 -04:00
lwvmobile
3e221f29f5 Merge pull request #369 from ryandrake08/rtltcp-input
rtl_tcp input support
2026-10-17 16:42:02 -04:00
Ryan Drake
4754f6a59b Include sys/socket.h for the UDP tuning socket 2026-10-17 11:08:40 -07:00
Ryan Drake
7027b145b8 Add interruptible timeouts for stalled rtl_tcp servers 2026-10-17 10:19:43 -07:00
Ryan Drake
f38b5976f3 Add rtl_tcp input: -i rtltcp:host:port:freq:gain:ppm:bw:sq:vol 2026-10-17 09:55:27 -07:00
lwvmobile
5244c85774 DMR: Fix Kirisun Keystream Creation Per Slot; #319
Use Full Name of Each Kirisun Cipher in Code;
2026-10-17 09:21:46 -04:00
lwvmobile
4005c9b313 DMR: Add More Kirisun Encryption Support; #319
Ciphers added include "Enhanced" "Reinforced" and "Strengthened"; Placeholders for remaining ciphers;
2026-10-16 11:04:42 -04:00
lwvmobile
72b3e5efe4 Fix Typo 2026-10-13 20:10:08 -04:00
lwvmobile
a3a6fb999a DMR: Tait Talker Alias Single Block Embedded LC; #367 2026-10-13 19:58:53 -04:00
lwvmobile
37855b817b DMR: Fix MassReg reg_address value and tweaks; #366 2026-10-09 19:42:45 -04:00
lwvmobile
4fe32db7f5 Merge pull request #364 from eremeevdev/docs/macos-install-notes
Add macOS Homebrew Build Notes to Install Notes;
2026-09-26 16:47:03 -04:00
Aleksey Eremeev
6819f9010e Add macOS Homebrew Build Notes to Install Notes; 2026-09-26 22:25:45 +03:00
lwvmobile
719d9709e6 Merge pull request #363 from xdab/perf/use-symbol-top2
use_symbol: replace per-symbol full sort with single-pass top-2 selection
2026-09-22 17:58:43 -04:00
xdab
26e4a619f6 use_symbol: replace per-symbol full sort with single-pass top-2 selection 2026-09-22 22:41:21 +02:00
lwvmobile
0838a7f7ba DMR: Fix Copy and Paste Bug with Caltta BP Unmute and Doc Typos; #360 2026-09-21 22:18:24 -04:00
lwvmobile
a6ea60b345 Merge pull request #362 from xdab/perf/sync-hunt-topn
Frame Sync: Replace per-symbol full sort with single-pass top-5 selection
2026-09-21 18:30:55 -04:00
xdab
9232d66fcb Frame Sync: Replace per-symbol full sort with single-pass top-5 selection
The frame sync hunt loop sorted the entire symbol amplitude window
(lbuf2, t_max entries) with qsort on every iteration only to read
three ranked values off each end. Replace the copy + qsort with a
single O(n) pass maintaining the five smallest and five largest
values in two fixed 5-slot arrays, reading the exact same ranks.

Rank equivalence: lmn[] ascending = sorted[0..], lmx[] descending =
sorted[..t_max-1], so both the short (t_max < 13) and long rank
formulas map 1:1. The now-unused lbuf2 scratch buffer is removed.

Decode output verified byte-identical (timestamps stripped) against
DMR, dPMR, D-Star and YSF sample captures, including runs with a
10 second no-carrier lead-in.
2026-09-21 20:48:33 +02:00
lwvmobile
15515158b5 DMR: Add Support for Callta BP 10/32/64; #360
DMR: Add optional discard value to -S straight_xor keystream application;
2026-09-20 04:31:06 -04:00
lwvmobile
fa4a332582 DMR: Add Special Use Cases for RAS Passwords that use a # or a $ in the help and complete usage doc; #359 2026-09-10 16:32:24 -04:00
lwvmobile
4139b19bfe DMR: RAS MD5 Hash Fringe Case Correction; #359 2026-09-07 15:19:30 -04:00
lwvmobile
cd5f0f4a8e DMR: Remove Condition where VLC and TLC without FID 0x10 did not calculate RAS MAC attachments; Misc Tweaks to dburst RAS Indicator; #359
DMR: Tweak FLCO to remove 0x23 as a Cap+ Private Call when it is only a RAS indicator on a normal Private Call; Clarify Comments;

DMR: Tweak RAS MD5 loading to allow for a correction against a flat value (if needed in future based on observation);

DMR: Misc Tweaks to Vertex Standard Key Loading; #316
2026-09-06 17:57:42 -04:00
lwvmobile
fb3dc75c47 RAS MD5 Tweaks; #359
Disable Fist Byte Correction on MD5 password hash following observations based on real world system;

Improve BPTC127x77 Decoder Logic;

Tweak Debug code to show checksum diff if there is no actual diff;
2026-09-04 21:36:45 -04:00
lwvmobile
f2254f413a Vertex Standard 10 Char and 64 Char Encryption Support; #316
Special mode of 0:0 to apply the inversion to the interlevaed frame if present without application of a keystream for analysis;

Misc Tweaks to VTX STD ENC Header and VTX TMS message;

TG Value loading of VTX 10 char keys available only by #define VERTEX_KEY40_BY_TG which will override the Moto BP loadout;
2026-09-04 15:47:56 -04:00
lwvmobile
c97400a704 RTL: Retry Tuning if Error; #358
This is just a simple spam of the rtlsdr_set_center_freq function if that function returns a negative value indicating failure. Similar retries are present for gain and ppm set as well.

This is primarily to address issues that have been noted to arise on USB 2.0 ports on some AM4 motherboard / chipset configurations, prevalently, Ryzen CPUs with the 3XX, 4XX, and 5XX motherboards. This may also address other misc issues due to bad USB cables (within reason) as long as a retry will suffice to allow proper tuning;

Upstream rtl-sdr repo was updated to 2.0.3 previously as well to allow for the V4L version dongles to work correctly when compiled in Cygwin. Linux users will need to update their own rtl-sdr versions;
2026-09-01 14:38:11 -04:00
lwvmobile
cc6711fe4d DMR: RAS Implementation; #369
RAS Implementation includes converting RAS 6-24 character passwords into 56-bit RAS Effective Key Values and Performing RAS Permutations on RAS Enabled Burst Types to Calculate the Correct MAC attachment to validate messages;

Better Handling for RAS indicators found in BPTC 196,96 Reserved Bits and handling for RAS indicator bits found in embedded link control opcode byte;

Users may either enter the RAS password with the -$ option, or the 56-bit effective key value with the -y option.

Users will no longer need to use the -F option to bypass CRC errors on RAS enabled payloads when FEC does not explicitely fail.

Removed the old float audio -y option.
2026-09-01 13:29:55 -04:00
lwvmobile
198f0eacb5 DMR: Auctus / GoComm Encryption Support w/ Forced Application; #346
Use -, Secure04 to use this mode;
2026-08-14 03:05:21 -04:00
lwvmobile
5e5c3c4555 MBE: Tweaks to DMRA Late Entry IV (change run order to align w/ superframe) for SDRTrunk .mbe files; #298
MBE: Fixes for non forced DMRA supported decryption modes (with supplied IV + expansion in mbe file, etc);

MBE: Support for DMRA DES Mode (rare and untested for .mbe files);
2026-07-20 23:02:17 -04:00
lwvmobile
272f17bfa3 MBE: Add DMRA Late Entry IV and Forced AES Alg Decryption support (w/ known key) for SDRTrunk .mbe files; #298 2026-07-20 11:31:31 -04:00
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
75 changed files with 14166 additions and 4361 deletions

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)
@@ -79,6 +134,7 @@ FILE(GLOB HEADERS include/*.h include/*.hpp)
if(NOT RTLSDR_FOUND)
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp)
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_tcp.c)
endif(NOT RTLSDR_FOUND)
configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY)

View File

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

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

@@ -61,10 +61,6 @@ if [ "$RELEASE" = "y" ]; then
#start copying things to the lib folder
cp -r /lib/pulseaudio/ dsd-fme-portable/lib/
#support for LAPACK, BLAS, and FFTW3 (SIMD math libraries and dependencies)
cp /lib/lapack/* dsd-fme-portable/dsd-fme/
cp /bin/cyggfortran* dsd-fme-portable/dsd-fme/
#start copying things into the dsd-fme folder
cp dsd-fme/build/dsd-fme.exe dsd-fme-portable/dsd-fme/
cp -r dsd-fme/examples dsd-fme-portable/dsd-fme/
@@ -98,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/

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,22 +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/
#newer Cygwin has Cmake 4.1.3 (or newer) so we need to replace the line with cmake_minimum_required to a newer version
#this doesn't seem to break anything, its just that cmake will refuse to run properly without a newer min version in it
#I'm just going to use the version that is used for most all my other cmake projects, seems to still be okay with new cmake
sed -i '/^cmake_minimum_required *( *VERSION *2\.8\.6 *)$/c\cmake_minimum_required(VERSION 3.10.2)' CMakeLists.txt
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")
@@ -127,6 +108,46 @@ LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH
```
### macOS Builds (Homebrew)
Building on macOS (Apple Silicon) using [Homebrew](https://brew.sh "Homebrew") for dependencies has been tested working, but keep in mind this project is primarily focused on Linux, so macOS support should be considered experimental. Unlike the Linux steps above, macOS has no `ldconfig`, so simply skip that step wherever it appears below.
Install dependencies first:
```
brew install cmake git libsndfile pulseaudio rtl-sdr codec2 ncurses pkg-config
```
Note: Homebrew's ncurses is keg-only (not symlinked into /opt/homebrew), so we will pass an extra CMake flag when configuring DSD-FME below so it can be found.
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")
```
git clone https://github.com/lwvmobile/mbelib
cd mbelib
git checkout ambe_tones
mkdir build
cd build
cmake ..
make -j `sysctl -n hw.ncpu`
sudo make install
cd ../..
```
Finish by cloning and building DSD-FME (note the added -DCMAKE_PREFIX_PATH flag for keg-only ncurses):
```
git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme
mkdir build
cd build
cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix ncurses)
make -j `sysctl -n hw.ncpu`
sudo make install
```
Pulse Audio is still the default input/output in DSD-FME, so start the Homebrew Pulse Audio daemon before running dsd-fme (it will also restart at login): `brew services start pulseaudio`. To confirm your RTL-SDR dongle is detected, run `rtl_test` (ships with Homebrew's rtl-sdr package); unlike Linux, there is no driver blacklist to deal with on macOS.
### Windows Cygwin Builds
Cygwin builds now have an experimental semi-automatic installer, to run, follow steps below:
@@ -135,7 +156,7 @@ Open Windows PowerShell (not Command Prompt) and copy and paste all of this in a
```
Invoke-WebRequest https://cygwin.com/setup-x86_64.exe -OutFile setup-x86_64.exe
.\setup-x86_64.exe --packages nano,libpulse-devel,libpulse-mainloop-glib0,libpulse-simple0,libpulse0,pulseaudio,pulseaudio-debuginfo,pulseaudio-equalizer,pulseaudio-module-x11,pulseaudio-module-zeroconf,pulseaudio-utils,sox-fmt-pulseaudio,libusb0,libusb1.0,libusb1.0-debuginfo,libusb1.0-devel,libncurses++w10,libncurses-devel,libncursesw10,ncurses,cmake,gcc-core,gcc-debuginfo,gcc-objc,git,make,socat,sox,sox-fmt-ao,zip,unzip,wget,gcc-g++,libsndfile-devel,libopenblas,liblapack-devel,libfftw3-devel
.\setup-x86_64.exe --packages nano,libpulse-devel,libpulse-mainloop-glib0,libpulse-simple0,libpulse0,pulseaudio,pulseaudio-debuginfo,pulseaudio-equalizer,pulseaudio-module-x11,pulseaudio-module-zeroconf,pulseaudio-utils,sox-fmt-pulseaudio,libusb0,libusb1.0,libusb1.0-debuginfo,libusb1.0-devel,libncurses++w10,libncurses-devel,libncursesw10,ncurses,cmake,gcc-core,gcc-debuginfo,gcc-objc,git,make,socat,sox,sox-fmt-ao,zip,unzip,wget,gcc-g++,libsndfile-devel
```

View File

@@ -1,4 +1,4 @@
Build Version: AW 2025-209-g14edf32
Build Version: AW 2026-47-ga6ea60b
MBElib Version: 1.3.4
CODEC2 Support Enabled
@@ -21,6 +21,7 @@ Input/Output options:
pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O)
rtl for rtl dongle (Default Values -- see below)
rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)
rtltcp:host:port:freq:gain:ppm:bw:sq:vol for rtl dongle on an rtl_tcp server (see below)
tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)
tcp:192.168.7.5:7355 for custom address and port
filename.bin for OP25/FME capture bin files
@@ -50,6 +51,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.
@@ -60,7 +63,6 @@ Input/Output options:
-q Reverse Mute - Mute Unencrypted Voice and Unmute Encrypted Voice
-V <num> Enable TDMA Voice Synthesis on Slot 1 (1), Slot 2 (2), or Both (3); Default is 3;
-z Set TDMA Voice Slot Preference when using /dev/dsp audio output (prevent lag and stuttering)
-y Enable Experimental Pulse Audio Float Audio Output
-v <hex> Set Filtering Bitmap Options (Advanced Option)
1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on
@@ -76,6 +78,11 @@ RTL-SDR options:
vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)
Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2
Usage: rtltcp:host:port:freq:gain:ppm:bw:sq:vol
host <name> rtl_tcp Server Hostname or IP Address (default = localhost)
port <num> rtl_tcp Server Port (default = 1234)
freq, gain, ppm, bw, sq, vol as above
Example: dsd-fme -fs -i rtltcp:192.168.1.10:1234:450M:26:-2:8 -C connect_plus_chan.csv -T
Decoder options:
-fa Auto Detection
@@ -99,10 +106,10 @@ Decoder options:
-fE Decode only EDACS EA/ProVoice with ESK 0xA0*
-fm Decode only dPMR*
-l Disable DMR, dPMR, NXDN, M17 input filtering
-u <num> Unvoiced speech quality (default=3)
-xx Expect non-inverted X2-TDMA signal
-xr Expect inverted DMR signal
-xd Expect inverted ICOM dPMR signal
-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.
@@ -135,11 +142,20 @@ Advanced Decoder options:
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)
-H <hex> Manually Enter AES-128, AES-256, or Kirisun 128 or 256 Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-H <hex> Manually Enter Caltta 10/32/64 Char Basic Privacy Hex Key (see example below)
-H C:40:1234567890
-H C:128:736B9A9C5645288B243AD5CB8701EF8A'
-H C:256:20029736A5D91042C923EB0697484433005EFC58A1905195E28E9C7836AA2DB8'
-H <hex> Manually Enter Hytera OTA Encryption 32 Char Hex Key (see example below)
-H O:0:0 no keystream application, but check for Hytera OTA Encryption and DMRA EP Late Entry IV.
-H O:128:17bfe59b29a06136fb7d3811fd1a4904 for 128-bit RC4 OTA Key Setup
-R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)
-1 <hex> Manually Enter RC4 or DES Key Value (DMR, P25, NXDN) (Hex Value)
@@ -172,6 +188,23 @@ Advanced Decoder options:
-A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value)
-$ <string> Enter RAS Password 6-24 Char Key Value (DMR) (a-z A-Z, 0-9, $ # _ -)
-$ Password123
Special Use Cases involving # and $, use parenthesis and put \ in front of all $ signs
-$ "######"
-$ "\$\$\$\$\$\$"
-y <hex> Enter RAS 56-bit Effective Key Value (DMR) (14 Char Hex Value)
-y 6DF241BC4AE2EC
-, <string> Manually Enter and Enforce Auctus A6 (GoComm) 8 Char Key Value (DMR) (String Value)
-, Secure04
-u <string> Manually Enter Vertex Standard 10 or 64 Char Hex Key
-u 10:1234567891 or 64:AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD
-u 0:0 no KS application, but apply the inversion to the interleave (clear errors on 256)
-S <str> Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits)
For Example, enter 16-bit Keystream 0909 as:
-S 16:0909
@@ -181,6 +214,8 @@ Advanced Decoder options:
-S 49:ED0AED4AED4AED4A
For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:
-S 49:11AA22BB33CC44
For Example, Caltta 40-bit Keystream w/ optional 7-bit discard as:
-S 40:1234567890:7
-k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.
Only supports NXDN, DMR Basic Privacy (decimal value).
@@ -211,7 +246,7 @@ Advanced Decoder options:
-p Disable Tune to Private Calls (DMR TIII, P25, NXDN Type-C and Type-D)
-E Disable Tune to Group Calls (DMR TIII, Con+, Cap+, P25, NXDN Type-C, and Type-D)
-e Enable Tune to Data Calls (DMR TIII, Cap+, NXDN Type-C)
(NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls
(NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls)
-I <dec> Specify TG to Hold During Trunking (DMR, P25, NXDN Type-C Trunking)
-U <port> Enable RIGCTL/TCP; Set TCP Port for RIGCTL. (4532 on SDR++)
-B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF)
@@ -220,5 +255,4 @@ Advanced Decoder options:
-t <secs> Set Trunking or Scan Speed VC/sync loss hangtime in seconds. (default = 1 second)
Trunking Example TCP: dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.txt
Trunking Example RTL: dsd-fme -fs -i rtl:0:450M:26:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.txt
Trunking Example RTL: dsd-fme -fs -i rtl:0:450M:26:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.txt

View File

@@ -22,27 +22,20 @@
@REM set options to pass to dsd-fme
set "options= -N -Z "
@REM Set Date Time for log (sourced from: https://stackoverflow.com/questions/1192476/format-date-and-time-in-a-windows-batch-script)
@REM Create Folder and Set Date Time for logs
@echo off
REM "%date: =0%" replaces spaces with zeros
set d=%date: =0%
REM "set yyyy=%d:~-4%" pulls the last 4 characters
set yyyy=%d:~-4%
set mm=%d:~4,2%
set dd=%d:~7,2%
set dow=%d:~0,3%
set d=%yyyy%%mm%%dd%
set t=%TIME: =0%
REM "%t::=%" removes semi-colons
REM Instead of above, you could use "%t::=-%" to
REM replace semi-colons with hyphens (or any
REM non-special character)
set t=%t::=%
set t=%t:.=%
set datetimestr=%d%_%t%
@REM @echo Long date time str = %datetimestr%
mkdir logs
@REM Locale-independent date/time for log filenames (yyyyMMdd_HHmmss)
@REM Works on older Windows without wmic or PowerShell
for /f "tokens=1-6 delims=/: " %%a in ('robocopy "|" . /njh ^| find ":"') do (
set "YYYY=%%a"
set "MM=%%b"
set "DD=%%c"
set "HH=%%d"
set "Min=%%e"
set "Sec=%%f"
)
set "datetimestr=%YYYY%%MM%%DD%_%HH%%Min%%Sec%"
@REM random number is used so that two log files don't share the same name
set rnd=%RANDOM%

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,6 +355,7 @@ typedef struct
int uvquality;
int inverted_x2tdma;
int inverted_dmr;
int inverted_nxdn;
int mod_threshold;
int ssize;
int msize;
@@ -362,12 +365,16 @@ typedef struct
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_rms;
int rtltcp_enabled;
char rtltcp_hostname[1024];
int rtltcp_portno;
int monitor_input_audio;
int analog_only;
int pulse_raw_rate_in;
@@ -645,6 +652,14 @@ typedef struct
//xl specific, we need to know if the ESS is from HDU, or from LDU2
int xl_is_hdu;
//Vertex Standard Keys
// #define VERTEX_KEY40_BY_TG
// #define VERTEX_KEY256_BY_TG
char vtx256_key[64];
unsigned long long int vtx40_key;
unsigned long long int vtx256_ekey;
uint8_t vtx_key_loaded;
unsigned int debug_audio_errors;
unsigned int debug_audio_errorsR;
unsigned int debug_header_errors;
@@ -868,7 +883,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
@@ -914,6 +929,26 @@ typedef struct
//Remus DMR End Call Alert Beep
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
//RAS Key Variables
// #define DEBUG_RAS
unsigned long long int ras_effective_key;
int ras_permutation_order[104];
uint8_t dmr_is_ras; //indicate a partial or complete RAS enabled system
//Hytera OTA Key Variables
// #define DEBUG_HOTA
uint8_t hota_enc_check;
uint8_t hota_key_loaded;
uint8_t dmr_is_hota;
int hota_bit_shift;
int hota_byte_shifts[12];
uint8_t hota_rc4_key[16];
uint8_t hota_rc4_ks[80];
uint8_t hota_lc_ctx[2][4];
uint8_t hota_ctx_ok[2];
int hota_sbrc_idx1[2];
int hota_sbrc_idx2[2];
//Bitmap Filtering Options
int audio_smoothing;
@@ -1000,6 +1035,7 @@ typedef struct
//generic ks
int straight_ks;
int straight_mod;
int straight_discard;
uint8_t static_ks_bits[2][882];
int static_ks_counter[2];
@@ -1182,7 +1218,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);
@@ -1196,7 +1232,6 @@ void printFrameInfo (dsd_opts * opts, dsd_state * state);
void processFrame (dsd_opts * opts, dsd_state * state);
void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation);
int getFrameSync (dsd_opts * opts, dsd_state * state);
int comp (const void *a, const void *b);
void noCarrier (dsd_opts * opts, dsd_state * state);
void initOpts (dsd_opts * opts);
void initState (dsd_state * state);
@@ -1261,25 +1296,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_pn95_dibit_scrambler(dsd_state * state, 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], uint8_t lich);
//MT and Voice
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);
@@ -1288,13 +1323,10 @@ 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
@@ -1310,6 +1342,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,
@@ -1318,19 +1353,28 @@ 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);
@@ -1345,11 +1389,14 @@ int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
//BPTC (Block Product Turbo Code) functions
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3], uint8_t * is_ras);
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32], uint32_t ParityCheckTypeOdd);
uint32_t BPTC_128x77_Check_Errors(uint8_t InputDataMatrix[8][16]);
//Reed Solomon (12,9) functions
//Reed Solomon DMR functions
void rs_16_13_encode(uint8_t * input);
void rs_12_9_encode(uint8_t * input);
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
@@ -1362,6 +1409,7 @@ uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes,
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData);
uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData);
uint32_t ComputeCrc32BitV(uint8_t * DMRData, uint32_t NbData);
//new simplified dmr functions
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
@@ -1371,7 +1419,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
@@ -1393,10 +1441,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);
@@ -1482,6 +1531,11 @@ void LFSRP(dsd_state * state);
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
void LFSR64(dsd_state * state);
//IV Reversals using LFSR tap +1 arrangements
uint64_t reverse_lfsr_32a_to_len(uint8_t * iv, int16_t len);
uint64_t reverse_lfsr_32d_to_len(uint8_t * iv, int16_t len);
uint64_t reverse_lfsr_64_to_len(dsd_opts * opts, uint8_t * iv, int16_t len);
void Hamming_7_4_init();
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Hamming_7_4_decode(unsigned char *rxBits);
@@ -1605,6 +1659,9 @@ 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);
//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[]);
void rc4_block_output (int drop, int keylen, int meslen, uint8_t * key, uint8_t * output_blocks);
@@ -1621,7 +1678,7 @@ 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 / Retevis / Anytone / Kenwood / Misc DMR Encryption Modes
//Tytera / Retevis / Anytone / Kenwood / Vertex / Baofeng / 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);
@@ -1631,21 +1688,48 @@ 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);
void caltta_bp_keystream_creation(dsd_state * state, char * input);
void auctus_keystream_creation(dsd_state * state, char * input);
void vertex_keystream_setup(dsd_state * state, char * input);
void vertex_40_keystream_creation(dsd_state * state, unsigned long long int key_value);
void vertex_256_keystream_creation(dsd_state * state, char * key_string);
void vtx256_ambe2_interleave_inversion(char ambe_fr[4][24]);
//Kirisun
//Kirisun LFSR and General Keystream Creator
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);
void kirisun_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);
void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value, int len);
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len);
void hytera_enhanced_alg_refresh(dsd_state * state);
//RAS
uint32_t ras_mac_calculator(dsd_state * state, uint8_t * input, int input_len, int type);
void ras_effective_key_creation(dsd_state * state, char * input);
void ras_permute_order(uint32_t seed, int * order);
//Hytera OTA Encryption
void hytera_ota_keystream_setup(dsd_state * state, char * input);
void hytera_ota_decrypt_bptc_96(dsd_state * state, uint8_t * input, uint8_t type);
void hytera_ota_decrypt_emb_lc(dsd_state * state, uint8_t input[8][16]);
void hytera_ota_search_emb_lc(dsd_state * state, uint8_t input[8][16]);
void hytera_ota_decrypt_sb_rc(dsd_state * state, uint8_t * input);
void hytera_ota_search_sb_rc(dsd_state * state, uint8_t * input, int ptype);
void hytera_create_lc_ctx(dsd_state * state, uint8_t * input);
void hytera_ota_unit_testing(dsd_state * state);
//Hytera Decryption for Data Blocks
uint8_t hytera_bp_data_decrypt(dsd_state * state, uint8_t * input, int len);
uint8_t hytera_ep_data_decrypt(dsd_opts * opts, dsd_state * state, uint8_t * input, int len);
//LFSR to expand either a DMR 32-bit or P25/NXDN 64-bit MI into a 128-bit IV for AES
void lfsr_32_to_128(uint8_t * iv);
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
@@ -1660,6 +1744,21 @@ void rtlsdr_sighandler();
void rtl_dev_tune(dsd_opts * opts, long int frequency);
long int rtl_return_rms();
void rtl_clean_queue();
//rtl_tcp client (rtl_sdr_tcp.c)
int rtltcp_open(const char *hostname, int portno);
int rtlsdr_or_tcp_close(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
int rtlsdr_or_tcp_cancel_async(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_reset_buffer(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_get_tuner_gains(rtlsdr_dev_t *dev, int *gains);
int rtlsdr_or_tcp_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq);
int rtlsdr_or_tcp_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate);
int rtlsdr_or_tcp_set_direct_sampling(rtlsdr_dev_t *dev, int on);
int rtlsdr_or_tcp_set_offset_tuning(rtlsdr_dev_t *dev, int on);
int rtlsdr_or_tcp_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual);
int rtlsdr_or_tcp_set_tuner_gain(rtlsdr_dev_t *dev, int gain);
int rtlsdr_or_tcp_set_freq_correction(rtlsdr_dev_t *dev, int ppm);
#endif
@@ -1669,6 +1768,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

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;
};

View File

@@ -167,223 +167,295 @@ void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output)
/*
* @brief : This function extract the 96 bits of a deinteleaved 196 bits
* buffer using BPTC (196,96)
*
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (196 bytes)
*
* @param DMRDataExtracted : Pointer where the DMR data will be written (96 bytes)
*
* @return The total number of irrecoverable Hamming check errors
* BPTC (196,96) extract — ETSI TS 102 361-1 B.1.1
* Two-pass row Hamming(15,11) + column Hamming(13,9).
* Debug: define BPTC_196x96_DEBUG 1 for row/col correction chatter.
*/
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3])
// #define BPTC_196x96_DEBUG 1
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196],
uint8_t DMRDataExtracted[96],
uint8_t R[3],
uint8_t * is_ras)
{
uint32_t i, j, k;
uint8_t DataMatrix[13][15];
uint8_t LineUncorrected[15];
uint8_t LineBefore[15];
uint8_t LineCorrected[11];
uint8_t ColumnUncorrected[13];
uint8_t ColumnBefore[13];
uint8_t ColumnCorrected[9];
uint32_t HammingIrrecoverableErrorNb = 0;
uint32_t row_corr_p1 = 0, col_corr_p1 = 0;
uint32_t row_fail_p1 = 0, col_fail_p1 = 0;
uint32_t row_corr_p2 = 0, col_corr_p2 = 0;
uint32_t row_fail_p2 = 0, col_fail_p2 = 0;
/* First step : Reconstitute the BPTC 15x11 matrix
* Note : Input data shall be deinterleaved */
k = 1; /* Discard R(3) bit - See DMR standard chapter B1.1 BPTC (196,96) */
for(i = 0; i < 13; i++)
UNUSED(row_corr_p1); UNUSED(col_corr_p1);
UNUSED(row_fail_p1); UNUSED(col_fail_p1);
UNUSED(row_corr_p2); UNUSED(col_corr_p2);
UNUSED(row_fail_p2); UNUSED(col_fail_p2);
#if BPTC_196x96_DEBUG
fprintf (stderr, "\n");
#endif
/* Rebuild 13×15 matrix; discard R(3) at index 0 — DMR B.1.1 */
k = 1;
for (i = 0; i < 13; i++)
{
for(j = 0; j < 15; j++)
for (j = 0; j < 15; j++)
{
/* Only the LSBit of the byte is stored */
DataMatrix[i][j] = (InputDeInteleavedData[k] & 1);
k++;
}
}
/* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */
// DataMatrix[0][0] = 0; /* R(2) */
// DataMatrix[0][1] = 0; /* R(1) */
// DataMatrix[0][2] = 0; /* R(0) */
/* Init the Hamming (15,11,3) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_15_11_init();
/* Init the Hamming (13,9,3) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_13_9_init();
/* Init the Hamming error counter */
HammingIrrecoverableErrorNb = 0;
/* Process the the Hamming (15,11,3) code
* check on each line (first time).
* Do not check the 4th last lines */
for(i = 0; i < 9; i++)
/* ========== Pass 1: row Hamming(15,11) on rows 0..8 ========== */
for (i = 0; i < 9; i++)
{
/* Get a full line */
for(j = 0; j < 15; j++)
for (j = 0; j < 15; j++)
{
LineUncorrected[j] = DataMatrix[i][j];
LineBefore[j] = LineUncorrected[j];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
if (Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
}
/* Re-inject the line in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 11; j++)
{
DataMatrix[i][j] = LineCorrected[j];
}
}
/* Process the the Hamming (15,11,3) code
* check on each column/row (first time) */
for(i = 0; i < 15; i++)
{
/* Get a full column */
for(j = 0; j < 13; j++)
{
ColumnUncorrected[j] = DataMatrix[j][i];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
}
/* Re-inject the column in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 9; j++)
{
DataMatrix[j][i] = ColumnCorrected[j];
}
}
/* The first Hamming code check has maybe corrected bit
* witch can be use to correct other bit, so make the
* same operation twice */
/* Reset the number of irrecoverable errors */
HammingIrrecoverableErrorNb = 0;
/* Process the the Hamming (15,11,3) code
* check on each line (second time).
* Do not check the 4th last lines */
for(i = 0; i < 9; i++)
{
/* Get a full line */
for(j = 0; j < 15; j++)
{
LineUncorrected[j] = DataMatrix[i][j];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
{
/* Row/Line contains irrecoverable errors */
HammingIrrecoverableErrorNb++;
row_fail_p1++;
#if BPTC_196x96_DEBUG
fprintf(stderr, " BPTC196 P1 ROW FAIL row=%u rx=", (unsigned)i);
for (j = 0; j < 15; j++)
fprintf(stderr, "%u", LineBefore[j] & 1);
fprintf(stderr, "\n");
#endif
/* Keep original data bits on failure */
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineBefore[j];
}
else
{
/* Row/Line contains correct data */
}
/* Re-inject the line in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 11; j++)
{
DataMatrix[i][j] = LineCorrected[j];
/* rxBits were corrected in place; note any flip */
for (j = 0; j < 15; j++)
{
if ((LineBefore[j] & 1) != (LineUncorrected[j] & 1))
{
row_corr_p1++;
#if BPTC_196x96_DEBUG
fprintf(stderr,
" BPTC196 P1 ROW CORRECTED row=%u bit=%u %u->%u\n",
(unsigned)i, (unsigned)j,
(unsigned)(LineBefore[j] & 1),
(unsigned)(LineUncorrected[j] & 1));
#endif
}
}
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineCorrected[j];
}
}
#if BPTC_196x96_DEBUG
if (row_fail_p1 || row_corr_p1)
fprintf(stderr, " BPTC196 P1 ROW summary: fail=%u corrected_bits=%u\n",
(unsigned)row_fail_p1, (unsigned)row_corr_p1);
#endif
/* Process the the Hamming (15,11,3) code
* check on each column/row (second time) */
for(i = 0; i < 15; i++)
/* ========== Pass 1: column Hamming(13,9) on cols 0..14 ========== */
for (i = 0; i < 15; i++)
{
/* Get a full column */
for(j = 0; j < 13; j++)
for (j = 0; j < 13; j++)
{
ColumnUncorrected[j] = DataMatrix[j][i];
ColumnBefore[j] = ColumnUncorrected[j];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
if (Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
{
/* Row/Line contains irrecoverable errors */
HammingIrrecoverableErrorNb++;
col_fail_p1++;
#if BPTC_196x96_DEBUG
fprintf(stderr, " BPTC196 P1 COL FAIL col=%u rx=", (unsigned)i);
for (j = 0; j < 13; j++)
fprintf(stderr, "%u", ColumnBefore[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 9; j++)
DataMatrix[j][i] = ColumnBefore[j];
}
else
{
/* Row/Line contains correct data */
}
/* Re-inject the column in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 9; j++)
{
DataMatrix[j][i] = ColumnCorrected[j];
for (j = 0; j < 13; j++)
{
if ((ColumnBefore[j] & 1) != (ColumnUncorrected[j] & 1))
{
col_corr_p1++;
#if BPTC_196x96_DEBUG
fprintf(stderr,
" BPTC196 P1 COL CORRECTED col=%u bit=%u %u->%u\n",
(unsigned)i, (unsigned)j,
(unsigned)(ColumnBefore[j] & 1),
(unsigned)(ColumnUncorrected[j] & 1));
#endif
}
}
for (j = 0; j < 9; j++)
DataMatrix[j][i] = ColumnCorrected[j];
}
}
#if BPTC_196x96_DEBUG
if (col_fail_p1 || col_corr_p1)
fprintf(stderr, " BPTC196 P1 COL summary: fail=%u corrected_bits=%u\n",
(unsigned)col_fail_p1, (unsigned)col_corr_p1);
#endif
/* Extract the DMR data (96 bit) from the matrix */
/* First line : Do not take R(2), R(1) and R(0) */
/* Second pass can fix more after the first cascade */
HammingIrrecoverableErrorNb = 0;
/* ========== Pass 2: rows ========== */
for (i = 0; i < 9; i++)
{
for (j = 0; j < 15; j++)
{
LineUncorrected[j] = DataMatrix[i][j];
LineBefore[j] = LineUncorrected[j];
}
if (Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
row_fail_p2++;
#if BPTC_196x96_DEBUG
fprintf(stderr, " BPTC196 P2 ROW FAIL row=%u rx=", (unsigned)i);
for (j = 0; j < 15; j++)
fprintf(stderr, "%u", LineBefore[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineBefore[j];
}
else
{
for (j = 0; j < 15; j++)
{
if ((LineBefore[j] & 1) != (LineUncorrected[j] & 1))
{
row_corr_p2++;
#if BPTC_196x96_DEBUG
fprintf(stderr,
" BPTC196 P2 ROW CORRECTED row=%u bit=%u %u->%u\n",
(unsigned)i, (unsigned)j,
(unsigned)(LineBefore[j] & 1),
(unsigned)(LineUncorrected[j] & 1));
#endif
}
}
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineCorrected[j];
}
}
#if BPTC_196x96_DEBUG
if (row_fail_p2 || row_corr_p2)
fprintf(stderr, " BPTC196 P2 ROW summary: fail=%u corrected_bits=%u\n",
(unsigned)row_fail_p2, (unsigned)row_corr_p2);
#endif
/* ========== Pass 2: columns ========== */
for (i = 0; i < 15; i++)
{
for (j = 0; j < 13; j++)
{
ColumnUncorrected[j] = DataMatrix[j][i];
ColumnBefore[j] = ColumnUncorrected[j];
}
if (Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
col_fail_p2++;
#if BPTC_196x96_DEBUG
fprintf(stderr, " BPTC196 P2 COL FAIL col=%u rx=", (unsigned)i);
for (j = 0; j < 13; j++)
fprintf(stderr, "%u", ColumnBefore[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 9; j++)
DataMatrix[j][i] = ColumnBefore[j];
}
else
{
for (j = 0; j < 13; j++)
{
if ((ColumnBefore[j] & 1) != (ColumnUncorrected[j] & 1))
{
col_corr_p2++;
#if BPTC_196x96_DEBUG
fprintf(stderr,
" BPTC196 P2 COL CORRECTED col=%u bit=%u %u->%u\n",
(unsigned)i, (unsigned)j,
(unsigned)(ColumnBefore[j] & 1),
(unsigned)(ColumnUncorrected[j] & 1));
#endif
}
}
for (j = 0; j < 9; j++)
DataMatrix[j][i] = ColumnCorrected[j];
}
}
#if BPTC_196x96_DEBUG
if (col_fail_p2 || col_corr_p2)
fprintf(stderr, " BPTC196 P2 COL summary: fail=%u corrected_bits=%u\n",
(unsigned)col_fail_p2, (unsigned)col_corr_p2);
fprintf(stderr,
" BPTC196 FINAL irrecoverable=%u P1(row_f=%u col_f=%u row_c=%u col_c=%u) "
"P2(row_f=%u col_f=%u row_c=%u col_c=%u)\n",
(unsigned)HammingIrrecoverableErrorNb,
(unsigned)row_fail_p1, (unsigned)col_fail_p1,
(unsigned)row_corr_p1, (unsigned)col_corr_p1,
(unsigned)row_fail_p2, (unsigned)col_fail_p2,
(unsigned)row_corr_p2, (unsigned)col_corr_p2);
#endif
/* Extract 96 data bits */
k = 0;
for(i = 3; i < 11; i++)
for (i = 3; i < 11; i++)
{
DMRDataExtracted[k] = DataMatrix[0][i];
k++;
DMRDataExtracted[k++] = DataMatrix[0][i];
}
for (i = 1; i < 9; i++)
{
for (j = 0; j < 11; j++)
DMRDataExtracted[k++] = DataMatrix[i][j];
}
/* Next lines */
for(i = 1; i < 9; i++)
if (HammingIrrecoverableErrorNb == 0)
{
for(j = 0; j < 11; j++)
{
DMRDataExtracted[k] = DataMatrix[i][j];
k++;
}
R[0] = DataMatrix[0][2]; /* R(0) */
R[1] = DataMatrix[0][1]; /* R(1) */
R[2] = DataMatrix[0][0]; /* R(2) */
}
/* R(0) to R(2) may be used to transport some
* Restricted Access System (RAS) information,
* So save these three bits after hamming correction
* See patent US 2013/0288643 A1 */
R[0] = DataMatrix[0][2]; /* Save R(0) */
R[1] = DataMatrix[0][1]; /* Save R(1) */
R[2] = DataMatrix[0][0]; /* Save R(2) */
if (R[0] == 0 && R[1] == 0 && R[2] == 1)
*is_ras = 1;
else
*is_ras = 0;
/* Return the number of irrecoverable Hamming errors */
return HammingIrrecoverableErrorNb;
} /* End BPTC_196x96_Extract_Data() */
}
/*
* @brief : This function extract the 77 bits of a deinteleaved 128 bits
* buffer using BPTC (128,77).
* BPTC (128,77) extract with iterative Hamming + parity-guided repair
* and diagnostic logging.
* See ETSI TS 102 361-1 B.2.1
*
* @note : See DMR standard ETSI TS 102 361-1 chapter B.2.1 for details
*
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (128 bytes
act as a 16 x 8 byte data matrix)
*
* @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes)
* DMRDataExtracted[0..71] = Util data (hamming corrected)
* DMRDataExtracted[72..76] = 5 bit of CRC extracted (to be checked)
*
* @return 0 = All is OK
* Other = Error - Irrecoverable Hamming parity check error number
* Enable/disable chatter with BPTC_128x77_DEBUG (1 = on).
*/
// #define BPTC_128x77_DEBUG 1
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77])
{
uint32_t i, j, k;
@@ -394,102 +466,193 @@ uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDat
uint32_t ParityCheckErrorNb = 0;
uint32_t NbOfOne;
/* First step : Reconstitute the BPTC 16x8 matrix */
for(i = 0; i < 8; i++)
uint8_t row_bad[7];
uint8_t col_bad[16];
#if BPTC_128x77_DEBUG
fprintf (stderr, "\n");
#endif
for (i = 0; i < 8; i++)
{
for(j = 0; j < 16; j++)
{
/* Only the LSBit of the byte is stored */
for (j = 0; j < 16; j++)
DataMatrix[i][j] = (InputDataMatrix[i][j] & 1);
}
}
/* Init the Hamming (16,11,4) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_16_11_4_init();
/* Process the the Hamming (16,11,4) code
* check on each line.
* Do not check the last line (line nb 8) */
for(i = 0; i < 7; i++)
/* ---------- Pass 1: Hamming (16,11,4) on rows 0..6 ---------- */
memset(row_bad, 0, sizeof(row_bad));
for (i = 0; i < 7; i++)
{
/* Get a full line */
for(j = 0; j < 16; j++)
{
for (j = 0; j < 16; j++)
LineUncorrected[j] = DataMatrix[i][j];
}
/* Apply Hamming (16,11,4) code correction */
if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
if (Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
row_bad[i] = 1;
#if BPTC_128x77_DEBUG
fprintf(stderr, " BPTC128 P1 Hamming FAIL row=%u rx=", (unsigned)i);
for (j = 0; j < 16; j++)
fprintf(stderr, "%u", LineUncorrected[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineUncorrected[j];
}
/* Re-inject the line in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 11; j++)
else
{
DataMatrix[i][j] = LineCorrected[j];
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineCorrected[j];
}
}
#if BPTC_128x77_DEBUG
if (HammingIrrecoverableErrorNb)
fprintf(stderr, " BPTC128 P1 Hamming irrecoverable rows=%u\n",
(unsigned)HammingIrrecoverableErrorNb);
#endif
/* Extract the DMR data (77 bit) from the matrix */
k = 0;
/* 2 first lines */
for(i = 0; i < 2; i++)
{
for(j = 0; j < 11; j++)
{
DMRDataExtracted[k] = DataMatrix[i][j];
k++;
}
}
/* 5 Next lines */
for(i = 2; i < 7; i++)
{
for(j = 0; j < 10; j++)
{
DMRDataExtracted[k] = DataMatrix[i][j];
k++;
}
}
/* 5 bit of CRC */
for(i = 2; i < 7; i++)
{
DMRDataExtracted[k] = DataMatrix[i][10];
k++;
}
/* Verify the data integrity by checking
* all column parity bit */
/* ---------- Column even-parity check (row 7) ---------- */
memset(col_bad, 0, sizeof(col_bad));
ParityCheckErrorNb = 0;
for(i = 0; i < 16; i++)
for (i = 0; i < 16; i++)
{
NbOfOne = 0;
/* Get a full column */
for(j = 0; j < 7; j++)
{
for (j = 0; j < 7; j++)
NbOfOne += DataMatrix[j][i];
}
/* Check the parity bit (number of "1"
* must be even) */
if((NbOfOne % 2) != DataMatrix[7][i])
if ((NbOfOne % 2) != DataMatrix[7][i])
{
ParityCheckErrorNb++;
col_bad[i] = 1;
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 P1 parity FAIL col=%u sum7=%u parity_bit=%u (expect even)\n",
(unsigned)i, (unsigned)NbOfOne, (unsigned)DataMatrix[7][i]);
#endif
}
}
#if BPTC_128x77_DEBUG
if (ParityCheckErrorNb)
fprintf(stderr, " BPTC128 P1 parity bad columns=%u\n",
(unsigned)ParityCheckErrorNb);
#endif
/* ---------- Parity-guided single-bit repair ---------- */
{
uint32_t n_bad_rows = 0, n_bad_cols = 0;
uint32_t br = 0, bc = 0;
for (i = 0; i < 7; i++)
if (row_bad[i]) { n_bad_rows++; br = i; }
for (i = 0; i < 16; i++)
if (col_bad[i]) { n_bad_cols++; bc = i; }
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 repair candidates: bad_rows=%u bad_cols=%u",
(unsigned)n_bad_rows, (unsigned)n_bad_cols);
if (n_bad_rows == 1)
fprintf(stderr, " row=%u", (unsigned)br);
if (n_bad_cols == 1)
fprintf(stderr, " col=%u", (unsigned)bc);
fprintf(stderr, "\n");
#endif
if (n_bad_rows == 1 && n_bad_cols == 1 && bc < 11)
{
DataMatrix[br][bc] ^= 1;
row_bad[br] = 0;
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 parity-guided FLIP at row=%u col=%u -> %u\n",
(unsigned)br, (unsigned)bc, (unsigned)DataMatrix[br][bc]);
#endif
}
#if BPTC_128x77_DEBUG
else if (n_bad_rows || n_bad_cols)
{
fprintf(stderr,
" BPTC128 no single-intersection flip "
"(need exactly 1 bad row and 1 data col)\n");
}
#endif
}
/* ---------- Pass 2: Hamming (16,11,4) again ---------- */
HammingIrrecoverableErrorNb = 0;
for (i = 0; i < 7; i++)
{
for (j = 0; j < 16; j++)
LineUncorrected[j] = DataMatrix[i][j];
if (Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
#if BPTC_128x77_DEBUG
fprintf(stderr, " BPTC128 P2 Hamming FAIL row=%u rx=", (unsigned)i);
for (j = 0; j < 16; j++)
fprintf(stderr, "%u", LineUncorrected[j] & 1);
fprintf(stderr, "\n");
#endif
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineUncorrected[j];
}
else
{
for (j = 0; j < 11; j++)
DataMatrix[i][j] = LineCorrected[j];
}
}
#if BPTC_128x77_DEBUG
if (HammingIrrecoverableErrorNb)
fprintf(stderr, " BPTC128 P2 Hamming irrecoverable rows=%u\n",
(unsigned)HammingIrrecoverableErrorNb);
#endif
/* ---------- Final column parity recount ---------- */
ParityCheckErrorNb = 0;
for (i = 0; i < 16; i++)
{
NbOfOne = 0;
for (j = 0; j < 7; j++)
NbOfOne += DataMatrix[j][i];
if ((NbOfOne % 2) != DataMatrix[7][i])
{
ParityCheckErrorNb++;
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 P2 parity FAIL col=%u sum7=%u parity_bit=%u\n",
(unsigned)i, (unsigned)NbOfOne, (unsigned)DataMatrix[7][i]);
#endif
}
}
/* Return the number of irrecoverable Hamming errors +
* the number of parity check error */
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
} /* End BPTC_128x77_Extract_Data() */
#if BPTC_128x77_DEBUG
fprintf(stderr,
" BPTC128 result: Hamming_err=%u parity_err=%u total=%u\n",
(unsigned)HammingIrrecoverableErrorNb,
(unsigned)ParityCheckErrorNb,
(unsigned)(HammingIrrecoverableErrorNb + ParityCheckErrorNb));
#endif
/* ---------- Extract 77 bits ---------- */
k = 0;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 11; j++)
DMRDataExtracted[k++] = DataMatrix[i][j];
}
for (i = 2; i < 7; i++)
{
for (j = 0; j < 10; j++)
DMRDataExtracted[k++] = DataMatrix[i][j];
}
for (i = 2; i < 7; i++)
DMRDataExtracted[k++] = DataMatrix[i][10];
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
}
/*
* @brief : This function extract the 32 bits of a deinteleaved 32 bits
@@ -577,6 +740,52 @@ uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDat
} /* End BPTC_16x2_Extract_Data() */
uint32_t BPTC_128x77_Check_Errors(uint8_t InputDataMatrix[8][16])
{
uint32_t i, j;
uint32_t ham_bad = 0, ham_fix = 0, par_err = 0;
uint8_t line[16], before[16], decoded[11];
uint32_t ones;
int changed;
for (i = 0; i < 7; i++)
{
for (j = 0; j < 16; j++)
{
before[j] = (InputDataMatrix[i][j] & 1);
line[j] = before[j];
}
if (Hamming_16_11_4_decode(line, decoded, 1) == false)
{
ham_bad++;
}
else
{
changed = 0;
for (j = 0; j < 16; j++)
{
if (line[j] != before[j])
{
changed = 1;
break;
}
}
if (changed)
ham_fix++;
}
}
for (i = 0; i < 16; i++)
{
ones = 0;
for (j = 0; j < 7; j++)
ones += (InputDataMatrix[j][i] & 1);
if ((ones % 2) != (InputDataMatrix[7][i] & 1))
par_err++;
}
return ham_bad + ham_fix + par_err;
}
/* End of file */

View File

@@ -39,6 +39,7 @@
#include <stdint.h>
#include <stddef.h>
#include "dsd.h"
#include "bp.h"
#define AES_BLOCKLEN 16
unsigned Nb = 4;
@@ -510,6 +511,57 @@ void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
}
//Vertex Standard "AES" Encryption - convert AES key into a round key at 256 Nb,Nk,Nr values
//then select entries from the BP table to invert and convert to a keystream
void vertex_256_keystream_creation(dsd_state * state, char * key_string)
{
Nb = 4;
Nk = 8;
Nr = 14;
uint8_t key[32];
memset (key, 0, sizeof(key));
parse_raw_user_string(key_string, key);
struct AES_ctx ctx;
KeyExpansion(ctx.RoundKey, key);
uint64_t vtx_key = 0;
//use the bytes from round key output to select 4 BP key 16-bit words
for (int i = 224; i < 228; i++)
{
vtx_key <<= 16;
vtx_key |= BPK[ctx.RoundKey[i]];
}
//invert value
vtx_key = ~vtx_key;
fprintf (stderr,"Vertex Round Key Bytes: ");
for (uint16_t i = 224; i < 228; i++)
fprintf (stderr, "%02X ", ctx.RoundKey[i]);
fprintf (stderr, "Vertex Keystream: %016llX;", (unsigned long long int)vtx_key);
fprintf (stderr, "\n");
uint8_t vtx_bytes[8];
memset (vtx_bytes, 0, sizeof(vtx_bytes));
for (int i = 0; i < 8; i++)
vtx_bytes[i] = (vtx_key >> (56-(i*8))) & 0xFF;
unpack_byte_array_into_bit_array(vtx_bytes, state->static_ks_bits[0], 8);
unpack_byte_array_into_bit_array(vtx_bytes, state->static_ks_bits[1], 8);
//hex representation of the effective keystream value
state->vtx256_ekey = convert_bits_into_output(state->static_ks_bits[0], 64);
state->vtx_key_loaded = 1;
}
//byte-wise output of AES OFB Keystream
//input iv is a 16-byte uint8_t array of initialization vector
//input key is up to 32-byte uint8_t array of key value

View File

@@ -287,7 +287,7 @@ void des_cipher (uint8_t * main_key, uint8_t * input_register, uint8_t * output_
memcpy (left, initial_permutation+0, sizeof(left));
memcpy (right, initial_permutation+4, sizeof(right));
//16 fiestel rounds
//16 feistel rounds
for (uint8_t f = 0; f < 16; f++)
{
@@ -339,7 +339,102 @@ void des_cipher (uint8_t * main_key, uint8_t * input_register, uint8_t * output_
memcpy (left, right, sizeof(left));
memcpy (right, rperm, sizeof(right));
} //end fiestel rounds f
} //end feistel rounds f
//combine right and left into the pre_end_permutation array
memcpy (pre_end_permutation+0, right, sizeof(right));
memcpy (pre_end_permutation+4, left, sizeof(left));
//carry out the final permuation to get the completed output_register for this cipher iteration
permute (pre_end_permutation, output_register, final_register_permutation, 0, 64);
}
//kirisun variation on this cipher w/ preconfigured unique key schedule
void ksun_des_cipher (uint8_t * key, uint8_t * input_register, uint8_t * output_register, uint8_t de)
{
//starting and ending permutations
uint8_t initial_permutation[8]; memset(initial_permutation, 0, sizeof(initial_permutation));
uint8_t pre_end_permutation[8]; memset(pre_end_permutation, 0, sizeof(pre_end_permutation));
//intermediate left, right, and expansion/selection arrays
uint8_t left[4]; memset(left, 0, sizeof(left));
uint8_t right[4]; memset(right, 0, sizeof(right));
uint8_t rperm[4]; memset(rperm, 0, sizeof(rperm));
uint8_t exp[6]; memset(exp, 0, sizeof(exp));
uint8_t sel[4]; memset(sel, 0, sizeof(sel));
//key sets
uint8_t Ks[16][6]; memset(Ks, 0, sizeof(Ks));
//file the key into the keysets
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 6; j++)
Ks[i][j] = key[(i*6) + j];
}
//permute the input_register with the ip (initial_register_permutation) table
permute (input_register, initial_permutation, initial_register_permutation, 0, 64);
//copy the initial permutation to left and right half arrays
memcpy (left, initial_permutation+0, sizeof(left));
memcpy (right, initial_permutation+4, sizeof(right));
//16 feistel rounds
for (uint8_t f = 0; f < 16; f++)
{
//reset rperm and exp arrays
memset(rperm, 0, sizeof(rperm));
memset(exp, 0, sizeof(exp));
//expand right half from 32-bit to 48-bit
permute (right, exp, message_expansion, 0, 48);
//apply current iteration keyset to the expanded array (encryption cycles Ks forward, decryption goes in reverse)
if (de == 1)
{
for (uint8_t i = 0; i < 6; i++)
exp[i] ^= Ks[f][i];
}
else
{
for (uint8_t i = 0; i < 6; i++)
exp[i] ^= Ks[15-f][i];
}
//extract 6 bits for each sbox swap from the expanded array
uint8_t e1 = exp[0] >> 2;
uint8_t e2 = ((exp[0] & 0x3) << 4) | (exp[1] >> 4);
uint8_t e3 = ((exp[1] & 0xF) << 2) | (exp[2] >> 6);
uint8_t e4 = exp[2] & 0x3F;
uint8_t e5 = exp[3] >> 2;
uint8_t e6 = ((exp[3] & 0x3) << 4) | (exp[4] >> 4);
uint8_t e7 = ((exp[4] & 0xF) << 2) | (exp[5] >> 6);
uint8_t e8 = exp[5] & 0x3F;
//select 32-bits of each 48-bit expansion via modified S Box Look Up Tables
//swap the 6 bits for 4 bits via SLUT tables and OR them together into a selection array
sel[0] = SLUT1[e1] | SLUT2[e2];
sel[1] = SLUT3[e3] | SLUT4[e4];
sel[2] = SLUT5[e5] | SLUT6[e6];
sel[3] = SLUT7[e7] | SLUT8[e8];
//permute the selected 32-bits into a new right half
permute (sel, rperm, right_half_permutation, 0, 32);
//xor new right half against left half
for (uint8_t i = 0; i < 4; i++)
rperm[i] ^= left[i];
//swap old right half to left, set new right half to rperm
memcpy (left, right, sizeof(left));
memcpy (right, rperm, sizeof(right));
} //end feistel rounds f
//combine right and left into the pre_end_permutation array
memcpy (pre_end_permutation+0, right, sizeof(right));

View File

@@ -1,4 +1,5 @@
#include "dsd.h"
#include "dmr_const.h"
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input)
{
@@ -92,6 +93,9 @@ void straight_mod_xor_keystream_creation(dsd_state * state, char * input)
}
else goto END_KS;
//debug
// fprintf (stderr, "\ncurr: %s\n", curr);
uint8_t ks_bytes[112];
memset (ks_bytes, 0, sizeof(ks_bytes));
parse_raw_user_string(curr, ks_bytes);
@@ -122,5 +126,229 @@ void straight_mod_xor_keystream_creation(dsd_state * state, char * input)
if (curr == NULL)
fprintf (stderr, "Straight KS String Malformed! No KS Created!\n");
else
{
curr = strtok(NULL, ":"); //optional discard value
if (curr != NULL)
{
len = 0;
sscanf (curr, "%hd", &len);
if (len != 0 && len < 8)
{
state->straight_discard = (int)len;
fprintf (stderr, "Optional Straight XOR Keystream Discard: %d; \n", len);
}
}
}
}
void caltta_bp_keystream_creation(dsd_state * state, char * input)
{
uint16_t len = 0;
char * curr;
curr = strtok(input, ":"); //should be len (mod) of key (decimal)
if (curr != NULL)
sscanf (curr, "%hd", &len);
else goto END_KS;
//len sanity check, can't be greater than 882
if (len > 256)
len = 256;
curr = strtok(NULL, ":"); //should be key in hex
if (curr != NULL)
{
//continue
}
else goto END_KS;
uint8_t ks_bytes[112];
memset (ks_bytes, 0, sizeof(ks_bytes));
parse_raw_user_string(curr, ks_bytes);
uint8_t ks_bits[896];
memset(ks_bits, 0, sizeof(ks_bits));
uint16_t unpack_len = len / 8;
if (len % 8)
unpack_len++;
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, unpack_len);
for (uint16_t i = 0; i < len; i++)
{
state->static_ks_bits[0][i] = ks_bits[i];
state->static_ks_bits[1][i] = ks_bits[i];
}
fprintf (stderr,"Caltta BP %d-bit Keystream: ", len);
for (uint16_t i = 0; i < unpack_len; i++)
fprintf (stderr, "%02X", ks_bytes[i]);
fprintf (stderr, "\n");
state->straight_ks = 0x26;
state->straight_mod = (int)len;
END_KS:
if (curr == NULL)
fprintf (stderr, "Caltta BP KS String Malformed! No KS Created!\n");
}
void hytera_bp_keystream_creation(dsd_state * state, char * input)
{
uint16_t len = 0;
char * curr;
curr = strtok(input, ":"); //should be len (mod) of key (decimal)
if (curr != NULL)
sscanf (curr, "%hd", &len);
else goto END_KS;
//len sanity check, can't be greater than 882
if (len > 256)
len = 256;
curr = strtok(NULL, ":"); //should be key in hex
if (curr != NULL)
{
//continue
}
else goto END_KS;
uint8_t ks_bytes[112];
memset (ks_bytes, 0, sizeof(ks_bytes));
parse_raw_user_string(curr, ks_bytes);
uint8_t ks_bits[896];
memset(ks_bits, 0, sizeof(ks_bits));
uint16_t unpack_len = len / 8;
if (len % 8)
unpack_len++;
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, unpack_len);
for (uint16_t i = 0; i < len; i++)
{
state->static_ks_bits[0][i] = ks_bits[i];
state->static_ks_bits[1][i] = ks_bits[i];
}
fprintf (stderr,"Hytera BP %d-bit Keystream: ", len);
for (uint16_t i = 0; i < unpack_len; i++)
fprintf (stderr, "%02X", ks_bytes[i]);
state->straight_ks = 0x68;
state->straight_mod = (int)len;
END_KS:
if (curr == NULL)
fprintf (stderr, "Hytera BP KS String Malformed! No KS Created!\n");
}
//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++;
}
}
void vtx256_ambe2_interleave_inversion(char ambe_fr[4][24])
{
uint32_t silence_check = 0;
for (int i = 0; i < 20; i++)
{
silence_check <<= 1;
silence_check |= (uint8_t)ambe_fr[0][23-i];
}
if (silence_check == 0xF8014)
goto END_INV;
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++;
}
//invert the bits in the 49 to 71 position
for (int8_t i = 49; i < 72; i++)
interleaved[i] ^= 1;
//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++;
}
END_INV: ; //do nothing
}

View File

@@ -1,6 +1,9 @@
#include "dsd.h"
#include "pc4.h"
//function prototype linked in crypt-des.c
void ksun_des_cipher (uint8_t * key, uint8_t * input_register, uint8_t * output_register, uint8_t de);
/* === Global MD2II buffers === */
int x1, x2, i, n1global;
unsigned char h2[n1];
@@ -174,8 +177,69 @@ void keystream37(unsigned char *key, uint64_t frame, unsigned char *output)
}
//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)
#define ROTATE_LEFT(VALUE, SHIFT) \
(((VALUE) << ((SHIFT) & 63)) | ((VALUE) >> (64 - ((SHIFT) & 63))))
#define ROTATE_RIGHT(VALUE, SHIFT) \
(((VALUE) >> ((SHIFT) & 63)) | ((VALUE) << (64 - ((SHIFT) & 63))))
void keystream35(uint8_t * input_register, unsigned char * key)
{
uint64_t round = 0x9E3779B97F4A7C15;
uint64_t round_key[2]; memset(round_key, 0, sizeof(round_key));
uint64_t state[2]; memset(state, 0, sizeof(state));
//load input_register into state as two 64-bit chunks
for (int i = 0; i < 8; i++)
{
state[0] <<= 8;
state[0] |= input_register[i+0];
state[1] <<= 8;
state[1] |= input_register[i+8];
}
//load key into round_key as two 64-bit chunks
for (int i = 0; i < 8; i++)
{
round_key[0] <<= 8;
round_key[0] |= key[i+0];
round_key[1] <<= 8;
round_key[1] |= key[i+8];
}
for (int i = 0; i < 64; i++)
{
round_key[0] += round;
round_key[1] -= round;
state[0] ^= round_key[0];
state[0] += state[1];
state[0] = ROTATE_LEFT(state[0], state[1]);
state[1] = ROTATE_RIGHT(state[1], state[0]);
state[1] -= state[0];
state[1] ^= round_key[1];
}
uint64_t temp = state[0];
state[0] = state[1];
state[1] = temp;
//reload input_register with current state values
for (int i = 0; i < 8; i++)
input_register[i+0] = (state[0] >> (56 - (i * 8))) & 0xFF;
for (int i = 0; i < 8; i++)
input_register[i+8] = (state[1] >> (56 - (i * 8))) & 0xFF;
}
//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_universal_keystream_creation(dsd_state *state)
{
uint8_t slot = 0;
if (state->currentslot == 0)
@@ -292,8 +356,8 @@ void kirisun_uni_keystream_creation(dsd_state *state)
}
//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)
//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_advanced_keystream_creation(dsd_state *state)
{
uint8_t slot = 0;
@@ -415,3 +479,764 @@ void kirisun_adv_keystream_creation(dsd_state *state)
}
}
//dsd-fme -fs -Z -i 'SSD_STORAGE/2025_DEV/Tytera BP EP AP Master Folder/kirisun/enhanced-key-12.wav/enhanced-key-12.wav' -H 'C607727B8BECC3CB 7DD174F5526BB546'
void kirisun_enhanced_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[128]; 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];
if (K1 != 0 && K2 != 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]);
// printf("=== userkey input (16 bytes) ===\n");
// print_hex(userkeyhexa,16);
/* --- MD2II first pass --- */
n1global=16;
unsigned char h4[n1]; memset(h4, 0, sizeof(h4));
MD2II_init();
MD2II_hashing(userkeyhexa,16);
MD2II_end(h4);
for(int i=0;i<16;i++) realkey[i]=h4[i];
// printf("=== realkey final (16 bytes) ===\n");
// print_hex(realkey,16);
n1global=16;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,16);
MD2II_end(h4);
uint8_t input_register[16]; memset(input_register, 0, sizeof(input_register));
//copy h4 to the input_register for the cipher
memcpy(input_register, h4, 16);
/* --- MD2II with n1global=32 --- */
n1global=32;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,16);
MD2II_end(h4);
//copy h4 to the tab for the cipher
uint8_t tab[16]; memset(tab, 0, sizeof(tab));
memcpy(tab, h4, 16);
// printf("=== Input Register (16 bytes) ===\n");
// print_hex(input_register,16);
// printf("=== Tab (16 bytes) ===\n");
// print_hex(tab,16);
/* --- keystream output --- */
uint8_t output[128]; memset(output, 0, sizeof(output));
//discard round
keystream35(input_register, tab);
for (int i = 0; i < 8; i++)
{
keystream35(input_register, tab);
memcpy(output+(i*16), input_register, 16);
}
//store ks_bytes to storage
for (int i = 0; i < 128; i++)
ks_bytes[i] = 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 < 128; 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/kirisun_files/reinforced-key-9.wav' -H 'C607727B8BECC3CB 7DD174F5526BB546 B3DEA1C111DC1994 69243120CDB6F99A'
void kirisun_reinforced_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[128]; 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];
// printf("=== userkey input (32 bytes) ===\n");
// print_hex(userkeyhexa,32);
/* --- 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);
n1global=8;
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<8;i++)
internal_state=(internal_state<<8)+(h4[i]);
// printf("internal_state=%016llX\n",(unsigned long long)internal_state);
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy internal state value to the input_register for the modified DES cipher
memcpy(input_register, h4, 8);
/* --- MD2II with n1global=96 --- */
n1global=96;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("realkey (96 bytes):\n");
// print_hex(h4,96);
/* --- keystream output --- */
uint8_t output[128]; memset(output, 0, sizeof(output));
//run discard round first
ksun_des_cipher(h4, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
//begin creation of actual keystream now
for (int i = 0; i < 16; i++)
{
//the internal state is the output feedback mode
ksun_des_cipher(h4, input_register, output_register, 1);
memcpy(output+(i*8), output_register, 8);
memcpy(input_register, output_register, 8);
memset(output_register, 0, sizeof(output_register));
}
// printf("keystream output (128 bytes):\n");
// print_hex(output,128);
//store ks_bytes to storage
for (int i = 0; i < 128; i++)
ks_bytes[i] = 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 < 128; 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/kirisun_files/strengthened-key-7.wav' -H 'C607727B8BECC3CB 7DD174F5526BB546 B3DEA1C111DC1994 69243120CDB6F99A'
void kirisun_strengthened_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[128]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t global_key[288]; memset (global_key, 0, sizeof(global_key));
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];
// printf("=== userkey input (32 bytes) ===\n");
// print_hex(userkeyhexa,32);
/* --- MD2II first pass --- */
n1global=32;
unsigned char h4[288]; 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);
n1global=288;
MD2II_init();
MD2II_hashing(realkey, 32);
MD2II_end(h4);
for (int i=0;i<288;i++) global_key[i]=h4[i];
// printf("=== global key (288 bytes) ===\n");
// print_hex(realkey,288);
n1global=8;
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<8;i++)
internal_state=(internal_state<<8)+(h4[i]);
// printf("internal_state=%016llX\n",(unsigned long long)internal_state);
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy internal state value to the input_register for the modified DES cipher
memcpy(input_register, h4, 8);
/* --- MD2II with n1global=96 --- */
n1global=96;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("realkey (96 bytes):\n");
// print_hex(h4,96);
/* --- keystream output --- */
uint8_t output[128]; memset(output, 0, sizeof(output));
//run discard round first
ksun_des_cipher(global_key+0, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
ksun_des_cipher(global_key+96, input_register, output_register, 0);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
ksun_des_cipher(global_key+192, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
//begin creation of actual keystream now
for (int i = 0; i < 16; i++)
{
//the internal state is the output feedback mode
ksun_des_cipher(global_key+0, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
ksun_des_cipher(global_key+96, input_register, output_register, 0);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
memset(output_register, 0, sizeof(output_register));
ksun_des_cipher(global_key+192, input_register, output_register, 1);
memcpy(input_register, output_register, 8); //internal_state = des_reinforced(internal_state, encrypt);
//copy out last output_register to output
memcpy(output+(i*8), output_register, 8);
memset(output_register, 0, sizeof(output_register));
}
// printf("keystream output (128 bytes):\n");
// print_hex(output,128);
//store ks_bytes to storage
for (int i = 0; i < 128; i++)
ks_bytes[i] = 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 < 128; i++)
// {
// if ((i != 0) && ((i%16)==0))
// fprintf (stderr, "\n ");
// fprintf (stderr, "%02X ", ks_bytes[i]);
// }
// fprintf (stderr, "\n");
}
}
void kirisun_low_keystream_creation(dsd_state *state)
{
UNUSED(state);
}
void kirisun_middle_keystream_creation(dsd_state *state)
{
UNUSED(state);
}
void kirisun_high_keystream_creation(dsd_state *state)
{
UNUSED(state);
}
void kirisun_top_keystream_creation(dsd_state *state)
{
UNUSED(state);
}
#define BLOCK_BYTES 512
#define MEMORY_BYTES 4608
static uint8_t memory_area[MEMORY_BYTES];
static uint8_t *substitution_area = memory_area;
static uint8_t *state_area = memory_area + 2048;
static uint8_t *feedback_area = memory_area + 3584;
static uint8_t *temporary_area = memory_area + 4096;
static size_t processed_bytes;
static uint8_t feedback_value;
static const uint8_t substitution_maps[16][16] = {
{ 0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3 },
{ 0xE, 0xB, 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9 },
{ 0x5, 0x8, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6, 0x0, 0x9, 0xB },
{ 0x7, 0xD, 0xA, 0x1, 0x0, 0x8, 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3 },
{ 0x6, 0xC, 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2 },
{ 0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, 0x8, 0x5, 0x9, 0xC, 0xF, 0xE },
{ 0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, 0x6, 0x8, 0x2, 0xC },
{ 0x1, 0xF, 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, 0x8, 0xC },
{ 0xC, 0x4, 0x6, 0x2, 0xA, 0x5, 0xB, 0x9, 0xE, 0x8, 0xD, 0x7, 0x0, 0x3, 0xF, 0x1 },
{ 0x6, 0x8, 0x2, 0x3, 0x9, 0xA, 0x5, 0xC, 0x1, 0xE, 0x4, 0x7, 0xB, 0xD, 0x0, 0xF },
{ 0xB, 0x3, 0x5, 0x8, 0x2, 0xF, 0xA, 0xD, 0xE, 0x1, 0x7, 0x4, 0xC, 0x9, 0x6, 0x0 },
{ 0xC, 0x8, 0x2, 0x1, 0xD, 0x4, 0xF, 0x6, 0x7, 0x0, 0xA, 0x5, 0x3, 0xE, 0x9, 0xB },
{ 0x7, 0xF, 0x5, 0xA, 0x8, 0x1, 0x6, 0xD, 0x0, 0x9, 0x3, 0xE, 0xB, 0x4, 0x2, 0xC },
{ 0x5, 0xD, 0xF, 0x6, 0x9, 0x2, 0xC, 0xA, 0xB, 0x7, 0x8, 0x1, 0x4, 0x3, 0xE, 0x0 },
{ 0x8, 0xE, 0x2, 0x5, 0x6, 0x9, 0x1, 0xC, 0xF, 0x4, 0xB, 0x0, 0xD, 0xA, 0x3, 0x7 },
{ 0x1, 0x7, 0xE, 0xD, 0x0, 0x5, 0x8, 0x3, 0x4, 0xF, 0xA, 0x6, 0x9, 0xC, 0xB, 0x2 }
};
static const uint8_t nonlinear_map[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};
void prepare_tables(void)
{
for (unsigned int index = 0; index < 256; ++index) {
substitution_area[7 * 256 + index] = (uint8_t)((substitution_maps[15][index >> 4] << 4) | substitution_maps[14][index & 15]);
substitution_area[6 * 256 + index] = (uint8_t)((substitution_maps[13][index >> 4] << 4) | substitution_maps[12][index & 15]);
substitution_area[5 * 256 + index] = (uint8_t)((substitution_maps[11][index >> 4] << 4) | substitution_maps[10][index & 15]);
substitution_area[4 * 256 + index] = (uint8_t)((substitution_maps[9][index >> 4] << 4) | substitution_maps[8][index & 15]);
substitution_area[3 * 256 + index] = (uint8_t)((substitution_maps[7][index >> 4] << 4) | substitution_maps[6][index & 15]);
substitution_area[2 * 256 + index] = (uint8_t)((substitution_maps[5][index >> 4] << 4) | substitution_maps[4][index & 15]);
substitution_area[1 * 256 + index] = (uint8_t)((substitution_maps[3][index >> 4] << 4) | substitution_maps[2][index & 15]);
substitution_area[index] = (uint8_t)((substitution_maps[1][index >> 4] << 4) | substitution_maps[0][index & 15]);
}
}
void reset_state(void)
{
processed_bytes = 0;
feedback_value = 0;
memset(feedback_area, 0, 512);
memset(state_area, 0, 1536);
}
void md2IIult(const uint8_t input[], size_t length)
{
size_t input_position = 0;
while (length != 0) {
for (; length && processed_bytes < BLOCK_BYTES; --length, ++processed_bytes) {
uint8_t current_byte = input[input_position++];
state_area[processed_bytes + BLOCK_BYTES] = current_byte;
state_area[processed_bytes + 2 * BLOCK_BYTES] = (uint8_t)(current_byte ^ state_area[processed_bytes]);
feedback_area[processed_bytes] ^= nonlinear_map[current_byte ^ feedback_value];
feedback_value = feedback_area[processed_bytes];
}
if (processed_bytes == BLOCK_BYTES) {
unsigned int accumulator = 0;
processed_bytes = 0;
for (unsigned int round = 0; round < BLOCK_BYTES + 2; ++round) {
for (unsigned int index = 0; index < 3 * BLOCK_BYTES; ++index) {
state_area[index] ^= nonlinear_map[accumulator];
accumulator = state_area[index];
}
accumulator = (accumulator + round) & 255U;
}
}
}
}
void finalize_state(uint8_t output[BLOCK_BYTES])
{
size_t remaining_bytes = BLOCK_BYTES - processed_bytes;
memset(temporary_area, (int)remaining_bytes, remaining_bytes);
md2IIult(temporary_area, remaining_bytes);
md2IIult(feedback_area, BLOCK_BYTES);
memcpy(output, state_area, BLOCK_BYTES);
}
void derive_schedule(const uint8_t digest[BLOCK_BYTES], uint8_t schedule[BLOCK_BYTES])
{
size_t digest_position = 0;
memset(schedule, 0, BLOCK_BYTES);
for (unsigned int block = 0; block < 64; ++block) {
uint64_t value = 0;
for (unsigned int index = 0; index < 8; ++index) value = (value << 8) | digest[digest_position++];
memcpy(schedule + block * 8, &value, sizeof(value));
}
}
static uint64_t transform_word(uint64_t value)
{
uint32_t upper_word = (uint32_t)(value >> 32);
uint32_t lower_word = (uint32_t)value;
upper_word = ((uint32_t)substitution_area[3 * 256 + ((upper_word >> 24) & 255u)] << 24) |
((uint32_t)substitution_area[2 * 256 + ((upper_word >> 16) & 255u)] << 16) |
((uint32_t)substitution_area[1 * 256 + ((upper_word >> 8) & 255u)] << 8) |
(uint32_t)substitution_area[upper_word & 255u];
lower_word = ((uint32_t)substitution_area[7 * 256 + ((lower_word >> 24) & 255u)] << 24) |
((uint32_t)substitution_area[6 * 256 + ((lower_word >> 16) & 255u)] << 16) |
((uint32_t)substitution_area[5 * 256 + ((lower_word >> 8) & 255u)] << 8) |
(uint32_t)substitution_area[4 * 256 + (lower_word & 255u)];
value = ((uint64_t)upper_word << 32) | lower_word;
return (value << 11) | (value >> (64 - 11));
}
void encode_block(const uint64_t input[2], uint64_t output[2], const uint8_t schedule[BLOCK_BYTES])
{
uint64_t left_word = input[0], right_word = input[1];
for (unsigned int round = 0; round < 32; ++round) {
uint64_t round_key;
memcpy(&round_key, schedule + round * 16, sizeof(round_key));
right_word ^= transform_word(left_word + round_key);
memcpy(&round_key, schedule + round * 16 + 8, sizeof(round_key));
left_word ^= transform_word(right_word + round_key);
}
output[0] = right_word;
output[1] = left_word;
}
void decode_block(const uint64_t input[2], uint64_t output[2], const uint8_t schedule[BLOCK_BYTES])
{
uint64_t left_word = input[0], right_word = input[1];
for (int round = 31; round >= 0; --round) {
uint64_t round_key;
memcpy(&round_key, schedule + round * 16 + 8, sizeof(round_key));
right_word ^= transform_word(left_word + round_key);
memcpy(&round_key, schedule + round * 16, sizeof(round_key));
left_word ^= transform_word(right_word + round_key);
}
output[0] = right_word;
output[1] = left_word;
}
void kirisun_ultra_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[128]; 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];
// printf("=== userkey input (32 bytes) ===\n");
// print_hex(userkeyhexa,32);
/* --- 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);
unsigned char digest[512]; memset(digest, 0, sizeof(digest));
unsigned char schedule[512]; memset(schedule, 0, sizeof(schedule));
prepare_tables();
reset_state();
md2IIult(realkey, 32);
finalize_state(digest);
derive_schedule(digest, schedule);
n1global=32;
MD2II_init();
MD2II_hashing(lfsrhexa,4);
MD2II_hashing(realkey,32);
MD2II_end(h4);
// printf("h4[0..7]: ");
// print_hex(h4,8);
//the state machine is comprised of two 64-bit words that
//when output become the bytes of the keystream
uint64_t internal_state[2]; memset(internal_state, 0, sizeof(internal_state));
for (int i = 0; i < 8; i++)
{
internal_state[0] <<= 8;
internal_state[0] |= (uint64_t)h4[i];
internal_state[1] <<= 8;
internal_state[1] |= (uint64_t)h4[i+8];
}
/* --- keystream output --- */
uint8_t output[128]; memset(output, 0, sizeof(output));
for (int i = 0; i < 8; i++)
{
encode_block(internal_state, internal_state, schedule);
output[(i*16)+0] = (internal_state[0] >> 56) & 0xFF;
output[(i*16)+1] = (internal_state[0] >> 48) & 0xFF;
output[(i*16)+2] = (internal_state[0] >> 40) & 0xFF;
output[(i*16)+3] = (internal_state[0] >> 32) & 0xFF;
output[(i*16)+4] = (internal_state[0] >> 24) & 0xFF;
output[(i*16)+5] = (internal_state[0] >> 16) & 0xFF;
output[(i*16)+6] = (internal_state[0] >> 8) & 0xFF;
output[(i*16)+7] = (internal_state[0] >> 0) & 0xFF;
output[(i*16)+8] = (internal_state[1] >> 56) & 0xFF;
output[(i*16)+9] = (internal_state[1] >> 48) & 0xFF;
output[(i*16)+10] = (internal_state[1] >> 40) & 0xFF;
output[(i*16)+11] = (internal_state[1] >> 32) & 0xFF;
output[(i*16)+12] = (internal_state[1] >> 24) & 0xFF;
output[(i*16)+13] = (internal_state[1] >> 16) & 0xFF;
output[(i*16)+14] = (internal_state[1] >> 8) & 0xFF;
output[(i*16)+15] = (internal_state[1] >> 0) & 0xFF;
}
// printf("keystream output (128 bytes):\n");
// print_hex(output,128);
//store ks_bytes to storage
for (int i = 0; i < 128; i++)
ks_bytes[i] = 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 < 128; i++)
// {
// if ((i != 0) && ((i%16)==0))
// fprintf (stderr, "\n ");
// fprintf (stderr, "%02X ", ks_bytes[i]);
// }
// fprintf (stderr, "\n");
}
}
//General Keystream Creator for any known Kirisun cipher
void kirisun_keystream_creation(dsd_state * state)
{
uint8_t algid = 0;
if (state->currentslot == 0)
algid = state->payload_algid;
else algid = state->payload_algidR;
if (algid == 0x31)
kirisun_reinforced_keystream_creation(state);
else if (algid == 0x32)
kirisun_low_keystream_creation(state);
else if (algid == 0x33)
kirisun_middle_keystream_creation(state);
else if (algid == 0x34)
kirisun_high_keystream_creation(state);
else if (algid == 0x35)
kirisun_enhanced_keystream_creation(state);
else if (algid == 0x36)
kirisun_advanced_keystream_creation(state);
else if (algid == 0x37)
kirisun_universal_keystream_creation(state);
else if (algid == 0x38)
kirisun_strengthened_keystream_creation(state);
else if (algid == 0x39)
kirisun_top_keystream_creation(state);
else if (algid == 0x3A)
kirisun_ultra_keystream_creation(state);
}

458
src/crypt-md5.c Normal file
View File

@@ -0,0 +1,458 @@
//MD5 Code Originally Sourced From: https://github.com/Zunawe/md5-c
/*
* Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm
* and modified slightly to be functionally identical but condensed into control structures.
*/
#include "dsd.h"
typedef struct{
uint64_t size; // Size of input in bytes
uint32_t buffer[4]; // Current accumulation of hash
uint8_t input[64]; // Input to be used in the next step
uint8_t digest[16]; // Result of algorithm
}MD5Context;
/*
* Constants defined by the MD5 algorithm
*/
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
/*
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
*/
static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/*
* Bit-manipulation functions defined by the MD5 algorithm
*/
#define F(X, Y, Z) ((X & Y) | (~X & Z))
#define G(X, Y, Z) ((X & Z) | (Y & ~Z))
#define H(X, Y, Z) (X ^ Y ^ Z)
#define I(X, Y, Z) (Y ^ (X | ~Z))
/*
* Rotates a 32-bit word left by n bits
*/
uint32_t rotateLeft(uint32_t x, uint32_t n){
return (x << n) | (x >> (32 - n));
}
/*
* Initialize a context
*/
void md5Init(MD5Context *ctx){
ctx->size = (uint64_t)0;
ctx->buffer[0] = (uint32_t)A;
ctx->buffer[1] = (uint32_t)B;
ctx->buffer[2] = (uint32_t)C;
ctx->buffer[3] = (uint32_t)D;
}
/*
* Step on 512 bits of input with the main MD5 algorithm.
*/
void md5Step(uint32_t *buffer, uint32_t *input){
uint32_t AA = buffer[0];
uint32_t BB = buffer[1];
uint32_t CC = buffer[2];
uint32_t DD = buffer[3];
uint32_t E;
unsigned int j;
for(unsigned int i = 0; i < 64; ++i){
switch(i / 16){
case 0:
E = F(BB, CC, DD);
j = i;
break;
case 1:
E = G(BB, CC, DD);
j = ((i * 5) + 1) % 16;
break;
case 2:
E = H(BB, CC, DD);
j = ((i * 3) + 5) % 16;
break;
default:
E = I(BB, CC, DD);
j = (i * 7) % 16;
break;
}
uint32_t temp = DD;
DD = CC;
CC = BB;
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);
AA = temp;
}
buffer[0] += AA;
buffer[1] += BB;
buffer[2] += CC;
buffer[3] += DD;
}
/*
* Add some amount of input to the context
*
* If the input fills out a block of 512 bits, apply the algorithm (md5Step)
* and save the result in the buffer. Also updates the overall size.
*/
void md5Update(MD5Context *ctx, uint8_t *input_buffer, size_t input_len){
uint32_t input[16];
unsigned int offset = ctx->size % 64;
ctx->size += (uint64_t)input_len;
// Copy each byte in input_buffer into the next space in our context input
for(unsigned int i = 0; i < input_len; ++i){
ctx->input[offset++] = (uint8_t)*(input_buffer + i);
// If we've filled our context input, copy it into our local array input
// then reset the offset to 0 and fill in a new buffer.
// Every time we fill out a chunk, we run it through the algorithm
// to enable some back and forth between cpu and i/o
if(offset % 64 == 0){
for(unsigned int j = 0; j < 16; ++j){
// Convert to little-endian
// The local variable `input` our 512-bit chunk separated into 32-bit words
// we can use in calculations
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
(uint32_t)(ctx->input[(j * 4)]);
}
md5Step(ctx->buffer, input);
offset = 0;
}
}
}
/*
* Pad the current input to get to 448 bytes, append the size in bits to the very end,
* and save the result of the final iteration into digest.
*/
void md5Finalize(MD5Context *ctx){
uint32_t input[16];
unsigned int offset = ctx->size % 64;
unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
// Fill in the padding and undo the changes to size that resulted from the update
md5Update(ctx, PADDING, padding_length);
ctx->size -= (uint64_t)padding_length;
// Do a final update (internal to this function)
// Last two 32-bit words are the two halves of the size (converted from bytes to bits)
for(unsigned int j = 0; j < 14; ++j){
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
(uint32_t)(ctx->input[(j * 4)]);
}
input[14] = (uint32_t)(ctx->size * 8);
input[15] = (uint32_t)((ctx->size * 8) >> 32);
md5Step(ctx->buffer, input);
// Move the result into digest (convert from little-endian)
for(unsigned int i = 0; i < 4; ++i){
ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF));
ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >> 8);
ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16);
ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24);
}
}
/*
* Functions that run the algorithm on the provided input and put the digest into result.
* result should be able to store 16 bytes.
*/
void md5String(char *input, uint8_t *result){
MD5Context ctx;
md5Init(&ctx);
md5Update(&ctx, (uint8_t *)input, strlen(input));
md5Finalize(&ctx);
memcpy(result, ctx.digest, 16);
}
void md5File(FILE *file, uint8_t *result){
char *input_buffer = malloc(1024);
size_t input_size = 0;
MD5Context ctx;
md5Init(&ctx);
while((input_size = fread(input_buffer, 1, 1024, file)) > 0){
md5Update(&ctx, (uint8_t *)input_buffer, input_size);
}
md5Finalize(&ctx);
free(input_buffer);
memcpy(result, ctx.digest, 16);
}
/**
* RAS 104-bit permutation
*
* Generates a permutation of bit positions 0..103 using the 32-bit seed
* (the last four active key bytes interpreted as a big-endian uint32).
*
* Algorithm:
* - Maintain a circular list of the still-available positions.
* - Repeatedly select (seed % remaining_count) steps from the current
* position, record that index, remove it, and continue from its successor.
*
* Parameters:
* seed - 32-bit seed (e.g. 0x92604BD5)
* order - output array of 104 integers. order[i] = source bit position
* that should be placed at output bit i.
*/
void ras_permute_order(uint32_t seed, int * order)
{
int remaining[104];
int count = 104;
int pos = 0; /* current index inside the remaining[] array */
for (int i = 0; i < 104; i++)
remaining[i] = i;
for (int i = 0; i < 104; i++)
{
unsigned idx = seed % (unsigned)count;
int actual = (pos + (int)idx) % count;
order[i] = remaining[actual];
#ifdef DEBUG_RAS
//debug
// fprintf (stderr, "\n I: %d; C: %d; P: %d; IDX: %u; ACT: %d; ORD: %d; ", i, count, pos, idx, actual, order[i]);
#endif
/* remove the chosen entry */
for (int j = actual; j < count - 1; j++)
remaining[j] = remaining[j + 1];
count--;
/* continue from the successor of the removed element */
if (count > 0)
pos = actual % count;
}
#ifdef DEBUG_RAS
//debug
// fprintf(stderr, "\n Order: ");
// for (int i = 0; i < 104; i++)
// fprintf (stderr, "%03d,", order[i]);
#endif
}
void apply_ras_permutation(const uint8_t * src, uint8_t * dst, const int * order)
{
for (int i = 0; i < 104; i++)
{
int src_bit = order[i];
int src_byte = src_bit / 8;
int src_off = 7 - (src_bit % 8); /* MSB of byte is lowest bit index */
int bit = (src[src_byte] >> src_off) & 1;
int dst_byte = i / 8;
int dst_off = 7 - (i % 8);
if (bit)
dst[dst_byte] |= (uint8_t)(1u << dst_off);
}
}
//Full RAS workflow, return value is the the calculated MAC attachment
//input is a pointer to byte-wise payload from dburst of RAS enabled payloads
uint32_t ras_mac_calculator(dsd_state * state, uint8_t * input, int input_len, int type)
{
if (state->ras_effective_key == 0)
return 0;
uint32_t mac = 0;
uint8_t ras_input[13];
memset (ras_input, 0, sizeof(ras_input));
for (int i = 0; i < input_len; i++)
ras_input[i] = input[i];
int shift = 48;
for (int i = input_len; i < 13; i++)
{
ras_input[i] = (state->ras_effective_key >> shift) & 0xFF;
shift -= 8;
}
#ifdef DEBUG_RAS
//debug input value
if (type != 2)
{
fprintf (stderr, "\n RAS Input: ");
for (int i = 0; i < 13; i++)
fprintf (stderr, "%02X", ras_input[i]);
}
#endif
uint8_t ras_output[16]; memset(ras_output, 0, sizeof(ras_output));
if (type != 2)
{
apply_ras_permutation(ras_input, ras_output, state->ras_permutation_order);
#ifdef DEBUG_RAS
//debug output value
fprintf (stderr, "\n RAS Output: ");
for (int i = 0; i < 13; i++)
fprintf (stderr, "%02X", ras_output[i]);
#endif
}
//Run Appropriate CRC or RS calculation on Permuted Output
uint8_t output_bits[104]; memset(output_bits, 0, sizeof(output_bits));
unpack_byte_array_into_bit_array(ras_output, output_bits, 13);
if (type == 0)
mac = ComputeCrcCCITT16d(output_bits, 13*8);
else if (type == 1)
{
rs_16_13_encode(ras_output);
for (int i = 0; i < 3; i++)
{
mac <<= 8;
mac |= ras_output[i+13];
}
}
else if (type == 2)
{
mac = ComputeCrc5Bit(input);
mac += state->ras_effective_key & 0x1F;
mac %= 31;
}
return mac;
}
void ras_effective_key_creation(dsd_state * state, char * input)
{
uint8_t md5_key[30];
memset (md5_key, 0, sizeof(md5_key));
uint16_t len = strlen((const char*)input);
//sanity check on provided char len
if (len > 24)
len = 24;
//load chars as uint8_t into key array
for (int i = 0; i < len; i++)
md5_key[i] = (uint8_t)input[i];
//debug
fprintf (stderr, "RAS Key String: %s; LEN: %d; HEX: ", input, len);
for (int i = 0; i < len; i++)
fprintf (stderr, "%02X ", md5_key[i]);
fprintf (stderr, "\n");
MD5Context ctx;
md5Init(&ctx);
md5Update(&ctx, md5_key, len);
md5Finalize(&ctx);
//debug dump the digest
fprintf (stderr, "MD5: ");
for (int i = 0; i < 16; i++)
fprintf (stderr, "%02X ", ctx.digest[i]);
fprintf (stderr, "\n");
//collect 7 bytes of the MD5 Hash to create the flat key value
unsigned long long int flat_key = 0;
for (int i = 0; i < 7; i++)
{
flat_key <<= 8;
flat_key |= ctx.digest[i];
}
//On real world testing, it was discovered that by selecting a password (VluByDZhkPenguinkeeper) which has
//an MD5 partial hash of FFFFFFFFFEDFFF, the value actually placed into the radio was FFFFFFFFFEE001
//this indicates the entire value will roll-over, and not just a byte rollvoer (mod)
//The known rule is that the last 5 bits of the flat key value (prior to reverse)
//is that it cannot equal 0x1F (all 1 bits), if so, a correction of +2 is
//performaed onto a flat value, which increments the entire value appropriately
//Below patent also suggest all bytes cannot equal zero
//and if so, the next portion of MD5 may be used,
//or some other method may be utilized to prevent fringe cases
//https://patents.google.com/patent/US20130288643A1
if ((flat_key & 0x1F) == 0x1F)
{
fprintf (stderr, "Flat Correction: %014llX -> %014llX;\n", flat_key, flat_key+2);
flat_key += 2;
}
//byte-reverse the flat_key into the effective_key
unsigned long long int effective_key = 0;
for (int i = 0; i < 7; i++)
{
effective_key <<= 8;
effective_key |= (flat_key >> (i*8)) & 0xFF;
}
uint32_t ras_seed = effective_key & 0xFFFFFFFF;
ras_permute_order(ras_seed, state->ras_permutation_order);
//debug
fprintf (stderr, "RAS Effective Key Value: %014llX;", effective_key);
state->ras_effective_key = effective_key;
fprintf (stderr, "\n");
}

File diff suppressed because it is too large Load Diff

View File

@@ -307,55 +307,3 @@ void tyt_ep_aes_keystream_creation(dsd_state * state, char * input)
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

@@ -187,6 +187,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera");
else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera");
else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot");
else if (p_mfid == 0x77) sprintf (mfid_string, "%s", "VTX STD");
else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard");
else sprintf (mfid_string, "%s", "Other");
}
@@ -270,13 +271,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 +293,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.
@@ -368,38 +379,77 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
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));
fprintf (stderr, "\n Vertex Standard PDU ENC Header;");
//Unsure of how similar this is to the DMRA Enc Header, but using
//guess work and best judgment from single sample of Vertex Enhanced
//This Extended Header only seems to exist to signal encryption,
//but is otherwise mostly empty aside from a FID and single 0x01 value
//[4F][77][01][00][00][00][00][00][00][00][XX][XX]
//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)
//Further analysis shows that using different key types on different channels
//does not change the byte values of this header, and what is reported is that
//the Vertex Standard radios only allow one encryption type and key per channel.
//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 == 0x68)
{
//TODO: Assume Alg, Key ID, IV and other values present in this header
//if using Hytera Enhanced RC4, otherwise, may use DMRA header
//ALG 1 = BP (observed), ALG 2 = Hytera Enhanced (not observed, but speculation)
/*
15:44:00 Sync: +DMR MS/DM MODE/MONO | Color Code=01 | DATA
Slot 1 Data Header - Extended - SAP 03 [UDP Comp] - MFID 68 [Hytera]
Unknown Extended Header: 0101000000000000
DMR PDU Payload [3F][68][01][01][00][00][00][00][00][00][E6][71] <-- key 1
16:06:21 Sync: +DMR MS/DM MODE/MONO | Color Code=02 | DATA
Slot 1 Data Header - Extended - SAP 03 [UDP Comp] - MFID 68 [Hytera]
Hytera PDU ENC Header;
DMR PDU Payload [3F][68][01][02][00][00][00][00][00][00][3E][F3] <--key 2
*/
uint8_t alg = (uint8_t)ConvertBitIntoBytes(&dheader_bits[16], 8);
uint8_t key = (uint8_t)ConvertBitIntoBytes(&dheader_bits[24], 8);
uint64_t mi = (uint64_t)ConvertBitIntoBytes(&dheader_bits[32], 40);
fprintf (stderr, "\n Hytera PDU ENC Header: MFID: %02X; ALG: %02X; Key ID: %02X;", p_mfid, alg, key);
if (mi != 0) fprintf (stderr, "MI: %010llX", (unsigned long long int)mi);
//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;
//going to use algid of 0x68*alg to signal a Hytera enc method
//should be 0x68 for bp, or 0xD0 for enhanced
if (state->currentslot == 0)
state->payload_algid = 0x68 * alg;
else state->payload_algidR = 0x68 * alg;
if (state->currentslot == 0)
state->payload_keyid = key;
else state->payload_keyidR = key;
if (state->currentslot == 0)
state->payload_mi = mi;
else state->payload_miR = mi;
//reset ks start value
state->data_ks_start[slot] = 0;
}
else if (p_sap != 1 && p_mfid == 0x10)
{
@@ -1028,20 +1078,20 @@ 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;
kaes[i+8] = ((state->rkey_array[kid+0x101]) >> (56-(i*8))) & 0xFF;
kaes[i+16] = ((state->rkey_array[kid+0x201]) >> (56-(i*8))) & 0xFF;
kaes[i+24] = ((state->rkey_array[kid+0x301]) >> (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 kaes is loaded with a key, then flag on the key loaded variable
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)
{
@@ -1079,6 +1129,8 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;");
if (alg == 7) fprintf (stderr, " VTX STD;");
if (alg == 0x68) fprintf (stderr, " Hytera BP;");
if (alg == 0xD0) fprintf (stderr, " Hytera EP;");
if (R && alg != 0) fprintf (stderr, " Key: %010llX;", R);
//generate 128-bit IV from 32-bit MI
@@ -1105,6 +1157,65 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
decrypted_pdu = 1;
}
else if (alg == 0x68) //Hytera BP
{
//just passing through to see if a key was loaded and the blocks decrypted
decrypted_pdu = hytera_bp_data_decrypt(state, empt, 1);
}
else if (alg == 0xD0) //Hytera Enhanced RC4
{
//just passing through to see if a key was loaded and the blocks decrypted
decrypted_pdu = hytera_ep_data_decrypt(opts, state, empt, 1);
}
else if (alg == 7 && state->vtx_key_loaded == 1) //Vertex Standard, either enc type
{
pack_bit_array_into_byte_array(state->static_ks_bits[slot], ob, 8);
end += (poc + 4); //also perform application over padding bytes and crc value
for (i = 0; i < end; i++)
state->dmr_pdu_sf[slot][i+start] ^= ob[i%8];
decrypted_pdu = 1;
//Recalcuate CRC32
uint8_t reordered_bits[1000];
memset (reordered_bits, 0, sizeof(reordered_bits));
//rearrage for ridiculously stupid CRC32 LSO/MSO ordering
for(i = 0, j = 0; i < ctr; i+=2, j+=16)
{
reordered_bits[j + 0] = (state->dmr_pdu_sf[slot][i+1] >> 7) & 0x01;
reordered_bits[j + 1] = (state->dmr_pdu_sf[slot][i+1] >> 6) & 0x01;
reordered_bits[j + 2] = (state->dmr_pdu_sf[slot][i+1] >> 5) & 0x01;
reordered_bits[j + 3] = (state->dmr_pdu_sf[slot][i+1] >> 4) & 0x01;
reordered_bits[j + 4] = (state->dmr_pdu_sf[slot][i+1] >> 3) & 0x01;
reordered_bits[j + 5] = (state->dmr_pdu_sf[slot][i+1] >> 2) & 0x01;
reordered_bits[j + 6] = (state->dmr_pdu_sf[slot][i+1] >> 1) & 0x01;
reordered_bits[j + 7] = (state->dmr_pdu_sf[slot][i+1] >> 0) & 0x01;
reordered_bits[j + 8] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
reordered_bits[j + 9] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
reordered_bits[j + 10] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
reordered_bits[j + 11] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
reordered_bits[j + 12] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
reordered_bits[j + 13] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
reordered_bits[j + 14] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
reordered_bits[j + 15] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
}
CRCComputed = (uint32_t)ComputeCrc32BitV(reordered_bits, (ctr * 8) - 32);
CRCExtracted = (uint32_t)convert_bits_into_output(reordered_bits+((ctr * 8) - 32), 32);
if (CRCComputed == CRCExtracted)
CRCCorrect = 1;
else CRCCorrect = 0;
//set back to original value
end -= (poc + 4);
//debug
// fprintf (stderr, " CTR: %d; CRC32 CMP: %08X EXT: %08X", ctr, CRCComputed, CRCExtracted);
}
else if (alg == 5 && akl == 1 && mi != 0) //AES-256 OFB
{
int nblocks = (state->data_byte_ctr[slot] / 16) + 1;
@@ -1256,7 +1367,8 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
}
else if (state->data_header_sap[slot] == 2 || state->data_header_sap[slot] == 3) //TCP and UDP Compression (may only be 3 UDP compression, unknown)
{
uint16_t len = ((blocks+1)*block_len)-4; //total number of bytes in PDU minus 4 CRC32 bytes
uint8_t poc = state->data_block_poc[slot]; //need to test if others also need this
uint16_t len = ((blocks+1)*block_len)-4 - poc; //total number of bytes in PDU minus 4 CRC32 bytes and padding bytes
dmr_udp_comp_pdu (opts, state, len, state->dmr_pdu_sf[slot]);
}
else if (state->data_header_sap[slot] == 1 && state->dmr_pdu_sf[slot][1] == 0x10) //MNIS Proprietary Header
@@ -1594,4 +1706,12 @@ void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
// sprintf (state->call_string[1], "%s", " "); //21 spaces
sprintf (state->dmr_lrrp_gps[0], "%s", "");
sprintf (state->dmr_lrrp_gps[1], "%s", "");
//Hytera OTA Encryption
memset(state->hota_lc_ctx, 0, sizeof(state->hota_lc_ctx));
memset(state->hota_ctx_ok, 0, sizeof(state->hota_ctx_ok));
state->hota_sbrc_idx1[0] = -1;
state->hota_sbrc_idx2[0] = -1;
state->hota_sbrc_idx1[1] = -1;
state->hota_sbrc_idx2[1] = -1;
}

View File

@@ -462,6 +462,12 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if (internalslot == 0 && vc1 == 6)
{
if (state->hota_enc_check && state->forced_alg_id > 1)
{
state->dmr_so = 0x40;
state->payload_algid = state->forced_alg_id;
state->payload_keyid = 0xFF;
}
//this needs to run prior to embedded link control
if (state->payload_algid == 0x02)
hytera_enhanced_alg_refresh(state);
@@ -475,6 +481,12 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if (internalslot == 1 && vc2 == 6)
{
if (state->hota_enc_check && state->forced_alg_id > 1)
{
state->dmr_soR = 0x40;
state->payload_algidR = state->forced_alg_id;
state->payload_keyidR = 0xFF;
}
//this needs to run prior to embedded link control
if (state->payload_algidR == 0x02)
hytera_enhanced_alg_refresh(state);
@@ -509,6 +521,13 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
if (state->vtx256_ekey != 0)
{
vtx256_ambe2_interleave_inversion(ambe_fr);
vtx256_ambe2_interleave_inversion(ambe_fr2);
vtx256_ambe2_interleave_inversion(ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
@@ -702,8 +721,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)
@@ -713,8 +732,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);
}
@@ -968,6 +987,13 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
if (state->vtx256_ekey != 0)
{
vtx256_ambe2_interleave_inversion(ambe_fr);
vtx256_ambe2_interleave_inversion(ambe_fr2);
vtx256_ambe2_interleave_inversion(ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
@@ -1069,10 +1095,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

@@ -940,14 +940,21 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
//MassReg
if (a_type == 4)
{
uint8_t mreg_res = (uint8_t)ConvertBitIntoBytes(&bpbits1[0], 5);
uint8_t reg_window = (uint8_t)ConvertBitIntoBytes(&bpbits1[5], 4); //7.2.19.5.1 Table 7.77 convert raw to seconds
uint8_t aloha_mask = (uint8_t)ConvertBitIntoBytes(&bpbits1[9], 5);
//ADDRNull or MS Individual Address
uint8_t reg_address = (uint8_t)ConvertBitIntoBytes(&bpbits2[16], 8); //see clause 7.2.41
uint32_t reg_address = bparms2; //see clause 7.2.41
fprintf (stderr, "\n"); //raw value only on the reg window, see aforementioned table
fprintf (stderr, " Reg Window: %X; Aloha Mask: %02X; Target: %d; ", reg_window, aloha_mask, reg_address);
dmr_gateway_identifier(0,reg_address);
if (mreg_res != 0)
fprintf (stderr, "Res: %02X", mreg_res);
if (reg_address != 0)
dmr_gateway_identifier(0, reg_address);
}
//debug

View File

@@ -6,7 +6,7 @@
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
*
* LWVMOBILE
* 2023-12 DSD-FME Florida Man Edition
* 2026-09 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
//TODO: Test USBD LIP Decoder with Real World Samples (if/when available)
@@ -51,14 +51,15 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//PDU Bytes and Bits
uint8_t DMR_PDU[25];
uint8_t DMR_PDU_bits[196];
uint8_t DMR_PDU_bits[200];
memset (DMR_PDU, 0, sizeof (DMR_PDU));
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
uint8_t R[3];
uint8_t BPTCReservedBits = 0;
uint8_t is_ras = 0;
uint8_t crc_original_validity = 0;
uint8_t hota_enc = 0;
uint8_t is_ras = 0;
uint8_t bptc_res_bits[3];
memset (bptc_res_bits, 0, sizeof(bptc_res_bits));
uint8_t crc_original_validity = 0;
uint32_t crcmask = 0;
uint8_t crclen = 0;
@@ -76,6 +77,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
switch(databurst){
case 0x00: //PI
hota_enc = 1;
is_bptc = 1;
crclen = 16;
crcmask = 0x6969; //insert pi and 69 jokes here
@@ -83,6 +85,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " PI ");
break;
case 0x01: //VLC
hota_enc = 1;
is_bptc = 1;
is_lc = 1;
crclen = 24;
@@ -91,6 +94,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " VLC ");
break;
case 0x02: //TLC
hota_enc = 1;
is_bptc = 1;
is_lc = 1;
crcmask = 0x999999;
@@ -99,6 +103,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " TLC ");
break;
case 0x03: //CSBK
hota_enc = 1;
is_bptc = 1;
crclen = 16;
crcmask = 0xA5A5;
@@ -106,6 +111,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " CSBK");
break;
case 0x04: //MBC Header
hota_enc = 1;
is_bptc = 1;
crclen = 16;
crcmask = 0xAAAA;
@@ -119,6 +125,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
sprintf(state->fsubtype, " MBCC");
break;
case 0x06: //Data Header
hota_enc = 1;
is_bptc = 1;
crclen = 16;
crcmask = 0xCCCC;
@@ -176,6 +183,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
}
break;
case 0x0B: //Unified Single Block Data USBD
hota_enc = 1;
is_bptc = 1;
crclen = 16;
crcmask = 0x3333;
@@ -185,6 +193,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//special types (not real data 'sync' bursts)
case 0xEB: //Embedded Signalling
hota_enc = 1;
crclen = 5;
is_emb = 1;
pdu_len = 9;
@@ -246,28 +255,41 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
/* Extract the BPTC 196,96 DMR data */
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x04);
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, bptc_res_bits, &is_ras);
#ifdef DEBUG_RAS
//debug print
//fprintf (stderr, " RAS? %X - %d %d %d", BPTCReservedBits, R[0], R[1], R[2]);
if (bptc_res_bits[0] != 0 || bptc_res_bits[1] != 0 || bptc_res_bits[2] != 0)
fprintf (stderr, " BPTC R BITS: %d %d %d", bptc_res_bits[0], bptc_res_bits[1], bptc_res_bits[2]);
#endif
/* Convert the 96 bits BPTC data into 12 bytes */
k = 0;
for(i = 0; i < 12; i++)
//TODO: Checkdown no irrecoverable errors prior to OTA Decryption?
//Hytera OTA Encryption
if (state->hota_key_loaded && hota_enc)
hytera_ota_decrypt_bptc_96(state, BPTCDmrDataBit, databurst);
//Hytera Data Block Level Decryption
if (databurst == 0x7) //Rate 1_2 Data Only
{
BPTCDmrDataByte[i] = 0;
for(j = 0; j < 8; j++)
{
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1;
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01);
k++;
}
uint8_t bytes[12];
memset(bytes, 0, sizeof(bytes));
pack_bit_array_into_byte_array(BPTCDmrDataBit, bytes, 12);
hytera_bp_data_decrypt(state, bytes, 12);
hytera_ep_data_decrypt(opts, state, bytes, 12);
unpack_byte_array_into_bit_array(bytes, BPTCDmrDataBit, 12);
}
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
/* Convert the 96 bits BPTC data into 12 bytes */
pack_bit_array_into_byte_array(BPTCDmrDataBit, BPTCDmrDataByte, 12);
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
if (IrrecoverableErrors == 0 && BPTCDmrDataByte[1] == 0x68) is_ras = 0;
//make sure if this is a PI header, the FID is set to 0x10
if (IrrecoverableErrors == 0 && databurst == 0x00 && BPTCDmrDataByte[1] != 0x10) is_ras = 0;
/* Fill the CRC extracted */
CRCExtracted = 0;
for(i = 0; i < crclen; i++)
{
@@ -279,7 +301,24 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
CRCExtracted = CRCExtracted ^ crcmask;
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
if (is_lc)
{
if (is_ras)
{
CRCComputed = ras_mac_calculator(state, BPTCDmrDataByte, 9, 1);
if (CRCComputed == CRCExtracted)
CRCCorrect = 1;
#ifdef DEBUG_RAS
fprintf (stderr, " EXT: %06X; MAC: %06X;", CRCExtracted, CRCComputed);
#endif
}
else CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
//Hytera OTA Encryption LC Context Creation for VLC Header
if (state->hota_key_loaded && CRCCorrect && databurst == 0x01)
hytera_create_lc_ctx(state, BPTCDmrDataBit);
}
//set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these)
else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
@@ -315,20 +354,23 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//run CCITT on other data forms
else
{
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
if (is_ras == 1)
{
CRCComputed = ras_mac_calculator(state, BPTCDmrDataByte, 10, 0);
#ifdef DEBUG_RAS
fprintf (stderr, " EXT: %04X; MAC: %04X;", CRCExtracted, CRCComputed);
#endif
}
else CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
else CRCCorrect = 0;
}
//set the 'RAS Flag', if no irrecoverable errors but bad crc, only when enabled by user (to prevent a lot of bad data CSBKs)
if (opts->aggressive_framesync == 0 && CRCCorrect == 0 && IrrecoverableErrors == 0 && BPTCReservedBits == 4) is_ras = 1;
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
if (BPTCDmrDataByte[1] == 0x68) is_ras = 0;
//if this is a RAS system, set the CRC to okay if irrecoverable errors are okay
//if we don't do this, then we can't view some data (CSBKs on RAS enabled systems)
if (is_ras == 1)
if (is_ras == 1 && state->ras_effective_key == 0)
{
crc_original_validity = CRCCorrect;
CRCCorrect = 1;
@@ -344,17 +386,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
}
/* Convert corrected x bytes into x*8 bits */
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
{
BPTCDmrDataBit[j + 0] = (BPTCDmrDataByte[i+pdu_start] >> 7) & 0x01;
BPTCDmrDataBit[j + 1] = (BPTCDmrDataByte[i+pdu_start] >> 6) & 0x01;
BPTCDmrDataBit[j + 2] = (BPTCDmrDataByte[i+pdu_start] >> 5) & 0x01;
BPTCDmrDataBit[j + 3] = (BPTCDmrDataByte[i+pdu_start] >> 4) & 0x01;
BPTCDmrDataBit[j + 4] = (BPTCDmrDataByte[i+pdu_start] >> 3) & 0x01;
BPTCDmrDataBit[j + 5] = (BPTCDmrDataByte[i+pdu_start] >> 2) & 0x01;
BPTCDmrDataBit[j + 6] = (BPTCDmrDataByte[i+pdu_start] >> 1) & 0x01;
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
}
unpack_byte_array_into_bit_array(BPTCDmrDataByte, BPTCDmrDataBit, pdu_len);
//convert to DMR_PDU and DMR_PDU_bits
for (i = 0; i < pdu_len; i++)
@@ -398,8 +430,42 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
} /* End for(j = 0; j < 8; j++) */
} /* End for(i = 0; i < 16; i++) */
//Hytera OTA Encryption
// if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0)
// hytera_ota_search_emb_lc(state, BptcDataMatrix); //only run as a debug option
if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 1)
hytera_ota_decrypt_emb_lc(state, BptcDataMatrix);
/* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0)
{
//just fill with all ones, would be garbage from FEC anyways
memset(LC_DataBit, 1, sizeof(LC_DataBit));
IrrecoverableErrors = 1;
//TODO: columns instead of rows to match application of keystream
#ifdef DEBUG_HOTA
fprintf(stderr, "\n BPTC 128: ");
for (int i = 0; i < 8; i++)
fprintf (stderr, "%04X", (uint16_t)convert_bits_into_output(BptcDataMatrix[i], 16));
fprintf (stderr, "\n");
#endif
}
else if (state->hota_enc_check == 1)
{
//just fill with all ones, would be garbage from FEC anyways
memset(LC_DataBit, 1, sizeof(LC_DataBit));
IrrecoverableErrors = 1;
//TODO: columns instead of rows to match application of keystream
#ifdef DEBUG_HOTA
fprintf(stderr, "\n BPTC 128: ");
for (int i = 0; i < 8; i++)
fprintf (stderr, "%04X", (uint16_t)convert_bits_into_output(BptcDataMatrix[i], 16));
fprintf (stderr, "\n");
#endif
}
else IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
/* Reconstitute the 5 bit CRC */
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5);
@@ -407,6 +473,16 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
/* Compute the 5 bit CRC */
CRCComputed = ComputeCrc5Bit(LC_DataBit);
//read ahead first two bytes, look for 1X10 or 2X10 pattern
uint16_t ras_test = (uint16_t)(ConvertBitIntoBytes(&LC_DataBit[0], 16) & 0x30FF);
if ((IrrecoverableErrors == 0) && (ras_test == 0x2010 || ras_test == 0x1010))
{
is_ras = 1;
if (state->ras_effective_key != 0)
CRCComputed = ras_mac_calculator(state, LC_DataBit, 9, 2);
}
else is_ras = 0;
if(CRCExtracted == CRCComputed) CRCCorrect = 1;
else CRCCorrect = 0;
@@ -438,7 +514,11 @@ 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);
//Hytera Data Block Level Decryption
hytera_bp_data_decrypt(state, TrellisReturn, 18);
hytera_ep_data_decrypt(opts, state, TrellisReturn, 18);
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
//they may have been successfully corrected, the CRC will reveal as much
@@ -518,6 +598,10 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
bit_ptr = 100; byte_ptr = 12-pdu_start;
pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12);
//Hytera Data Block Level Decryption
hytera_bp_data_decrypt(state, DMR_PDU, 24);
hytera_ep_data_decrypt(opts, state, DMR_PDU, 24);
//set CRC to correct on unconfirmed 1 rate data blocks (for reporting due to no CRC available on these)
if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
@@ -593,19 +677,8 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
else fprintf (stderr, "Reserved %d ", usbd_st);
}
//set the original CRCCorrect back to its original value if the RAS flag was tripped
if (is_ras == 1) CRCCorrect = crc_original_validity;
//start printing relevant fec/crc/ras messages, don't print on idle or MBC continuation blocks (handled in dmr_block.c)
// if (IrrecoverableErrors == 0 && CRCCorrect == 1 && databurst != 0x09 && databurst != 0x05) fprintf(stderr, "(CRC OK)");
// if (IrrecoverableErrors == 0 && CRCCorrect == 0 && databurst != 0x09 && databurst != 0x05)
// {
// fprintf (stderr, "%s", KYEL);
// fprintf(stderr, " (FEC OK)");
// fprintf (stderr, "%s", KNRM);
// }
//set the original CRCCorrect back to its original value if the RAS flag was tripped (if no key value present)
if (is_ras == 1 && state->ras_effective_key == 0) CRCCorrect = crc_original_validity;
if (IrrecoverableErrors != 0 && databurst != 0x08 && databurst != 0x09) //&& databurst != 0x05
{
@@ -614,16 +687,25 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
fprintf (stderr, "%s", KNRM);
}
//print whether or not the 'RAS Field' bits are set to indicate RAS enabled (to be verified)
if (is_ras == 1)
//print whether or not the 'RAS Field' bit is set to indicate RAS
//detected for RAS enabled burst types and disposition if key available
if (IrrecoverableErrors == 0 && is_ras == 1 && (is_bptc || is_emb) && databurst != 0x05 && databurst != 0x07)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " -RAS ");
//the value of this field seems to always, or usually, be 4, or just R[2] bit is set
if (opts->payload == 1) fprintf (stderr, "%X ", BPTCReservedBits);
fprintf (stderr, " {RAS ");
if (state->ras_effective_key != 0 && CRCComputed != CRCExtracted)
fprintf (stderr, "Fail} ");
else if (state->ras_effective_key != 0 && CRCComputed == CRCExtracted)
fprintf (stderr, "Okay} ");
else if (state->ras_effective_key == 0)
fprintf (stderr, "Skip} ");
fprintf (stderr, "%s", KNRM);
}
//might flicker the terminal on partially enabled systems, may need to find a better placement
if (IrrecoverableErrors == 0 && (is_bptc || is_emb) && databurst != 0x05 && databurst != 0x07 && databurst != 0x09)
state->dmr_is_ras = is_ras;
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
{
fprintf (stderr, "%s", KRED);
@@ -639,9 +721,24 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
fprintf (stderr, " DMR PDU Payload ");
for (i = 0; i < pdu_len; i++)
{
#ifdef DEBUG_RAS
fprintf (stderr, "%02X", DMR_PDU[i]);
#else
fprintf (stderr, "[%02X]", DMR_PDU[i]);
#endif
}
//RAS 5-bit - always same differential
//calculation should be 5-bit portion of RAS Effective Key's Last byte & 0x1F
//5-bit + CRCComputed % 31 = CRCExtracted
#ifdef DEBUG_RAS
if (opts->payload == 1 && is_emb && is_ras)
{
int32_t diff = ((int32_t)CRCExtracted - (int32_t)CRCComputed + 31) % 31;
fprintf (stderr, " CRC CMP: %02X EXT: %02X DIFF: %02X", CRCComputed, CRCExtracted, diff);
}
#endif
//debug print
// if (dbsn) fprintf (stderr, " SN %X", dbsn);
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);

View File

@@ -32,6 +32,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
int is_alias = 0;
int is_gps = 0;
UNUSED(capsite);
int is_ras = 0;
//XPT 'Things'
int is_xpt = 0;
@@ -75,10 +76,66 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
//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;
//disable late entry for DMRA, Flag 3 if encrypted, 0 if not
if (so & 0x40)
opts->dmr_le = 3;
else opts->dmr_le = 0;
}
//Hytera OTA Check Enabled, Set SRC from VLC (TLC and ELC differ)
//NOTE: VLC will always be same for each radio if its a group call
//while this is not the true src, it should be unique
//TGT will also be random between calls in each VLC,
//so assign static value
if (state->hota_enc_check)
{
uint32_t t_target = 8686868;
if (type == 1)
{
if (slot == 0)
{
state->lastsrc = source;
state->lasttg = t_target;
}
else
{
state->lastsrcR = source;
state->lasttgR = t_target;
}
fprintf (stderr, "\n%s", KYEL);
fprintf (stderr, " SLOT %d ", slot+1);
fprintf(stderr, "TGT=%u SRC=%u ", t_target, source);
fprintf (stderr, "Encrypted Pseudo Call Information");
fprintf (stderr, "%s", KNRM);
return;
}
else if (type == 2)
{
fprintf (stderr, "\n%s", KRED);
fprintf (stderr, " SLOT %d", slot+1);
fprintf (stderr, " TLC Encrypted;");
fprintf (stderr, "%s", KNRM);
return;
}
else if (type == 3)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " SLOT %d", slot+1);
fprintf (stderr, " EMB LC Encrypted;");
fprintf (stderr, "%s", KNRM);
return;
}
}
//HOTA CTX was not recovered, so dump that and return;
else if (state->hota_key_loaded && state->hota_ctx_ok[slot] == 0 && type == 3)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " SLOT %d", slot+1);
fprintf (stderr, " EMB LC Missing CTX; Encrypted;");
fprintf (stderr, "%s", KNRM);
return;
}
//read ahead a little to get this for the xpt flag
@@ -117,6 +174,17 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
if (slot == 0) state->dmr_flco = flco;
else state->dmr_flcoR = flco;
//debug output for potential RAS indicators on Moto Systems during EMB (0x2X and 0x1X indicators)
//TODO: Either leave handling of link control as is, or rework entire file to handle all the exceptions
//that have been added to it over the years.
if ( (flco & 0x30) && (fid == 0x10) && (type == 3) )
{
is_ras = 1;
UNUSED(is_ras);
// flco &= 0xCF; //mask off the RAS bits
//NOTE: Mask breaks below check on talker alias on RAS systems WIP: Fixing That
}
//FID 0x10 and FLCO 0x14, 0x15, 0x16 and 0x17 confirmed as Moto EMB Alias
//Can probably assume FID 0x10 FLCO 0x18 is Moto EMB GPS -- to be tested
if ( fid == 0x10 && (flco == 0x14 || flco == 0x15 || flco == 0x16 || flco == 0x17 || flco == 0x18) )
@@ -126,7 +194,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
}
//Embedded Talker Alias Header Only (format and len storage)
if ( (fid == 0 || fid == 0x68) && type == 3 && flco == 0x04)
if ((fid == 0 || fid == 0x68) && type == 3 && flco == 0x04)
{
is_alias = 1;
dmr_talker_alias_lc_header(opts, state, slot, lc_bits);
@@ -140,7 +208,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
}
//Embedded GPS
if ( (fid == 0 || fid == 0x68) && fid == 0 && type == 3 && flco == 0x08)
if ((fid == 0 || fid == 0x68) && type == 3 && flco == 0x08)
{
is_gps = 1;
dmr_embedded_gps(opts, state, lc_bits);
@@ -202,8 +270,29 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
goto END_FLCO;
}
//Tait Talker Alias
if (fid == 0x58 && flco == 0x08 && type == 3)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " SLOT %d", state->currentslot+1);
fprintf (stderr, " Tait Talker Alias: ");
char tta_str[8]; memset(tta_str, 0, sizeof(tta_str));
for (int i = 0; i < 7; i++)
tta_str[i] = (uint8_t)ConvertBitIntoBytes(&lc_bits[16+(i*8)], 8);
fprintf (stderr, "%s", tta_str);
uint32_t source = 0;
if (slot == 0)
source = state->lastsrc;
else source = state->lastsrcR;
if (state->event_history_s[slot].Event_History_Items[0].source_id == source)
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s; ", tta_str);
sprintf (state->generic_talker_alias[slot], "Talker Alias: %s; ", tta_str);
goto END_FLCO;
}
//Unknown Tait Things
if (fid == 0x58)
else if (fid == 0x58)
{
//NOTE: fid 0x58 (tait) had a single flco 0x06 emb observed, but without the other blocks (4,5,7) for an alias
//will need to observe this one, or just remove it from the list, going to isolate tait lc for now
@@ -453,6 +542,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
sprintf (state->generic_talker_alias[0], "%s", "");
// sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s", "BUMBLEBEETUNA");
memset (state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
//Hytera OTA Encryption
memset(state->hota_lc_ctx[0], 0, sizeof(state->hota_lc_ctx[0]));
state->hota_ctx_ok[0] = 0;
state->hota_sbrc_idx1[0] = -1;
state->hota_sbrc_idx2[0] = -1;
}
if (state->currentslot == 1)
{
@@ -473,6 +568,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
sprintf (state->generic_talker_alias[1], "%s", "");
// sprintf (state->event_history_s[1].Event_History_Items[0].alias, "%s", "BUMBLEBEETUNA");
memset (state->dmr_pdu_sf[1], 0, sizeof(state->dmr_pdu_sf[1]));
//Hytera OTA Encryption
memset(state->hota_lc_ctx[1], 0, sizeof(state->hota_lc_ctx[1]));
state->hota_ctx_ok[1] = 0;
state->hota_sbrc_idx1[1] = -1;
state->hota_sbrc_idx2[1] = -1;
}
}
@@ -500,15 +601,15 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
if (opts->payload == 1 && is_xpt == 1 && flco == 0x3) fprintf(stderr, "HASH=%d ", tg_hash);
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
//0x04 and 0x05 on a TLC seem to indicate a Cap + Private Call Terminator (perhaps one for each MS)
//0x07 on a VLC seems to indicate a Cap+ Private Call Header
//0x23 on the Embedded Voice Burst Sync seems to indicate a Cap+ or Cap+ TXI Private Call in progress
//0x20 on the Embedded Voice Burst Sync seems to indicate a Moto (non-specific) Group Call in progress
//its possible that both EMB FID 0x10 FLCO 0x20 and 0x23 are just Moto but non-specific (observed 0x20 on Tier 2)
//0x04 and 0x05 on a TLC seem to indicate a Cap + Private Call Terminator <--need to find this again and re-evaluate
//0x04 on a VLC seems to indicate a Cap+ Group Call Header <--single bit flip from normal group
//0x07 on a VLC seems to indicate a Cap+ Private Call Header <--single bit flip from normal private
//0x20 on the Embedded Voice Burst Sync seems to indicate a Moto w/ FID 0x10 Group Call in progress (RAS)
//0x23 on the Embedded Voice Burst Sync seems to indicate a Moto w/ FID 0x10 Private Call in progress (RAS)
if (fid == 0x68) sprintf (state->call_string[slot], " Hytera ");
else if (flco == 0x4 || flco == 0x5 || flco == 0x7 || flco == 0x23) //Cap+ Things
else if (flco == 0x4 || flco == 0x5 || flco == 0x7) //Cap+ Things (double check 0x05)
{
// sprintf (state->call_string[slot], " Cap+");
sprintf (state->call_string[slot], "%s","");
@@ -528,13 +629,13 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
state->gi[slot] = 1;
}
}
else if (flco == 0x3) //UU_V_Ch_Usr
else if (flco == 0x3 || flco == 0x23) //UU_V_Ch_Usr
{
sprintf (state->call_string[slot], " Private ");
fprintf (stderr, "Private ");
state->gi[slot] = 1;
}
else //Grp_V_Ch_Usr -- still valid on hytera VLC
else //Grp_V_Ch_Usr
{
sprintf (state->call_string[slot], " Group ");
fprintf (stderr, "Group ");
@@ -560,7 +661,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++)
@@ -1191,7 +1292,19 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
}
else if (slco == 0xF)
else if (slco == 0x5)
{
uint32_t unk = (uint32_t)ConvertBitIntoBytes(&slco_bits[4], 24);
fprintf (stderr, " SLCO Caltta: %06X;", unk);
}
else if (slco == 0xE)
{
uint32_t update_tgt = (uint32_t)ConvertBitIntoBytes(&slco_bits[4], 24);
fprintf (stderr, " SLCO Cap Max Activity Update; TGT: %d;", update_tgt);
}
else if (slco == 0xF) //This opcode has also been observed on Cap Max Payload Channels
{
fprintf (stderr, " SLCO Capacity Plus Site: %d - Rest LSN: %d - RS: %02X", capsite, restchannel, cap_reserved);

View File

@@ -8,6 +8,9 @@
#include "dsd.h"
static unsigned long long int next_iv_32[2] = {0,0};
static unsigned long long int next_iv_64[2] = {0,0};
//gather ambe_fr mi fragments for processing
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, uint8_t ambe_fr[4][24], uint8_t ambe_fr2[4][24], uint8_t ambe_fr3[4][24])
{
@@ -86,7 +89,96 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
if (g[0] && g[1] && g[2])
{
if (slot == 0 && state->payload_algid != 0)
if (mi_crc_ok == 1 && state->hota_enc_check == 1)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot %d DMRA EP Potential;", slot+1);
unsigned long long int temp_iv = mi_final;
//Reverse Potential IV for both 32-bit and 128-bit to show the IV
//that may have been carried by the PI header or last superframe
uint8_t iv_a[16]; memset(iv_a, 0, sizeof(iv_a));
uint8_t iv_d[16]; memset(iv_d, 0, sizeof(iv_d));
for (int i = 0; i < 4; i++)
{
iv_a[i] = (mi_final >> (24-(i*8))) & 0xFF;
iv_d[i] = (mi_final >> (24-(i*8))) & 0xFF;
}
fprintf (stderr, "\n ");
reverse_lfsr_32a_to_len(iv_a, 32);
fprintf (stderr, " ");
reverse_lfsr_32d_to_len(iv_d, 32);
fprintf (stderr, "\n");
if (next_iv_64[slot] != 0 && mi_final == next_iv_64[slot])
{
//probably safe to assume AES-256 if its DMRA specific
fprintf (stderr, " DMRA AES 128-bit IV detected;");
state->forced_alg_id = 0x25;
if (slot == 0 && state->payload_mi == 0)
{
fprintf (stderr, "\n");
state->payload_mi = mi_final;
state->dmr_so = 0x40;
LFSR128d(state);
}
else if (slot == 1 && state->payload_miR == 0)
{
fprintf (stderr, "\n");
state->payload_miR = mi_final;
state->dmr_soR = 0x40;
LFSR128d(state);
}
}
if (next_iv_32[slot] != 0 && mi_final == next_iv_32[slot])
{
//only DMRA RC4 uses this tap configuration
fprintf (stderr, " DMRA RC4 32-bit IV detected;");
state->forced_alg_id = 0x21;
if (slot == 0 && state->payload_mi == 0)
{
fprintf (stderr, "\n");
state->payload_mi = mi_final;
state->dmr_so = 0x40;
LFSR(state);
}
else if (slot == 1 && state->payload_miR == 0)
{
fprintf (stderr, "\n");
state->payload_miR = mi_final;
state->dmr_soR = 0x40;
LFSR(state);
}
}
//AES taps to next embedded value
for(int cnt = 0; cnt < 32; cnt++)
{
unsigned long long int bit = ( (temp_iv >> 31) ^ (temp_iv >> 21) ^ (temp_iv >> 1) ^ (temp_iv >> 0) ) & 0x1;
temp_iv = (temp_iv << 1) | bit;
}
next_iv_64[slot] = temp_iv & 0xFFFFFFFF;
//RC4 taps to next embedded value
temp_iv = mi_final;
for(int cnt = 0; cnt < 32; cnt++)
{
unsigned long long int bit = ( (temp_iv >> 31) ^ (temp_iv >> 3) ^ (temp_iv >> 1) ) & 0x1;
temp_iv = (temp_iv << 1) | bit;
}
next_iv_32[slot] = temp_iv & 0xFFFFFFFF;
fprintf (stderr, "\n");
fprintf (stderr, "%s", KNRM);
}
if (slot == 0 && state->payload_algid != 0 && state->payload_algid != 0x26)
{
if (state->payload_mi != mi_final)
{
@@ -116,7 +208,7 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
}
}
if (slot == 1 && state->payload_algidR != 0)
if (slot == 1 && state->payload_algidR != 0 && state->payload_algidR != 0x26)
{
if (state->payload_miR != mi_final)
{
@@ -219,20 +311,37 @@ 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)
if ((state->payload_algid & 0xF0) == 0x30) //any kirisun 0x3X alg
{
uint8_t alg = state->payload_algid;
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, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", alg, 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, " Kirisun ");
if (alg == 0x31)
fprintf (stderr, "Reinforced ");
else if (alg == 0x32)
fprintf (stderr, "Low ");
else if (alg == 0x33)
fprintf (stderr, "Middle ");
else if (alg == 0x34)
fprintf (stderr, "High ");
else if (alg == 0x35)
fprintf (stderr, "Enhanced ");
else if (alg == 0x36)
fprintf (stderr, "Advanced ");
else if (alg == 0x37)
fprintf (stderr, "Universal ");
else if (alg == 0x38)
fprintf (stderr, "Strengthened ");
else if (alg == 0x39)
fprintf (stderr, "Top ");
else if (alg == 0x3A)
fprintf (stderr, "Ultra ");
fprintf (stderr, "Encryption;");
fprintf (stderr, "%s\n", KNRM);
}
@@ -266,20 +375,37 @@ 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)
if ((state->payload_algidR & 0xF0) == 0x30) //any 0x3X alg
{
uint8_t alg = state->payload_algidR;
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, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", alg, 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, " Kirisun ");
if (alg == 0x31)
fprintf (stderr, "Reinforced ");
else if (alg == 0x32)
fprintf (stderr, "Low ");
else if (alg == 0x33)
fprintf (stderr, "Middle ");
else if (alg == 0x34)
fprintf (stderr, "High ");
else if (alg == 0x35)
fprintf (stderr, "Enhanced ");
else if (alg == 0x36)
fprintf (stderr, "Advanced ");
else if (alg == 0x37)
fprintf (stderr, "Universal ");
else if (alg == 0x38)
fprintf (stderr, "Strengthened ");
else if (alg == 0x39)
fprintf (stderr, "Top ");
else if (alg == 0x3A)
fprintf (stderr, "Ultra ");
fprintf (stderr, "Encryption;");
fprintf (stderr, "%s\n", KNRM);
}
@@ -331,6 +457,13 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
// 1 - The embedded signalling carries RC information associated to the other logical channel
for(i = 0; i < 32; i++) sbrc_interleaved[i] = state->dmr_embedded_signalling[slot][5][i + 8];
//decrypt for Hytera OTA Encryption w/ loaded key
if (state->hota_key_loaded && state->hota_sbrc_idx1[state->currentslot] == -1)
hytera_ota_search_sb_rc(state, sbrc_interleaved, power);
if (state->hota_key_loaded)
hytera_ota_decrypt_sb_rc(state, sbrc_interleaved);
//power == 0 should be single burst
if (power == 0) irr_err = BPTC_16x2_Extract_Data(sbrc_interleaved, sbrc_return, 0);
//power == 1 should be reverse channel -- still need to check the interleave inside of BPTC
@@ -338,6 +471,36 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
//bad emb burst, never set a valid power indicator value (probably 9)
if (power > 1) goto SBRC_END;
//Hytera OTA
if (irr_err && state->hota_key_loaded && state->hota_sbrc_idx2[state->currentslot] == -1)
{
state->hota_sbrc_idx1[state->currentslot] = -1;
state->hota_sbrc_idx2[state->currentslot] = -1;
fprintf (stderr, "%s \n", KRED);
fprintf (stderr, " SLOT %d ", slot+1);
fprintf (stderr, "SB_RC Missing IDX; Encrypted;");
fprintf (stderr, "%s\n", KNRM);
goto SBRC_END;
}
else if (irr_err && state->hota_key_loaded && state->hota_sbrc_idx2[state->currentslot] != -1)
{
state->hota_sbrc_idx1[state->currentslot] = -1;
state->hota_sbrc_idx2[state->currentslot] = -1;
fprintf (stderr, "%s \n", KRED);
fprintf (stderr, " SLOT %d ", slot+1);
fprintf (stderr, "SB_RC Incorrect IDX or FEC Errors; Encrypted;");
fprintf (stderr, "%s\n", KNRM);
goto SBRC_END;
}
else if (state->hota_enc_check)
{
fprintf (stderr, "%s \n", KRED);
fprintf (stderr, " SLOT %d ", slot+1);
fprintf (stderr, "SB_RC Encrypted;");
fprintf (stderr, "%s\n", KNRM);
goto SBRC_END;
}
//arrange the return for proper order for the crc3 check
for (i = 0; i < 8; i++) sbrc_retcrc[i] = sbrc_return[i+3];
@@ -407,7 +570,7 @@ 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
//Kirisun
if (opts->dmr_le == 3)
{
if (irr_err != 0)
@@ -422,23 +585,38 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
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?
else if (irr_err == 0 && sbrc_hex != 0) //older firmware had sbrc_hex 0x5F1, and 0x5E1 instead
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot %d", state->currentslot+1);
fprintf (stderr, " DMR LE SB Kirisun Encryption Identifier;");
fprintf (stderr, " DMR LE SB Kirisun");
//Newer Samples and/or updated firmware seem to do this with actual key values
uint8_t alg = sbrc_hex >> 7;
if (alg != 0xB)
{
fprintf (stderr, " ALG: %02X;", alg+0x30);
uint8_t kid = (sbrc_hex >> 3) & 0xF;
fprintf (stderr, " KID: %02X;", kid);
uint8_t unk = sbrc_hex & 0x7;
fprintf (stderr, " LOW3: %X;", unk);
if (state->currentslot == 0)
{
if (state->payload_algid == 0 && state->dmr_so & 0x40)
state->payload_algid = alg+0x30;
}
else
{
if (state->payload_algidR == 0 && state->dmr_soR & 0x40)
state->payload_algidR = alg+0x30;
}
}
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
}
}
}
@@ -528,6 +706,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
if (alg == 2) fprintf (stderr, " DES;");
if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;");
if (alg == 6) fprintf (stderr, " Caltta BP");
fprintf (stderr, "%s ", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
if (state->payload_keyid != key)
@@ -550,6 +729,11 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR LE SB ALG ID: %02X; KEY ID: %02X;", alg + 0x20, key);
if (alg == 1) fprintf (stderr, " RC4;");
if (alg == 2) fprintf (stderr, " DES;");
if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;");
if (alg == 6) fprintf (stderr, " Caltta BP");
fprintf (stderr, "%s ", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
if (state->payload_keyidR != key)

View File

@@ -167,16 +167,6 @@ 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];
@@ -270,6 +260,13 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
if (state->vtx256_ekey != 0)
{
vtx256_ambe2_interleave_inversion(ambe_fr);
vtx256_ambe2_interleave_inversion(ambe_fr2);
vtx256_ambe2_interleave_inversion(ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
@@ -588,6 +585,13 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
if (state->vtx256_ekey != 0)
{
vtx256_ambe2_interleave_inversion(ambe_fr);
vtx256_ambe2_interleave_inversion(ambe_fr2);
vtx256_ambe2_interleave_inversion(ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);

View File

@@ -267,8 +267,13 @@ void dmr_udp_comp_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t
slot = 1;
char comp_string[500]; memset (comp_string, 0, sizeof(comp_string));
sprintf (comp_string, "IPC: %d; OP: %d; SRC: %d:%d (%s):(%s); DST: %d:%d (%s):(%s); ", ipid, opcode, said, spid, addrstring[0], portstring[0], daid, dpid, addrstring[1], portstring[1]);
watchdog_event_datacall (opts, state, said, daid, comp_string, slot);
//use data header source and target instead of idx values
uint32_t source = state->dmr_lrrp_source[slot];
uint32_t target = state->dmr_lrrp_target[slot];
sprintf (comp_string, "IPC Call; SRC: %d; TGT: %d; %s", source, target, portstring);
// sprintf (comp_string, "IPC: %d; OP: %d; SRC: %d:%d (%s):(%s); DST: %d:%d (%s):(%s); ", ipid, opcode, said, spid, addrstring[0], portstring[0], daid, dpid, addrstring[1], portstring[1]);
watchdog_event_datacall (opts, state, source, target, comp_string, slot);
}
@@ -486,29 +491,24 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
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)
int tmslen = input[28];
//+29, get 8 bytes for unknown "header" information
//unknown elements 0002000000000000, could include type (utf-16, utf-8, is07?)
//the sample has a len of 14 utf-16 chars, corresponding to the found 0x0E value
if (opts->payload == 1)
{
// fprintf (stderr, "Len: %03d; ", len);
fprintf (stderr, "Len: %03d; ", tmslen);
fprintf (stderr, "??: ");
for (int i = 0; i < 9; i++)
fprintf (stderr, "%02X", input[i+28]);
for (int i = 0; i < 8; i++)
fprintf (stderr, "%02X", input[i+29]);
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);
utf16_to_text(state, 1, tmslen*2, input+37);
}
else if (port1 == 5017 && port2 == 5017)
{
@@ -521,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; ",
@@ -995,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

@@ -27,51 +27,68 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
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
//NOTE: Some Kirisun Radios seem to use byte 2 as the SVC_OPT, others seem to use it like a KID value
// 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
//NOTE: Original samples had this as 0x40 no matter which "key" was selected, new samples from different radio show a KID value
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 on older FW
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)
//Only set real KID if the byte value isn't 0x40
if (kid == 0x40)
kid = hash;
//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->dmr_so = 0x40;
state->payload_algid = alg;
state->payload_keyid = kid;
state->payload_mi = mi;
}
else
{
state->dmr_soR = so;
state->dmr_soR = 0x40;
state->payload_algidR = alg;
state->payload_keyidR = hash;
state->payload_keyidR = kid;
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, " DMR PI H- ALG ID: %02X; TGT: %d; KEY ID: %02X; MI(32): %08X;", alg, target, kid, mi);
fprintf (stderr, " Kirisun ");
if (alg == 0x36)
fprintf (stderr, "Advanced;");
if (alg == 0x31)
fprintf (stderr, "Reinforced ");
else if (alg == 0x32)
fprintf (stderr, "Low ");
else if (alg == 0x33)
fprintf (stderr, "Middle ");
else if (alg == 0x34)
fprintf (stderr, "High ");
else if (alg == 0x35)
fprintf (stderr, "Enhanced ");
else if (alg == 0x36)
fprintf (stderr, "Advanced ");
else if (alg == 0x37)
fprintf (stderr, "Universal;");
else fprintf (stderr, "Encryption;");
fprintf (stderr, "Universal ");
else if (alg == 0x38)
fprintf (stderr, "Strengthened ");
else if (alg == 0x39)
fprintf (stderr, "Top ");
else if (alg == 0x3A)
fprintf (stderr, "Ultra ");
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.
//flag late entry SB for Kirisun
opts->dmr_le = 3;
}
@@ -146,7 +163,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->payload_algid = PI_BYTE[0];
state->payload_keyid = PI_BYTE[2];
state->payload_mi = ((unsigned long long int)PI_BYTE[3] << 24ULL) | ((unsigned long long int)PI_BYTE[4] << 16ULL) | ((unsigned long long int)PI_BYTE[5] << 8ULL) | ((unsigned long long int)PI_BYTE[6] << 0ULL);
if (state->payload_algid < 0x26)
if (state->payload_algid < 0x28)
{
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot 1");
@@ -182,6 +199,13 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->payload_algid = 0x25;
}
else if ((state->payload_algid & 0x07) == 0x06)
{
fprintf (stderr, " Caltta BP;");
state->payload_algid = 0x26;
state->payload_mi = 0;
}
fprintf (stderr, "%s ", KNRM);
//expand the 32-bit MI to a 64-bit DES IV
@@ -199,7 +223,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
}
}
if (state->payload_algid >= 0x26)
if (state->payload_algid >= 0x28)
{
state->payload_algid = 0;
state->payload_keyid = 0;
@@ -249,6 +273,12 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->payload_algidR = 0x25;
}
else if ((state->payload_algidR & 0x07) == 0x06)
{
fprintf (stderr, " Caltta BP;");
state->payload_algidR = 0x26;
state->payload_miR = 0;
}
fprintf (stderr, "%s ", KNRM);
@@ -267,7 +297,7 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
}
}
if (state->payload_algidR >= 0x26)
if (state->payload_algidR >= 0x28)
{
state->payload_algidR = 0;
state->payload_keyidR = 0;
@@ -374,6 +404,33 @@ void LFSR64(dsd_state * state)
}
}
//if supplied IV is only 32-bit in value, then expand it to a 128-bit IV using these LFSR taps (packed array)
void lfsr_32_to_128(uint8_t * iv)
{
uint64_t lfsr = 0, bit = 0;
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
uint8_t cnt = 0, x = 32;
for(cnt = 0;cnt < 96; cnt++)
{
//32,22,2,1 (per Xilinx XAPP 052) Table 3: Taps for Maximum-Length LFSR Counters
bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
lfsr = (lfsr << 1) | bit;
//continue packing iv
iv[x/8] = (iv[x/8] << 1) + bit;
x++;
}
fprintf (stderr, "\n IV(128): ");
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", iv[x]);
fprintf (stderr, "\n");
}
//Expand a 32-bit MI into a 128-bit IV for AES
void LFSR128d(dsd_state * state)

View File

@@ -438,3 +438,43 @@ uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData)
/* Return the CRC */
return CRC;
} /* End ComputeCrc32Bit() */
uint32_t ComputeCrc32BitV(uint8_t * DMRData, uint32_t NbData)
{
uint32_t i;
uint32_t CRC = 0x00000000; /* Initialization value = 0x00000000 */
/* Polynomial x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
* Normal = 0x04C11DB7
* Reversed = 0xEDB88320
* Reciprocal = 0xDB710641
* Reversed reciprocal = 0x82608EDB */
uint32_t Polynome = 0x04C11DB7;
for(i = 0; i < NbData; i++)
{
if(((CRC >> 31) & 1) ^ (DMRData[i] & 1))
{
CRC = (CRC << 1) ^ Polynome;
}
else
{
CRC <<= 1;
}
}
//Alternate Return Configuration for Vertex
//Encrypted PDUs that have already been reordered once before
//CRC32 CMP: 1622377A EXT: 377A1622
uint32_t a, b, c, d;
a = b = c = d = 0;
a = ((CRC >> 0) & 0xFF) << 16;
b = ((CRC >> 8) & 0xFF) << 24;
c = ((CRC >> 16) & 0xFF) << 0;
d = ((CRC >> 24) & 0xFF) << 8;
CRC = a + b + c + d;
/* Return the CRC */
return CRC;
} /* End ComputeCrc32BitV() */

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

View File

@@ -189,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;
@@ -207,6 +215,7 @@ void openOSSOutput (dsd_opts * opts)
exit(1);
}
fmt = 0;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
{
@@ -303,6 +312,7 @@ void openOSSOutput (dsd_opts * opts)
}
}
}
#endif
void
processAudio (dsd_opts * opts, dsd_state * state)

View File

@@ -92,6 +92,10 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
{
encL = 0;
}
else if (state->vtx_key_loaded == 1)
{
encL = 0;
}
}
else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22)
{
@@ -100,7 +104,7 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encL = 0;
}
}
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
@@ -108,6 +112,21 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encL = 0;
}
}
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
else if (state->payload_algid == 0x26)
{
if (state->straight_ks == 0x26)
{
encL = 0;
}
}
}
if (encR)
@@ -118,6 +137,10 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
{
encR = 0;
}
else if (state->vtx_key_loaded == 1)
{
encR = 0;
}
}
else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22)
{
@@ -126,7 +149,7 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encR = 0;
}
}
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
@@ -134,6 +157,21 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encR = 0;
}
}
else if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
}
}
else if (state->payload_algidR == 0x26)
{
if (state->straight_ks == 0x26)
{
encR = 0;
}
}
}
//debug disable enc check for AES testing
@@ -716,13 +754,28 @@ 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 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3)
{
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
else if (state->payload_algidR == 0x26)
{
if (state->straight_ks == 0x26)
{
encL = 0;
}
}
}
//WIP: Mute if on B list (or not W list)
@@ -1123,6 +1176,10 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
{
encL = 0;
}
else if (state->vtx_key_loaded == 1)
{
encL = 0;
}
}
else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22)
{
@@ -1131,7 +1188,7 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encL = 0;
}
}
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
@@ -1139,6 +1196,21 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encL = 0;
}
}
else if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
else if (state->payload_algid == 0x26)
{
if (state->straight_ks == 0x26)
{
encL = 0;
}
}
}
if (encR)
@@ -1149,6 +1221,10 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
{
encR = 0;
}
else if (state->vtx_key_loaded == 1)
{
encR = 0;
}
}
else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22)
{
@@ -1157,7 +1233,7 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encR = 0;
}
}
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
@@ -1165,6 +1241,21 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encR = 0;
}
}
else if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
}
}
else if (state->payload_algidR == 0x26)
{
if (state->straight_ks == 0x26)
{
encR = 0;
}
}
}
//debug for AES
@@ -2194,6 +2285,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

@@ -16,6 +16,7 @@
*/
#include <assert.h>
#include <limits.h>
#include "dsd.h"
@@ -121,22 +122,46 @@ use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
UNUSED(symbol);
int i;
int sbuf2[128];
int lmin, lmax, lsum;
int lmn[2] = {INT_MAX, INT_MAX};
int lmx[2] = {INT_MIN, INT_MIN};
// single-pass tracking of the 2 smallest and 2 largest values in the symbol window
for (i = 0; i < opts->ssize; i++)
{
sbuf2[i] = state->sbuf[i];
int x = state->sbuf[i];
if (x < lmn[1])
{
if (x < lmn[0])
{
lmn[1] = lmn[0];
lmn[0] = x;
}
else
{
lmn[1] = x;
}
}
if (x > lmx[1])
{
if (x > lmx[0])
{
lmx[1] = lmx[0];
lmx[0] = x;
}
else
{
lmx[1] = x;
}
}
}
qsort (sbuf2, opts->ssize, sizeof (int), comp);
// continuous update of min/max in rf_mod=1 (QPSK) mode
// in c4fm min/max must only be updated during sync
if (state->rf_mod == 1)
{
lmin = (sbuf2[0] + sbuf2[1]) / 2;
lmax = (sbuf2[(opts->ssize - 1)] + sbuf2[(opts->ssize - 2)]) / 2;
lmin = (lmn[0] + lmn[1]) / 2;
lmax = (lmx[1] + lmx[0]) / 2;
state->minbuf[state->midx] = lmin;
state->maxbuf[state->midx] = lmax;
if (state->midx == (opts->msize - 1))
@@ -178,7 +203,7 @@ use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
if (opts->datascope == 1)
{
print_datascope(opts, state, sbuf2);
print_datascope(opts, state, state->sbuf);
}
}
else

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);
}

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)
{
@@ -841,6 +877,76 @@ void unpack_ambe (uint8_t * input, char * ambe)
ambe[48] = input[6] >> 7;
}
uint64_t reverse_lfsr_32a_to_len(uint8_t * iv, int16_t len)
{
uint64_t lfsr = 0, bit1 = 0, bit2 = 0;
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
memset (iv, 0, 8*sizeof(uint8_t));
for(int16_t cnt = 0; cnt < len; cnt++)
{
//32,22,2,1 (per Xilinx XAPP 052) Table 3: Taps for Maximum-Length LFSR Counters
//taps at the +1 position on all but MSB, then check the LSB and configure bit as required
bit1 = ( (lfsr >> 22) ^ (lfsr >> 2) ^ (lfsr >> 1) ) & 0x1;
bit2 = lfsr & 1;
if (bit1 == bit2)
bit2 = 0;
else bit2 = 1;
//just run this in reverse of normal LFSR
lfsr = (lfsr >> 1) | (bit2 << 31);
}
for (int16_t i = 0; i < 4; i++)
iv[i] = (lfsr >> (24-(i*8))) & 0xFF;
fprintf (stderr, "REV LFSR AES(%02d): ", len);
for (int16_t i = 0; i < 4; i++)
fprintf (stderr, "%02X", iv[i]);
return bit2;
}
uint64_t reverse_lfsr_32d_to_len(uint8_t * iv, int16_t len)
{
uint64_t lfsr = 0, bit1 = 0, bit2 = 0;
lfsr = ((uint64_t)iv[0] << 24ULL) + ((uint64_t)iv[1] << 16ULL) + ((uint64_t)iv[2] << 8ULL) + ((uint64_t)iv[3] << 0ULL);
memset (iv, 0, 8*sizeof(uint8_t));
for(int16_t cnt = 0; cnt < len; cnt++)
{
//magical taps that somebody must have dreamt up
//taps at the +1 position on all but MSB, then check the LSB and configure bit as required
bit1 = ((lfsr >> 4) ^ (lfsr >> 2)) & 0x1;
bit2 = lfsr & 1;
if (bit1 == bit2)
bit2 = 0;
else bit2 = 1;
//just run this in reverse of normal LFSR
lfsr = (lfsr >> 1) | (bit2 << 31);
}
for (int16_t i = 0; i < 4; i++)
iv[i] = (lfsr >> (24-(i*8))) & 0xFF;
fprintf (stderr, "REV LFSR RC4(%02d): ", len);
for (int16_t i = 0; i < 4; i++)
fprintf (stderr, "%02X", iv[i]);
return bit2;
}
//recover previous IV for SDRTrunk .mbe files when P25p1
uint64_t reverse_lfsr_64_to_len(dsd_opts * opts, uint8_t * iv, int16_t len)
{
@@ -928,9 +1034,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 +1085,28 @@ 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 = (uint8_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;
}
char ambe_d[49]; memset(ambe_d, 0, sizeof(ambe_d));
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
state->errs2 = state->errs;
@@ -1052,7 +1183,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 +1329,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 +1350,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 +1439,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 +1461,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 +1545,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 +1576,127 @@ 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;
}
else if ((alg_id == 0x24 || alg_id == 0x25) && state->payload_mi != 0)
{
uint8_t ks_bytes[375];
memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t aes_key[32];
memset(aes_key, 0, sizeof(aes_key));
uint8_t empty[32];
memset(empty, 0, sizeof(empty));
//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;
}
//check to see if a key is actually loaded
if (memcmp (aes_key, empty, sizeof(aes_key)) != 0)
{
if (alg_id == 0x24) //128, or 256
aes_ofb_keystream_output(state->aes_iv, aes_key, ks_bytes, 0, 16); //16*16=256
else aes_ofb_keystream_output(state->aes_iv, aes_key, ks_bytes, 2, 16); //16*16=256
unpack_byte_array_into_bit_array(ks_bytes+16, ks, 256-16); //unpack starting after discards
ks_available = 1;
}
}
//Forced Application of DMRA DES Mode (untested on .mbe real samples)
if (alg_id == 0x22 && state->R != 0 && state->payload_mi != 0)
{
//expand IV from 32 to 64 (late entry iv should fix this)
// if (alg_id == 0x22)
// LFSR64(state);
uint8_t ks_bytes[375];
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+8, ks, 256-8);
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 +1753,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);
@@ -1430,7 +1813,11 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
//debug set value
if (opts->payload == 1)
fprintf (stderr, "\n IV: %016llX;", iv_hex); //not really needed if loaded into array
{
if (iv_hex > 0xFFFFFFFF)
fprintf (stderr, "\n IV: %016llX;", iv_hex);
else fprintf (stderr, "\n IV: %08llX;", iv_hex);
}
//debug print current str_buffer
// fprintf (stderr, "\n Encryption MI/IV: %s", str_buffer);
@@ -1445,9 +1832,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 +1867,163 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
ks_available = 1;
}
} //end test
//P25 and DMR DES (v1 tested and working on P1, v2 should be okay now), DMR Untested but should be okay
else if ((alg_id == 0x22 || 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.
//expand IV from 32 to 64
if (alg_id == 0x22)
LFSR64(state);
//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)
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32); //32*8=256
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 and DMR AES (v1 and v2 tested working on P1), working on DMR
else if ( (alg_id == 0x24 || alg_id == 0x25 || 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
if (alg_id == 0x24 || alg_id == 0x25)
lfsr_32_to_128(aes_iv);
else lfsr_64_to_128(aes_iv);
if (alg_id == 0x89 || alg_id == 0x24) //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 +2047,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 +2057,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 +2081,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 +2090,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 +2104,56 @@ 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)
{
//run LFSR evaluate stored codewords for matching IV or replace if not match or not set
if (state->forced_alg_id == 0x21 && state->payload_mi != 0)
{
fprintf (stderr, "\n");
LFSR(state);
}
fprintf (stderr, "\n");
dmr_late_entry_mi (opts, state);
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)
{

View File

@@ -16,6 +16,7 @@
*/
#include "dsd.h"
#include <limits.h>
#include <locale.h>
void
@@ -164,7 +165,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
char synctest8[9]; //M17
char modulation[8];
char *synctest_p;
char synctest_buf[10240]; //what actually is assigned to this, can't find its use anywhere?
char synctest_buf[10240 + 48]; //history for sync matching; look-back window up to 48 bytes
int lmin, lmax, lidx;
//assign t_max value based on decoding type expected (all non-auto decodes first)
@@ -197,11 +198,10 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
else t_max = 24; //24 for everything else
int lbuf[48], lbuf2[48]; //if we use t_max in these arrays, and t >= t_max in condition below, then it can overflow those checks in there if t exceeds t_max
int lbuf[48]; //if we use t_max in these arrays, and t >= t_max in condition below, then it can overflow those checks in there if t exceeds t_max
int lsum;
//init the lbuf
memset (lbuf, 0, sizeof(lbuf));
memset (lbuf2, 0, sizeof(lbuf2));
// detect frame sync
t = 0;
@@ -214,7 +214,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
synctest20[20] = 0;
modulation[7] = 0; //not initialized or terminated (unsure if this would be an issue or not)
synctest_pos = 0;
synctest_p = synctest_buf + 10;
synctest_p = synctest_buf + 48;
sync = 0;
lmin = 0;
lmax = 0;
@@ -374,13 +374,38 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
*synctest_p = dibit;
if (t >= t_max) //works excelent now with short sync patterns, and no issues with large ones!
{
int lmn[5] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX};
int lmx[5] = {INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN};
int j, x;
for (i = 0; i < t_max; i++) //24
{
lbuf2[i] = lbuf[i];
x = lbuf[i];
if (x < lmn[4])
{
for (j = 4; j > 0 && x < lmn[j - 1]; j--)
lmn[j] = lmn[j - 1];
lmn[j] = x;
}
if (x > lmx[4])
{
for (j = 4; j > 0 && x > lmx[j - 1]; j--)
lmx[j] = lmx[j - 1];
lmx[j] = x;
}
}
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 = (lmn[0] + lmn[1] + lmn[2]) / 3;
lmax = (lmx[2] + lmx[1] + lmx[0]) / 3;
}
//on longer sync patterns, we can get more towards the middle and eliminate outlier values
else
{
lmin = (lmn[2] + lmn[3] + lmn[4]) / 3;
lmax = (lmx[4] + lmx[3] + lmx[2]) / 3;
}
if (state->rf_mod == 1)
{
@@ -1140,8 +1165,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
@@ -1155,11 +1180,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);
}
@@ -1177,9 +1205,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);
@@ -1187,7 +1278,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
@@ -1295,7 +1388,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
{
// buffer reset
synctest_pos = 0;
synctest_p = synctest_buf;
synctest_p = synctest_buf + 48;
noCarrier (opts, state);
}

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
@@ -646,7 +528,7 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
if (slot == 1) src = state->lasttgR;
//save to event history string
if (state->event_history_s[slot].Event_History_Items[0].source_id == src)
// if (state->event_history_s[slot].Event_History_Items[0].source_id == src)
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "%s", state->dmr_embedded_gps[slot]);
//save to LRRP report for mapping/logging
@@ -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

@@ -83,15 +83,6 @@
};
#endif
int comp (const void *a, const void *b)
{
if (*((const int *) a) == *((const int *) b))
return 0;
else if (*((const int *) a) < *((const int *) b))
return -1;
else
return 1;
}
//struct for checking existence of directory to write to
struct stat st = {0};
@@ -234,7 +225,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 +408,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
@@ -437,6 +428,11 @@
state->K2 = 0;
state->K3 = 0;
state->K4 = 0;
//vertex standard keys
// memset(state->vtx256_key, 0, sizeof(state->vtx256_key));
state->vtx40_key = 0;
state->vtx256_ekey = 0;
state->vtx_key_loaded = 0;
memset (state->A1, 0, sizeof(state->A1));
memset (state->A2, 0, sizeof(state->A2));
memset (state->A3, 0, sizeof(state->A3));
@@ -589,6 +585,14 @@
//we do reset the counter, but not the static_ks_bits
memset (state->static_ks_counter, 0, sizeof(state->static_ks_counter));
//Hytera OTA Encryption
memset(state->hota_lc_ctx, 0, sizeof(state->hota_lc_ctx));
memset(state->hota_ctx_ok, 0, sizeof(state->hota_ctx_ok));
state->hota_sbrc_idx1[0] = -1;
state->hota_sbrc_idx2[0] = -1;
state->hota_sbrc_idx1[1] = -1;
state->hota_sbrc_idx2[1] = -1;
} //nocarrier
@@ -636,6 +640,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;
@@ -678,6 +683,7 @@
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_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
@@ -688,6 +694,7 @@
//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_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)
@@ -696,6 +703,9 @@
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_rms = 0; //root means square power level on rtl input signal
opts->rtltcp_enabled = 0; //1 when the rtl input is an rtl_tcp server instead of a local dongle
sprintf (opts->rtltcp_hostname, "%s", "localhost");
opts->rtltcp_portno = 1234; //rtl_tcp default port
//end RTL user options
opts->pulse_raw_rate_in = 48000;
opts->pulse_raw_rate_out = 48000;//
@@ -1065,6 +1075,7 @@
state->any_bp = 0;
state->straight_ks = 0;
state->straight_mod = 0;
state->straight_discard = 0;
//ks array storage and counters
memset (state->ks_octetL, 0, sizeof(state->ks_octetL));
@@ -1090,6 +1101,12 @@
//xl specific, we need to know if the ESS is from HDU, or from LDU2
state->xl_is_hdu = 0;
//vertex standard keys
memset(state->vtx256_key, 0, sizeof(state->vtx256_key));
state->vtx40_key = 0;
state->vtx256_ekey = 0;
state->vtx_key_loaded = 0;
//NXDN, when a new IV has arrived
state->nxdn_new_iv = 0;
@@ -1185,7 +1202,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);
@@ -1201,7 +1220,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
@@ -1229,6 +1248,26 @@
//Remus DMR End Call Alert Beep
state->dmr_end_alert[0] = 0;
state->dmr_end_alert[1] = 0;
//RAS
memset (state->ras_permutation_order, 0, sizeof(state->ras_permutation_order));
state->ras_effective_key = 0;
state->dmr_is_ras = 0;
//Hytera OTA Encryption
state->hota_enc_check = 0;
state->hota_key_loaded = 0;
state->dmr_is_hota = 0;
state->hota_bit_shift = 0;
memset(state->hota_byte_shifts, 0, sizeof(state->hota_byte_shifts));
memset(state->hota_rc4_key, 0, sizeof(state->hota_rc4_key));
memset(state->hota_rc4_ks, 0, sizeof(state->hota_rc4_ks));
memset(state->hota_lc_ctx, 0, sizeof(state->hota_lc_ctx));
memset(state->hota_ctx_ok, 0, sizeof(state->hota_ctx_ok));
state->hota_sbrc_idx1[0] = -1;
state->hota_sbrc_idx2[0] = -1;
state->hota_sbrc_idx1[1] = -1;
state->hota_sbrc_idx2[1] = -1;
sprintf (state->dmr_branding, "%s", "");
sprintf (state->dmr_branding_sub, "%s", "");
@@ -1393,6 +1432,7 @@
printf (" pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O) \n");
printf (" rtl for rtl dongle (Default Values -- see below)\n");
printf (" rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)\n");
printf (" rtltcp:host:port:freq:gain:ppm:bw:sq:vol for rtl dongle on an rtl_tcp server (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 (" filename.bin for OP25/FME capture bin files\n");
@@ -1432,6 +1472,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");
@@ -1445,7 +1487,7 @@
printf (" If using /dev/dsp input and output at 48k1, launch two instances of DSD-FME w -V 1 and -V 2 if needed\n");
#endif
printf (" -z Set TDMA Voice Slot Preference when using /dev/dsp audio output (prevent lag and stuttering)\n");
printf (" -y Enable Experimental Pulse Audio Float Audio Output\n");
// printf (" -y Enable Experimental Pulse Audio Float Audio Output\n");
printf (" -v <hex> Set Filtering Bitmap Options (Advanced Option)\n");
printf (" 1 1 1 1 (0xF): PBF/LPF/HPF/HPFD on\n");
printf ("\n");
@@ -1462,6 +1504,11 @@
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 (" Usage: rtltcp:host:port:freq:gain:ppm:bw:sq:vol\n");
printf (" host <name> rtl_tcp Server Hostname or IP Address (default = localhost)\n");
printf (" port <num> rtl_tcp Server Port (default = 1234)\n");
printf (" freq, gain, ppm, bw, sq, vol as above\n");
printf (" Example: dsd-fme -fs -i rtltcp:192.168.1.10:1234:450M:26:-2:8 -C connect_plus_chan.csv -T\n");
printf ("\n");
printf ("Decoder options:\n");
printf (" -fa Auto Detection\n");
@@ -1485,10 +1532,15 @@
printf (" -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n");
printf (" -fm Decode only dPMR*\n");
printf (" -l Disable DMR, dPMR, NXDN, M17 input filtering\n");
printf (" -u <num> Unvoiced speech quality (default=3)\n");
// printf (" -u <num> Unvoiced speech quality (default=3)\n");
printf (" -xx Expect non-inverted X2-TDMA signal\n");
printf (" -xr Expect inverted DMR signal\n");
printf (" -xd Expect inverted ICOM dPMR 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");
@@ -1522,11 +1574,20 @@
printf (" -H '736B9A9C5645288B 243AD5CB8701EF8A' \n");
printf (" -H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8' \n");
printf ("\n");
printf (" -H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)\n");
printf (" -H <hex> Manually Enter AES-128, AES-256, or Kirisun 128 or 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");
printf ("\n");
printf (" -H <hex> Manually Enter Caltta 10/32/64 Char Basic Privacy Hex Key (see example below)\n");
printf (" -H C:40:1234567890 \n");
printf (" -H C:128:736B9A9C5645288B243AD5CB8701EF8A' \n");
printf (" -H C:256:20029736A5D91042C923EB0697484433005EFC58A1905195E28E9C7836AA2DB8' \n");
printf ("\n");
printf (" -H <hex> Manually Enter Hytera OTA Encryption 32 Char Hex Key (see example below)\n");
printf (" -H O:0:0 no keystream application, but check for Hytera OTA Encryption and DMRA EP Late Entry IV.\n");
printf (" -H O:128:17bfe59b29a06136fb7d3811fd1a4904 for 128-bit RC4 OTA Key Setup\n");
printf ("\n");
printf (" -R <dec> Manually Enter dPMR or NXDN EHR Scrambler Key Value (Decimal Value)\n");
printf (" \n");
printf (" -1 <hex> Manually Enter RC4 or DES Key Value (DMR, P25, NXDN) (Hex Value) \n");
@@ -1559,6 +1620,23 @@
printf (" \n");
printf (" -A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value) \n");
printf (" \n");
printf (" -$ <string> Enter RAS Password 6-24 Char Key Value (DMR) (a-z A-Z, 0-9, $ # _ -) \n");
printf (" -$ Password123\n");
printf (" \n");
printf (" Special Use Cases involving # and $, use parenthesis and put \\ in front of all $ signs\n");
printf (" -$ \"######\"\n");
printf (" -$ \"\\$\\$\\$\\$\\$\\$\"\n");
printf (" \n");
printf (" -y <hex> Enter RAS 56-bit Effective Key Value (DMR) (14 Char Hex Value) \n");
printf (" -y 6DF241BC4AE2EC\n");
printf (" \n");
printf (" -, <string> Manually Enter and Enforce Auctus A6 (GoComm) 8 Char Key Value (DMR) (String Value) \n");
printf (" -, Secure04\n");
printf (" \n");
printf (" -u <string> Manually Enter Vertex Standard 10 or 64 Char Hex Key \n");
printf (" -u 10:1234567891 or 64:AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD\n");
printf (" -u 0:0 no KS application, but apply the inversion to the interleave (clear errors on 256)\n");
printf (" \n");
printf (" -S <str> Manually Enter and Enforce Generic Static Keystream -> Length and BYTE PACKED / ALIGNED String for AMBE (up to 882 bits)\n");
printf (" For Example, enter 16-bit Keystream 0909 as:\n");
printf (" -S 16:0909\n");
@@ -1568,6 +1646,8 @@
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 (" For Example, Caltta 40-bit Keystream w/ optional 7-bit discard as:\n");
printf (" -S 40:1234567890:7\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");
@@ -1598,7 +1678,7 @@
printf (" -p Disable Tune to Private Calls (DMR TIII, P25, NXDN Type-C and Type-D)\n");
printf (" -E Disable Tune to Group Calls (DMR TIII, Con+, Cap+, P25, NXDN Type-C, and Type-D)\n");
printf (" -e Enable Tune to Data Calls (DMR TIII, Cap+, NXDN Type-C)\n");
printf (" (NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls \n");
printf (" (NOTE: No Clear Distinction between Cap+ Private Voice Calls and Data Calls -- Both enabled with Data Calls) \n");
printf (" -I <dec> Specify TG to Hold During Trunking (DMR, P25, NXDN Type-C Trunking)\n");
printf (" -U <port> Enable RIGCTL/TCP; Set TCP Port for RIGCTL. (4532 on SDR++)\n");
printf (" -B <Hertz> Set RIGCTL Setmod Bandwidth in Hertz (0 - default - OFF)\n");
@@ -1767,10 +1847,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)
@@ -1882,11 +1962,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:G:D:L:V:U:YK:b:H:X:M:NQ:WrlZTF@:!:01:2:345:6:^:7:8_:9:Ek:I:J:O+:")) != -1)
while ((c = getopt (argc, argv, "~haepPqs: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:,:$:y:")) != -1)
{
switch (c)
@@ -1900,7 +1980,7 @@
opts.call_alert = 1;
break;
//Free'd up switches include: j, M,
//NOTE: Don't use -*, found it exhibits unusual behavior, was setting
//itself to -3 for some reason, may be reading contents of directory
@@ -1909,6 +1989,12 @@
//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 '~':
@@ -1928,10 +2014,10 @@
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)
@@ -1999,6 +2085,30 @@
baofeng_ap_pc5_keystream_creation(&state, optarg);
break;
//Auctus (GoComm) Keystream Creation
case ',':
auctus_keystream_creation(&state, optarg);
break;
//RAS Effective Key Creation
case '$':
ras_effective_key_creation(&state, optarg);
break;
//RAS Effective Key Direct Set
case 'y':
sscanf (optarg, "%llX", &state.ras_effective_key);
state.ras_effective_key &= 0xFFFFFFFFFFFFFF; //truncate to 56-bits
ras_permute_order((state.ras_effective_key & 0xFFFFFFFF), state.ras_permutation_order);
fprintf (stderr,"DMR RAS 56-bit Effective Key: %llX\n", state.ras_effective_key);
break;
//Vertex Standard KS Generation
case 'u':
vertex_keystream_setup(&state, optarg);
state.keyloader = 0;
break;
//Straight KS Generation
case 'S':
straight_mod_xor_keystream_creation(&state, optarg);
@@ -2009,10 +2119,11 @@
fprintf (stderr,"DMRA Late Entry Encryption Identifiers Disabled\n");
break;
case 'y': //use experimental 'float' audio output
opts.floating_point = 1; //enable floating point audio output
fprintf (stderr,"Enabling Experimental Floating Point Audio Output\n");
break;
//float audio experiment not going to be used going forward, very minimal benefit
// case 'y': //use experimental 'float' audio output
// opts.floating_point = 1; //enable floating point audio output
// fprintf (stderr,"Enabling Experimental Floating Point Audio Output\n");
// break;
case 'Y': //conventional scanner mode
opts.scanner_mode = 1; //enable scanner
@@ -2212,32 +2323,48 @@
break;
case 'H':
//new handling for 10/32/64 Char Key
strncpy(opts.szNumbers, optarg, 1023);
opts.szNumbers[1023] = '\0';
state.K1 = strtoull (opts.szNumbers, &pEnd, 16);
state.K2 = strtoull (pEnd, &pEnd, 16);
state.K3 = strtoull (pEnd, &pEnd, 16);
state.K4 = strtoull (pEnd, &pEnd, 16);
fprintf (stderr, "Hytera40/128/256 BP or AES128/256 Key = %016llX %016llX %016llX %016llX\n", state.K1, state.K2, state.K3, state.K4);
opts.dmr_mute_encL = 0;
opts.dmr_mute_encR = 0;
if (state.K1 == 0 && state.K2 == 0 && state.K3 == 0 && state.K4 == 0)
//check for caltta bp key load
if (optarg[0] == 'C' && optarg[1] == ':')
{
opts.dmr_mute_encL = 1;
opts.dmr_mute_encR = 1;
caltta_bp_keystream_creation(&state, optarg+2);
}
//check for Hytera OTA key load
else if (optarg[0] == 'O' && optarg[1] == ':')
{
hytera_ota_keystream_setup(&state, optarg+2);
}
else
{
strncpy(opts.szNumbers, optarg, 1023);
opts.szNumbers[1023] = '\0';
state.K1 = strtoull (opts.szNumbers, &pEnd, 16);
state.K2 = strtoull (pEnd, &pEnd, 16);
state.K3 = strtoull (pEnd, &pEnd, 16);
state.K4 = strtoull (pEnd, &pEnd, 16);
if (state.K2 == 0 && state.K3 == 0 && state.K4 == 0)
fprintf (stderr, "Hytera BP 40-bit Key: %010llX\n", state.K1);
else if (state.K2 != 0)
fprintf (stderr, "Multi 128-bit Key: %016llX%016llX\n", state.K1, state.K2);
else
fprintf (stderr, "Multi 256-bit Key: %016llX%016llX%016llX%016llX\n", state.K1, state.K2, state.K3, state.K4);
opts.dmr_mute_encL = 0;
opts.dmr_mute_encR = 0;
if (state.K1 == 0 && state.K2 == 0 && state.K3 == 0 && state.K4 == 0)
{
opts.dmr_mute_encL = 1;
opts.dmr_mute_encR = 1;
}
state.H = state.K1; //shim still required?
//load the AES keys into a seperate handler
state.A1[0] = state.A1[1] = state.K1;
state.A2[0] = state.A2[1] = state.K2;
state.A3[0] = state.A3[1] = state.K3;
state.A4[0] = state.A4[1] = state.K4;
//disable keyloader function
state.keyloader = 0;
//signal an aes key is loaded into each slot -- if no value presented, then this won't matter anyways
state.aes_key_loaded[0] = state.aes_key_loaded[1] = 1;
}
state.H = state.K1; //shim still required?
//load the AES keys into a seperate handler
state.A1[0] = state.A1[1] = state.K1;
state.A2[0] = state.A2[1] = state.K2;
state.A3[0] = state.A3[1] = state.K3;
state.A4[0] = state.A4[1] = state.K4;
//disable keyloader function
state.keyloader = 0;
//signal an aes key is loaded into each slot -- if no value presented, then this won't matter anyways
state.aes_key_loaded[0] = state.aes_key_loaded[1] = 1;
break;
case '4':
@@ -2757,6 +2884,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')
{
@@ -2892,6 +3024,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')
{
@@ -3049,18 +3182,18 @@
fprintf (stderr,"Enabling 6000 sps P25p2 all optimizations.\n");
}
break;
case 'u':
sscanf (optarg, "%i", &opts.uvquality);
if (opts.uvquality < 1)
{
opts.uvquality = 1;
}
else if (opts.uvquality > 64)
{
opts.uvquality = 64;
}
fprintf (stderr,"Setting unvoice speech quality to %i waves per band.\n", opts.uvquality);
break;
// case 'u':
// sscanf (optarg, "%i", &opts.uvquality);
// if (opts.uvquality < 1)
// {
// opts.uvquality = 1;
// }
// else if (opts.uvquality > 64)
// {
// opts.uvquality = 64;
// }
// fprintf (stderr,"Setting unvoice speech quality to %i waves per band.\n", opts.uvquality);
// break;
case 'x':
if (optarg[0] == 'x')
{
@@ -3077,6 +3210,15 @@
opts.inverted_dpmr = 1;
fprintf (stderr, "Expecting inverted ICOM dPMR signals.\n");
}
else if (optarg[0] == 'n')
{
opts.inverted_nxdn = 1;
#ifdef NXDN_OLD_SYNC
//
#else
fprintf (stderr, "Expecting inverted NXDN/DCR/IDAS signals.\n");
#endif
}
break;
case 'r':
@@ -3088,7 +3230,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;
@@ -3098,6 +3239,10 @@
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)
{
@@ -3182,9 +3327,24 @@
if (curr != NULL) ; //continue
else goto RTLEND; //end early with preset values
curr = strtok(NULL, ":"); //rtl device number "-D"
if (curr != NULL) opts.rtl_dev_index = atoi (curr);
else goto RTLEND;
if (strcmp (curr, "rtltcp") == 0) //rtl_tcp server instead of a local dongle
{
opts.rtltcp_enabled = 1;
curr = strtok(NULL, ":"); //rtl_tcp hostname
if (curr != NULL) snprintf (opts.rtltcp_hostname, sizeof(opts.rtltcp_hostname), "%s", curr);
else goto RTLEND;
curr = strtok(NULL, ":"); //rtl_tcp port
if (curr != NULL) opts.rtltcp_portno = atoi (curr);
else goto RTLEND;
}
else
{
curr = strtok(NULL, ":"); //rtl device number "-D"
if (curr != NULL) opts.rtl_dev_index = atoi (curr);
else goto RTLEND;
}
curr = strtok(NULL, ":"); //rtl freq "-c"
if (curr != NULL) opts.rtlsdr_center_freq = (uint32_t)atofs(curr);
@@ -3227,6 +3387,8 @@
RTLEND:
if (opts.rtltcp_enabled == 1) goto RTLTCPEND; //no local dongle to look for
device_count = rtlsdr_get_device_count();
if (!device_count)
{
@@ -3254,10 +3416,15 @@
}
RTLTCPEND:
if (opts.rtl_volume_multiplier > 3 || opts.rtl_volume_multiplier < 0)
opts.rtl_volume_multiplier = 1; //I wonder if you could flip polarity by using -1
fprintf (stderr, "Dev %d ", opts.rtl_dev_index);
if (opts.rtltcp_enabled == 1)
fprintf (stderr, "rtl_tcp %s:%d ", opts.rtltcp_hostname, opts.rtltcp_portno);
else
fprintf (stderr, "Dev %d ", opts.rtl_dev_index);
fprintf (stderr, "Freq %d ", opts.rtlsdr_center_freq);
fprintf (stderr, "Gain %d ", opts.rtl_gain_value);
fprintf (stderr, "PPM %d ", opts.rtlsdr_ppm_error);
@@ -3392,18 +3559,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))
{
@@ -3437,24 +3609,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)
{
@@ -3466,8 +3645,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
@@ -3518,5 +3699,5 @@
return (0);
}
//Tag: 2025
//Tag: 2026

View File

@@ -93,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);
}
@@ -141,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);
}
@@ -304,9 +304,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->payload_algid == 0x9F) //DES-XL
des_multi_keystream_output (state->payload_miP, state->R, state->ks_octetL, 2, state->xl_is_hdu); //hard coded bit count value, xl_is_hdu determines lfsr run values
if (state->payload_algid == 0x84) //AES256
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 2, 14); //14 + 1 discard round
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 2, 16); //15 + 1 discard round
if (state->payload_algid == 0x89) //AES128
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 0, 14); //14 + 1 discard round
aes_ofb_keystream_output (state->aes_iv, aes_key, state->ks_octetL, 0, 16); //15 + 1 discard round
}
@@ -535,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;
@@ -546,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++];
@@ -599,7 +599,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
if (state->rkey_array[hash] != 0)
{
#ifdef VERTEX_KEY40_BY_TG
state->vtx40_key = state->rkey_array[hash] & 0xFFFFFFFFFF;
vertex_40_keystream_creation(state, state->vtx40_key);
#elif VERTEX_KEY256_BY_TG
//TODO: This
#else
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
#endif
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
opts->dmr_mute_encL = 0;
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
@@ -619,67 +626,101 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
}
if ( (state->vtx_key_loaded != 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->vtx_key_loaded != 0 && state->forced_alg_id == 1) )
{
if (memcmp(ambe_d, ambe_silence, 49) != 0)
{
for (int i = 0; i < 49; i++)
{
if (i < 8 || i > 11)
ambe_d[i] ^= (char)state->static_ks_bits[0][i];
}
}
opts->dmr_mute_encL = 0; //shim to unmute
}
//Hytera BP //TODO: Clean this mess up
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->K1 > 0 && state->forced_alg_id == 1) )
{
int pos = 0;
int pos = 0;
unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4;
unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4;
int T_Key[256] = {0};
int pN[882] = {0};
int T_Key[256] = {0};
int pN[882] = {0};
int len = 0;
int len = 0;
if (k2 == 0)
{
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
}
if (k4 != 0)
{
len = 255;
}
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
if (k2 == 0)
{
pos = 0;
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
}
if (k4 != 0)
{
len = 255;
}
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
{
pos = 0;
}
}
//sanity check
if (state->DMRvcL > 17) //18
{
state->DMRvcL = 17; //18
}
pos = state->DMRvcL * 49;
if (memcmp(ambe_d, ambe_silence, 49) == 0)
pos += 49;
else
{
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
}
state->DMRvcL++;
}
//sanity check
if (state->DMRvcL > 17) //18
//Caltta BP
if ( (state->straight_ks == 0x26 && state->dmr_so & 0x40 && state->payload_algid == 0x26) || //&& state->payload_keyid != 0
(state->straight_ks == 0x26 && state->forced_alg_id == 0x26) )
{
state->DMRvcL = 17; //18
}
pos = state->DMRvcL * 49;
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
state->DMRvcL++;
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 56;
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]++)%state->straight_mod] & 1); //Yikes!
state->static_ks_counter[state->currentslot] += 7;
}
}
//DMR and P25p2 DES-OFB 56 Handling, Slot 1, VCH 0 -- consider moving into the AES handler
@@ -738,8 +779,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 >= 0x31 && \
state->payload_algid <= 0x3A && state->aes_key_loaded[0] == 1 ) || //KIRI Any
(state->currentslot == 0 && state->payload_algid == 0x02 && state->R != 0 ) ) //HYT ENHANCED
{
@@ -772,19 +813,13 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if(state->payload_algid == 0x02)
{
n = 0;
hytera_enhanced_rc4_setup(opts, state, state->R, state->payload_mi);
hytera_enhanced_rc4_setup(opts, state, state->R, state->payload_mi, 135);
}
if (state->payload_algid == 0x36)
if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
n = 0;
kirisun_adv_keystream_creation(state);
kirisun_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
{
@@ -1011,6 +1046,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
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!
//apply optional discard (i.e., caltta bp)
if (state->straight_discard != 0)
state->static_ks_counter[state->currentslot] += state->straight_discard;
}
}
@@ -1061,7 +1099,14 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
if (state->rkey_array[hash] != 0)
{
#ifdef VERTEX_KEY40_BY_TG
state->vtx40_key = state->rkey_array[hash] & 0xFFFFFFFFFF;
vertex_40_keystream_creation(state, state->vtx40_key);
#elif VERTEX_KEY256_BY_TG
//TODO: This
#else
state->K = state->rkey_array[hash] & 0xFF; //doesn't exceed 255
#endif
state->K1 = state->H = state->rkey_array[hash] & 0xFFFFFFFFFF; //doesn't exceed 40-bit limit
opts->dmr_mute_encR = 0;
// fprintf (stderr, "Key: %X ", state->rkey_array[hash]);
@@ -1081,6 +1126,21 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
}
if ( (state->vtx_key_loaded != 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
(state->vtx_key_loaded != 0 && state->forced_alg_id == 1) )
{
if (memcmp(ambe_d, ambe_silence, 49) != 0)
{
for (int i = 0; i < 49; i++)
{
if (i < 8 || i > 11)
ambe_d[i] ^= (char)state->static_ks_bits[1][i];
}
}
opts->dmr_mute_encR = 0; //shim to unmute
}
//Hytera BP //TODO: Clean this mess up
if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
(state->K1 > 0 && state->forced_alg_id == 1))
{
@@ -1136,14 +1196,33 @@ 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++;
}
//Caltta BP
if ( (state->straight_ks == 0x26 && state->dmr_soR & 0x40 && state->payload_algidR == 0x26) || //&& state->payload_keyidR != 0
(state->straight_ks == 0x26 && state->forced_alg_id == 0x26) )
{
if (memcmp(ambe_d, ambe_silence, 49) == 0)
state->static_ks_counter[state->currentslot] += 56;
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]++)%state->straight_mod] & 1); //Yikes!
state->static_ks_counter[state->currentslot] += 7;
}
}
//DMR and P25p2 DES-OFB 56 Handling, Slot 2, VCH 1 -- Consider moving into AES handler
if ( (state->currentslot == 1 && state->payload_algidR == 0x22 && state->RR != 0) ||
(state->currentslot == 1 && state->payload_algidR == 0x81 && state->RR != 0) )
@@ -1200,8 +1279,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 >= 0x31 && \
state->payload_algidR <= 0x3A && state->aes_key_loaded[1] == 1 ) || //KIRI Any
(state->currentslot == 1 && state->payload_algidR == 0x02 && state->RR != 0 ) ) //HYT ENHANCED
{
@@ -1234,19 +1313,13 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if(state->payload_algidR == 0x02)
{
n = 0;
hytera_enhanced_rc4_setup(opts, state, state->RR, state->payload_miR);
hytera_enhanced_rc4_setup(opts, state, state->RR, state->payload_miR, 135);
}
if (state->payload_algidR == 0x36)
if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
{
n = 0;
kirisun_adv_keystream_creation(state);
kirisun_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
{
@@ -1472,6 +1545,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
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!
//apply optional discard (i.e., caltta bp)
if (state->straight_discard != 0)
state->static_ks_counter[state->currentslot] += state->straight_discard;
}
}
@@ -1528,6 +1604,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->ken_sc == 1)
state->dmr_encL = 0;
if (state->payload_algid == 0x26 && state->straight_ks == 0x26)
state->dmr_encL = 0;
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
if (opts->reverse_mute == 1)
{
@@ -1608,6 +1687,9 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->ken_sc == 1)
state->dmr_encR = 0;
if (state->payload_algidR == 0x26 && state->straight_ks == 0x26)
state->dmr_encR = 0;
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
if (opts->reverse_mute == 1)
{

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,130 +273,3 @@ long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as
if (rms < 0) rms = 150;
return rms;
}
//same as symbol levels in m17.h (m17-fme version)
const int8_t symbol_list[4]={-3, -1, +1, +3};
/**
* @brief Slice payload symbols into soft dibits.
* Input (RRC filtered baseband sampled at symbol centers)
* should be already normalized to {-3, -1, +1 +3}.
* @param out Soft valued dibits (type-4).
* @param inp Array of 184 floats (1 sample per symbol).
*/
void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD])
{
for(uint8_t i=0; i<SYM_PER_PLD; i++)
{
//bit 0
if(inp[i]>=symbol_list[3])
{
out[i*2+1]=0xFFFF;
}
else if(inp[i]>=symbol_list[2])
{
out[i*2+1]=-(float)0xFFFF/(symbol_list[3]-symbol_list[2])*symbol_list[2]+inp[i]*(float)0xFFFF/(symbol_list[3]-symbol_list[2]);
}
else if(inp[i]>=symbol_list[1])
{
out[i*2+1]=0x0000;
}
else if(inp[i]>=symbol_list[0])
{
out[i*2+1]=(float)0xFFFF/(symbol_list[1]-symbol_list[0])*symbol_list[1]-inp[i]*(float)0xFFFF/(symbol_list[1]-symbol_list[0]);
}
else
{
out[i*2+1]=0xFFFF;
}
//bit 1
if(inp[i]>=symbol_list[2])
{
out[i*2]=0x0000;
}
else if(inp[i]>=symbol_list[1])
{
out[i*2]=0x7FFF-inp[i]*(float)0xFFFF/(symbol_list[2]-symbol_list[1]);
}
else
{
out[i*2]=0xFFFF;
}
}
}
/**
* @brief Soft logic NOT.
*
* @param a Input A.
* @return uint16_t Output = not A.
*/
uint16_t soft_bit_NOT(const uint16_t a)
{
return 0xFFFFU-a;
}
//randomizing pattern
const uint8_t rand_seq[46]=
{
0xD6, 0xB5, 0xE2, 0x30, 0x82, 0xFF, 0x84, 0x62, 0xBA, 0x4E, 0x96, 0x90, 0xD8, 0x98, 0xDD, 0x5D, 0x0C, 0xC8, 0x52, 0x43, 0x91, 0x1D, 0xF8,
0x6E, 0x68, 0x2F, 0x35, 0xDA, 0x14, 0xEA, 0xCD, 0x76, 0x19, 0x8D, 0xD5, 0x80, 0xD1, 0x33, 0x87, 0x13, 0x57, 0x18, 0x2D, 0x29, 0x78, 0xC3
};
/**
* @brief Randomize type-4 soft bits.
*
* @param inp Input 368 soft type-4 bits.
*/
void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2])
{
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
{
if((rand_seq[i/8]>>(7-(i%8)))&1) //flip bit if '1'
{
inp[i]=soft_bit_NOT(inp[i]);
}
}
}
//interleaver pattern
const uint16_t intrl_seq[SYM_PER_PLD*2]=
{
0, 137, 90, 227, 180, 317, 270, 39, 360, 129, 82, 219, 172, 309, 262, 31,
352, 121, 74, 211, 164, 301, 254, 23, 344, 113, 66, 203, 156, 293, 246, 15,
336, 105, 58, 195, 148, 285, 238, 7, 328, 97, 50, 187, 140, 277, 230, 367,
320, 89, 42, 179, 132, 269, 222, 359, 312, 81, 34, 171, 124, 261, 214, 351,
304, 73, 26, 163, 116, 253, 206, 343, 296, 65, 18, 155, 108, 245, 198, 335,
288, 57, 10, 147, 100, 237, 190, 327, 280, 49, 2, 139, 92, 229, 182, 319,
272, 41, 362, 131, 84, 221, 174, 311, 264, 33, 354, 123, 76, 213, 166, 303,
256, 25, 346, 115, 68, 205, 158, 295, 248, 17, 338, 107, 60, 197, 150, 287,
240, 9, 330, 99, 52, 189, 142, 279, 232, 1, 322, 91, 44, 181, 134, 271,
224, 361, 314, 83, 36, 173, 126, 263, 216, 353, 306, 75, 28, 165, 118, 255,
208, 345, 298, 67, 20, 157, 110, 247, 200, 337, 290, 59, 12, 149, 102, 239,
192, 329, 282, 51, 4, 141, 94, 231, 184, 321, 274, 43, 364, 133, 86, 223,
176, 313, 266, 35, 356, 125, 78, 215, 168, 305, 258, 27, 348, 117, 70, 207,
160, 297, 250, 19, 340, 109, 62, 199, 152, 289, 242, 11, 332, 101, 54, 191,
144, 281, 234, 3, 324, 93, 46, 183, 136, 273, 226, 363, 316, 85, 38, 175,
128, 265, 218, 355, 308, 77, 30, 167, 120, 257, 210, 347, 300, 69, 22, 159,
112, 249, 202, 339, 292, 61, 14, 151, 104, 241, 194, 331, 284, 53, 6, 143,
96, 233, 186, 323, 276, 45, 366, 135, 88, 225, 178, 315, 268, 37, 358, 127,
80, 217, 170, 307, 260, 29, 350, 119, 72, 209, 162, 299, 252, 21, 342, 111,
64, 201, 154, 291, 244, 13, 334, 103, 56, 193, 146, 283, 236, 5, 326, 95,
48, 185, 138, 275, 228, 365, 318, 87, 40, 177, 130, 267, 220, 357, 310, 79,
32, 169, 122, 259, 212, 349, 302, 71, 24, 161, 114, 251, 204, 341, 294, 63,
16, 153, 106, 243, 196, 333, 286, 55, 8, 145, 98, 235, 188, 325, 278, 47
};
/**
* @brief Reorder (interleave) 368 soft bits.
*
* @param outp Reordered soft bits.
* @param inp Input soft bits.
*/
void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_PLD*2])
{
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
outp[i]=inp[intrl_seq[i]];
}

View File

@@ -230,6 +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_nxdn = 1;
}
else
{
@@ -237,6 +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_nxdn = 0;
}
}
@@ -377,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;
@@ -592,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;
@@ -916,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
{
@@ -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;
@@ -1585,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

@@ -244,11 +244,13 @@ 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->rtltcp_enabled == 1)
printw ("| RTL_TCP: %s:%d;", opts->rtltcp_hostname, opts->rtltcp_portno);
else printw ("| RTL: %d;", opts->rtl_dev_index);
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);
@@ -363,7 +365,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)");
@@ -382,8 +384,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)");
@@ -921,10 +923,16 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000);
}
if (state->dmr_is_ras == 1)
printw ("{RAS}");
}
else if (lls == 32 || lls == 33 || lls == 34)
{
printw ("DMR MS - DCC: %02i; ", state->dmr_color_code);
if (state->dmr_is_ras == 1)
printw ("{RAS}");
}
else if (lls == 0 || lls == 1) //P1
{
@@ -1104,6 +1112,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x26)
{
attron(COLOR_PAIR(1));
printw("Caltta BP ");
// if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); } //TODO: This
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x02)
{
attron(COLOR_PAIR(1));
@@ -1112,26 +1127,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw(" Key: %010llX", state->R);
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x36)
if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
attron(COLOR_PAIR(1));
printw("Kirisun Advanced ");
printw("Kirisun Encryption ");
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");
@@ -1299,6 +1301,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); }
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x26)
{
attron(COLOR_PAIR(1));
printw("Caltta BP ");
// if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); } //TODO: This
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x02)
{
attron(COLOR_PAIR(1));
@@ -1307,26 +1316,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw(" Key: %010llX", state->RR);
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x36)
if (state->payload_algidR >= 0x31 && state->payload_algidR <= 0x3A)
{
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

@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -2312,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])

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_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 == 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,154 +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 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;
#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)
{
@@ -340,13 +369,6 @@ 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_pn95_dibit_scrambler (state, dbuf, 182);
//seperate our dbuf (dibit_buffer) into individual bit array
@@ -358,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++)
@@ -371,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
@@ -551,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, lich);
if (pich_tch & 2) nxdn_deperm_pich_tch(opts, state, facch_bits_b, lich);
//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
@@ -632,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

@@ -506,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]));
}
@@ -538,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

@@ -314,3 +314,25 @@ rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword) {
}
return &rs_12_9_checksum;
}
//This will run a RS 12,9 encode on 9 bytes of input
//input is an array of 12 bytes or more
void rs_12_9_encode(uint8_t * input)
{
rs_12_9_codeword_t cw;
rs_12_9_checksum_t *cs;
//init cw
memset(cw.data, 0, 12*sizeof(uint8_t));
//Copy 9 bytes of data input
memcpy(cw.data, input, 9);
//Calculate the 3 parity / checksum bytes
cs = rs_12_9_calc_checksum(&cw);
//Build the final 12-byte codeword
input[9] = cs->bytes[0];
input[10] = cs->bytes[1];
input[11] = cs->bytes[2];
}

123
src/rs-16-13.c Normal file
View File

@@ -0,0 +1,123 @@
/*
* RS(16,13) encoder
*
* 13 data bytes + 3 parity bytes = 16-byte codeword
*
* Uses the same generator polynomial and GF(256) tables as the
* DMR RS(12,9) implementation (DMR AI Spec / dmrshark / dsd):
*
* g(x) = x^3 + 0x40 x^2 + 0x38 x + 0x0e
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "dsd.h"
/* -----------------------------------------------------------------------
* Constants
* ----------------------------------------------------------------------- */
#define RS_DATASIZE 13
#define RS_CHECKSUMSIZE 3
#define RS_CODEWORDSIZE (RS_DATASIZE + RS_CHECKSUMSIZE) /* 16 */
/* -----------------------------------------------------------------------
* Galois Field tables (GF(256) – same as DMR RS(12,9))
* ----------------------------------------------------------------------- */
static const uint8_t galois_exp_table[256] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1D, 0x3A, 0x74, 0xE8, 0xCD, 0x87, 0x13, 0x26,
0x4C, 0x98, 0x2D, 0x5A, 0xB4, 0x75, 0xEA, 0xC9, 0x8F, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0,
0x9D, 0x27, 0x4E, 0x9C, 0x25, 0x4A, 0x94, 0x35, 0x6A, 0xD4, 0xB5, 0x77, 0xEE, 0xC1, 0x9F, 0x23,
0x46, 0x8C, 0x05, 0x0A, 0x14, 0x28, 0x50, 0xA0, 0x5D, 0xBA, 0x69, 0xD2, 0xB9, 0x6F, 0xDE, 0xA1,
0x5F, 0xBE, 0x61, 0xC2, 0x99, 0x2F, 0x5E, 0xBC, 0x65, 0xCA, 0x89, 0x0F, 0x1E, 0x3C, 0x78, 0xF0,
0xFD, 0xE7, 0xD3, 0xBB, 0x6B, 0xD6, 0xB1, 0x7F, 0xFE, 0xE1, 0xDF, 0xA3, 0x5B, 0xB6, 0x71, 0xE2,
0xD9, 0xAF, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0D, 0x1A, 0x34, 0x68, 0xD0, 0xBD, 0x67, 0xCE,
0x81, 0x1F, 0x3E, 0x7C, 0xF8, 0xED, 0xC7, 0x93, 0x3B, 0x76, 0xEC, 0xC5, 0x97, 0x33, 0x66, 0xCC,
0x85, 0x17, 0x2E, 0x5C, 0xB8, 0x6D, 0xDA, 0xA9, 0x4F, 0x9E, 0x21, 0x42, 0x84, 0x15, 0x2A, 0x54,
0xA8, 0x4D, 0x9A, 0x29, 0x52, 0xA4, 0x55, 0xAA, 0x49, 0x92, 0x39, 0x72, 0xE4, 0xD5, 0xB7, 0x73,
0xE6, 0xD1, 0xBF, 0x63, 0xC6, 0x91, 0x3F, 0x7E, 0xFC, 0xE5, 0xD7, 0xB3, 0x7B, 0xF6, 0xF1, 0xFF,
0xE3, 0xDB, 0xAB, 0x4B, 0x96, 0x31, 0x62, 0xC4, 0x95, 0x37, 0x6E, 0xDC, 0xA5, 0x57, 0xAE, 0x41,
0x82, 0x19, 0x32, 0x64, 0xC8, 0x8D, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xDD, 0xA7, 0x53, 0xA6,
0x51, 0xA2, 0x59, 0xB2, 0x79, 0xF2, 0xF9, 0xEF, 0xC3, 0x9B, 0x2B, 0x56, 0xAC, 0x45, 0x8A, 0x09,
0x12, 0x24, 0x48, 0x90, 0x3D, 0x7A, 0xF4, 0xF5, 0xF7, 0xF3, 0xFB, 0xEB, 0xCB, 0x8B, 0x0B, 0x16,
0x2C, 0x58, 0xB0, 0x7D, 0xFA, 0xE9, 0xCF, 0x83, 0x1B, 0x36, 0x6C, 0xD8, 0xAD, 0x47, 0x8E, 0x01
};
static const uint8_t galois_log_table[256] = {
0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75,
4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69,
29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166,
6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64,
30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61,
202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87,
7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24,
227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97,
242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162,
31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246,
108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90,
203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175
};
/* -----------------------------------------------------------------------
* Galois multiplication
* ----------------------------------------------------------------------- */
static uint8_t galois_mult(uint8_t a, uint8_t b)
{
if (a == 0 || b == 0)
return 0;
return galois_exp_table[(galois_log_table[a] + galois_log_table[b]) % 255];
}
/* -----------------------------------------------------------------------
* RS(16,13) encoder – LFSR with the DMR generator polynomial
*
* genpoly coefficients (same as RS(12,9)):
* { 0x40, 0x38, 0x0e, 0x01 }
* ----------------------------------------------------------------------- */
void rs16_13_encode(const uint8_t data_in[RS_DATASIZE], uint8_t codeword[RS_CODEWORDSIZE])
{
/* Generator polynomial coefficients from DMR AI Spec */
static const uint8_t genpoly[] = { 0x40, 0x38, 0x0e, 0x01 };
uint8_t parity[3] = { 0, 0, 0 };
uint8_t i;
uint8_t feedback;
for (i = 0; i < RS_DATASIZE; i++) {
feedback = data_in[i] ^ parity[0];
parity[0] = parity[1] ^ galois_mult(genpoly[2], feedback);
parity[1] = parity[2] ^ galois_mult(genpoly[1], feedback);
parity[2] = galois_mult(genpoly[0], feedback);
}
/* Build systematic codeword: data || parity */
memcpy(codeword, data_in, RS_DATASIZE);
codeword[13] = parity[0];
codeword[14] = parity[1];
codeword[15] = parity[2];
}
//This will run a RS 16,13 encode on 13 bytes of input
//input is an array of 16 bytes or more
void rs_16_13_encode(uint8_t * input)
{
uint8_t output[16];
memset(output, 0, sizeof(output));
//Calculate the 3 parity / checksum bytes
rs16_13_encode(input, output);
//append the encoded parity to the input
input[13] = output[13];
input[14] = output[14];
input[15] = output[15];
}

View File

@@ -48,6 +48,7 @@ static int atan_lut_size = 131072; /* 512 KB */
static int atan_lut_coef = 8;
//UDP -- keep for compatibility reasons
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static pthread_t socket_freq;
@@ -453,6 +454,9 @@ void dc_block_filter(struct demod_state *fm)
{
int i, avg;
int64_t sum = 0;
if (fm->result_len == 0) {
return;
}
for (i=0; i < fm->result_len; i++) {
sum += fm->result[i];
}
@@ -465,27 +469,27 @@ void dc_block_filter(struct demod_state *fm)
}
long int rms(int16_t *samples, int len, int step)
/* largely lifted from rtl_power */
{
int i;
long int rms;
long p, t, s;
double dc, err;
long int rms_val;
int64_t p = 0, t = 0;
int64_t s;
p = t = 0L;
for (i=0; i<len; i+=step) {
s = (long)samples[i];
for (i = 0; i < len; i += step) {
s = samples[i];
t += s;
p += s * s;
}
/* correct for dc offset in squares */
dc = (double)(t*step) / (double)len;
err = t * 2 * dc - dc * dc * len;
rms = (long int)sqrt((p-err) / len);
//going with a value that's easy to figure out when its done the thing
if (rms < 0){ rms = 999; }
return rms;
double dc = (double)(t * (int64_t)step) / (double)len;
double err = (double)t * 2.0 * dc - dc * dc * (double)len;
double val = ((double)p - err) / (double)len;
if (val < 0.0) val = 0.0;
rms_val = (long int)sqrt(val);
if (rms_val < 0) rms_val = 999;
return rms_val;
}
void full_demod(struct demod_state *d)
@@ -567,7 +571,7 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
static void *dongle_thread_fn(void *arg)
{
struct dongle_state *s = static_cast<dongle_state*>(arg);
rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len);
rtlsdr_or_tcp_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len);
return 0;
}
@@ -604,17 +608,17 @@ int nearest_gain(rtlsdr_dev_t *dev, int target_gain)
{
int i, r, err1, err2, count, nearest;
int* gains;
r = rtlsdr_set_tuner_gain_mode(dev, 1);
r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 1);
if (r < 0) {
fprintf (stderr, "WARNING: Failed to enable manual gain.\n");
return r;
}
count = rtlsdr_get_tuner_gains(dev, NULL);
count = rtlsdr_or_tcp_get_tuner_gains(dev, NULL);
if (count <= 0) {
return 0;
}
gains = static_cast<int*>(malloc(sizeof(int) * count));
count = rtlsdr_get_tuner_gains(dev, gains);
count = rtlsdr_or_tcp_get_tuner_gains(dev, gains);
nearest = gains[0];
for (i=0; i<count; i++) {
err1 = abs(target_gain - nearest);
@@ -629,20 +633,28 @@ int nearest_gain(rtlsdr_dev_t *dev, int target_gain)
int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency)
{
int r;
r = rtlsdr_set_center_freq(dev, frequency);
if (r < 0) {
fprintf (stderr, " (WARNING: Failed to set Center Frequency). \n");
} else {
fprintf (stderr, " (Center Frequency: %u Hz.) \n", frequency);
int r = -1;
//spam tuning
for (int i = 0; i < 20; i++)
{
r = rtlsdr_or_tcp_set_center_freq(dev, frequency);
if (r < 0)
fprintf (stderr, " (RETRY: Failed to set Center Frequency %u). \n", dongle.freq);
else
{
fprintf (stderr, " (Center Frequency: %u Hz.) \n", frequency);
break;
}
}
return r;
}
int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate)
{
int r;
r = rtlsdr_set_sample_rate(dev, samp_rate);
r = rtlsdr_or_tcp_set_sample_rate(dev, samp_rate);
if (r < 0) {
fprintf (stderr, "WARNING: Failed to set sample rate.\n");
} else {
@@ -654,7 +666,7 @@ int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate)
int verbose_direct_sampling(rtlsdr_dev_t *dev, int on)
{
int r;
r = rtlsdr_set_direct_sampling(dev, on);
r = rtlsdr_or_tcp_set_direct_sampling(dev, on);
if (r != 0) {
fprintf (stderr, "WARNING: Failed to set direct sampling mode.\n");
return r;
@@ -671,7 +683,7 @@ int verbose_direct_sampling(rtlsdr_dev_t *dev, int on)
int verbose_offset_tuning(rtlsdr_dev_t *dev)
{
int r;
r = rtlsdr_set_offset_tuning(dev, 1);
r = rtlsdr_or_tcp_set_offset_tuning(dev, 1);
if (r != 0) {
fprintf (stderr, "WARNING: Failed to set offset tuning.\n");
} else {
@@ -683,7 +695,7 @@ int verbose_offset_tuning(rtlsdr_dev_t *dev)
int verbose_auto_gain(rtlsdr_dev_t *dev)
{
int r;
r = rtlsdr_set_tuner_gain_mode(dev, 0);
r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 0);
if (r != 0) {
fprintf (stderr, "WARNING: Failed to set tuner gain.\n");
} else {
@@ -695,12 +707,12 @@ int verbose_auto_gain(rtlsdr_dev_t *dev)
int verbose_gain_set(rtlsdr_dev_t *dev, int gain)
{
int r;
r = rtlsdr_set_tuner_gain_mode(dev, 1);
r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 1);
if (r < 0) {
fprintf (stderr, "WARNING: Failed to enable manual gain.\n");
return r;
}
r = rtlsdr_set_tuner_gain(dev, gain);
r = rtlsdr_or_tcp_set_tuner_gain(dev, gain);
if (r != 0) {
fprintf (stderr, "WARNING: Failed to set tuner gain.\n");
} else {
@@ -714,7 +726,7 @@ int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error)
int r;
// if (ppm_error == 0) {
// return 0;}
r = rtlsdr_set_freq_correction(dev, ppm_error);
r = rtlsdr_or_tcp_set_freq_correction(dev, ppm_error);
if (r < 0) {
fprintf (stderr, "WARNING: Failed to set ppm error.\n");
} else {
@@ -726,7 +738,7 @@ int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error)
int verbose_reset_buffer(rtlsdr_dev_t *dev)
{
int r;
r = rtlsdr_reset_buffer(dev);
r = rtlsdr_or_tcp_reset_buffer(dev);
if (r < 0) {
fprintf (stderr, "WARNING: Failed to reset buffers.\n");}
return r;
@@ -799,7 +811,7 @@ static void *controller_thread_fn(void *arg)
/* hacky hopping */
s->freq_now = (s->freq_now + 1) % s->freq_len;
optimal_settings(s->freqs[s->freq_now], demod.rate_in);
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
dongle.mute = BUFFER_DUMP;
}
return 0;
@@ -1004,7 +1016,7 @@ static void *socket_thread_fn(void *arg) {
new_freq = chars_to_int(buffer);
dongle.freq = new_freq;
optimal_settings(new_freq, demod.rate_in);
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq);
}
@@ -1019,7 +1031,7 @@ static void *socket_thread_fn(void *arg) {
void rtlsdr_sighandler()
{
fprintf (stderr, "Signal caught, exiting!\n");
rtlsdr_cancel_async(dongle.dev);
rtlsdr_or_tcp_cancel_async(dongle.dev);
}
void open_rtlsdr_stream(dsd_opts *opts)
@@ -1031,6 +1043,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
//this needs to be initted first, then we set the parameters
dongle_init(&dongle);
//init with low pass if decoding P25 or EDACS/Provoice
if (opts->frame_p25p1 == 1 || opts->frame_p25p2 == 1 || opts->frame_provoice == 1)
demod_init_ro2(&demod);
@@ -1071,14 +1084,23 @@ void open_rtlsdr_stream(dsd_opts *opts)
ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH;
r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index);
if (r < 0)
if (opts->rtltcp_enabled == 1)
{
fprintf (stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index);
exit(1);
} else {
fprintf (stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index);
}
if (rtltcp_open(opts->rtltcp_hostname, opts->rtltcp_portno) < 0)
//rtltcp_open prints its own error messages
exit(1);
}
else
{
r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index);
if (r < 0)
{
fprintf (stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index);
exit(1);
} else {
fprintf (stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index);
}
}
if (demod.deemph) {
demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6)))));
@@ -1089,7 +1111,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
verbose_auto_gain(dongle.dev);
fprintf (stderr, "Setting RTL Autogain. \n");
} else {
dongle.gain = nearest_gain(dongle.dev, dongle.gain);
opts->rtl_gain_actual = dongle.gain = nearest_gain(dongle.dev, dongle.gain);
verbose_gain_set(dongle.dev, dongle.gain);
// fprintf (stderr, "Setting RTL Nearest Gain to %d. \n", dongle.gain); //seems to be working now
}
@@ -1110,7 +1132,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
void cleanup_rtlsdr_stream()
{
fprintf (stderr, "cleaning up...\n");
rtlsdr_cancel_async(dongle.dev);
rtlsdr_or_tcp_cancel_async(dongle.dev);
pthread_join(dongle.thread, NULL);
safe_cond_signal(&demod.ready, &demod.ready_m);
pthread_join(demod.thread, NULL);
@@ -1123,7 +1145,7 @@ void cleanup_rtlsdr_stream()
output_cleanup(&output);
controller_cleanup(&controller);
rtlsdr_close(dongle.dev);
rtlsdr_or_tcp_close(dongle.dev);
}
//original for safe keeping
@@ -1148,7 +1170,51 @@ int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
if (opts->rtlsdr_ppm_error != dongle.ppm_error)
{
dongle.ppm_error = opts->rtlsdr_ppm_error;
verbose_ppm_set(dongle.dev, dongle.ppm_error);
for (int i = 0; i < 20; i++)
{
int r = -1;
r = verbose_ppm_set(dongle.dev, dongle.ppm_error);
if (r < 0)
fprintf (stderr, " Retry Gain.");
else break;
}
}
//if Gain or AGC Value is Manually Changed, Change it here now
if (opts->rtl_gain_actual != dongle.gain && opts->rtl_gain_value != 0)
{
dongle.gain = (opts->rtl_gain_value * 10);
opts->rtl_gain_actual = dongle.gain = nearest_gain(dongle.dev, dongle.gain);
for (int i = 0; i < 20; i++)
{
int r = -1;
r = verbose_gain_set(dongle.dev, dongle.gain);
if (r < 0)
fprintf (stderr, " Retry Gain.");
else break;
}
//debug
// fprintf (stderr, "\n Dongle Gain Set: %d; \n", dongle.gain);
}
else if (opts->rtl_gain_value == 0 && dongle.gain != AUTO_GAIN)
{
opts->rtl_gain_actual = dongle.gain = AUTO_GAIN;
for (int i = 0; i < 20; i++)
{
int r = 1;
r = verbose_auto_gain(dongle.dev);
if (r < 0)
fprintf (stderr, " Retry Gain.");
else break;
}
//debug
// fprintf (stderr, "\n AGC Set; \n");
}
while (output.queue.empty())
@@ -1176,16 +1242,23 @@ int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
//function may lag since it isn't running as its own thread
void rtl_dev_tune(dsd_opts * opts, long int frequency)
{
int r;
if (opts->payload == 1)
fprintf (stderr, "\nTuning to %lu Hz.", frequency);
dongle.freq = opts->rtlsdr_center_freq = frequency;
optimal_settings(dongle.freq, demod.rate_in);
optimal_settings(dongle.freq, demod.rate_in); //this too?
if (opts->payload == 1)
fprintf (stderr, " (Center Frequency: %u Hz.) \n", dongle.freq);
r = rtlsdr_set_center_freq(dongle.dev, dongle.freq);
if (r < 0)
fprintf (stderr, " (WARNING: Failed to set Center Frequency %u). \n", dongle.freq);
//spam tuning
for (int i = 0; i < 20; i++)
{
int r = -1;
r = rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
if (r < 0)
fprintf (stderr, " (RETRY: Failed to set Center Frequency %u). \n", dongle.freq);
else break;
}
pthread_rwlock_wrlock(&output.rw); //prevent possible segfault when cleaning the queue
rtl_clean_queue();
@@ -1193,20 +1266,18 @@ void rtl_dev_tune(dsd_opts * opts, long int frequency)
}
//return RMS value (root means square) power level -- used as soft squelch inside of framesync
static long int prev_rms = 0;
long int rtl_return_rms()
{
long int sr = 0;
// #ifdef __arm__
// sr = 100;
// #else
//debug -- on main machine, lp_len is around 6420, so this probably contributes to very high CPU usage
// fprintf (stderr, "LP_LEN: %d \n", demod.lp_len);
//I've found that just using a sample size of 160 will give us a good approximation without killing the CPU
// sr = rms(demod.lowpassed, demod.lp_len, 1);
sr = rms(demod.lowpassed, 160, 1); //I wonder what a reasonable value would be for #2 (input len) there
// #endif
return (sr);
long int sr = rms(demod.lowpassed, 512, 1); //change 512 to 160 if using too much CPU
// simple 4-block IIR average for stability
sr = (prev_rms * 3 + sr) / 4;
prev_rms = sr;
return sr;
}
//simple function to clear the rtl sample queue when tuning and during other events (ncurses menu open/close)

331
src/rtl_sdr_tcp.c Normal file
View File

@@ -0,0 +1,331 @@
/*-------------------------------------------------------------------------------
* rtl_sdr_tcp.c
* rtl_tcp client for the RTL-SDR input: samples arrive on a TCP socket, and tuner commands are sent back on it
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
//MSG_NOSIGNAL is missing before macOS 11; SO_NOSIGPIPE in rtltcp_open keeps send from raising SIGPIPE there
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
//rtlsdr_read_async's default block size, in bytes
#define RTLTCP_BUF_LENGTH (16 * 32 * 512)
//rtl_tcp state: socket, tuner type, and a mutex that keeps commands from different threads from interleaving
//NOTE: Keeping these as static global is not great. They really should be in some kind of opaque handle
// like rtl_tcp_dev_t or something. For applications that only use one device at a time, this is fine.
// But if we ever move to supporting multiple simultaneous RTL tuners, this will need to change.
static int rtltcp_sock = -1;
static int rtltcp_tuner_type = RTLSDR_TUNER_UNKNOWN; //from the server's greeting
static pthread_mutex_t rtltcp_m = PTHREAD_MUTEX_INITIALIZER;
//limit for connecting to each address, reading the greeting, and sending a command, so a stalled server can't hang dsd-fme
static const int RTLTCP_TIMEOUT_MS = 5000;
static int64_t rtltcp_now_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
//Poll in short intervals so shutdown works even when the server stops responding.
//A negative deadline permits an idle sample stream; setup and commands have fixed deadlines.
static int rtltcp_wait(int sock, short events, int64_t deadline)
{
struct pollfd pfd = {sock, events, 0};
while (!exitflag) {
int timeout = 100;
if (deadline >= 0) {
//check if we have exceeded our wait time
int64_t remaining = deadline - rtltcp_now_ms();
if (remaining <= 0) {
errno = ETIMEDOUT;
return -1;
}
//set the next poll timeout
if (remaining < timeout) timeout = (int)remaining;
}
//poll and handle the result
int r = poll(&pfd, 1, timeout);
if (r > 0) return 0; //recv/send/getsockopt will report EOF or socket errors
if (r < 0 && errno != EINTR) return -1;
}
errno = EINTR;
return -1;
}
//rtl_tcp command bytes, each named after the librtlsdr function the server calls with the parameter
enum
{
RTLTCP_SET_CENTER_FREQ = 0x01,
RTLTCP_SET_SAMPLE_RATE = 0x02,
RTLTCP_SET_TUNER_GAIN_MODE = 0x03,
RTLTCP_SET_TUNER_GAIN = 0x04,
RTLTCP_SET_FREQ_CORRECTION = 0x05,
RTLTCP_SET_DIRECT_SAMPLING = 0x09,
RTLTCP_SET_OFFSET_TUNING = 0x0a
};
//send one rtl_tcp command: a command byte followed by a 32-bit big-endian parameter.
//A partly sent command would misalign every later one, so a failed send ends the connection.
static int rtltcp_command(uint8_t cmd, uint32_t param)
{
//encode command and parameter to send to rtl_tcp
unsigned char buf[5] = {cmd, (unsigned char)(param >> 24), (unsigned char)(param >> 16), (unsigned char)(param >> 8), (unsigned char)param};
size_t sent = 0;
pthread_mutex_lock(&rtltcp_m);
//non-blocking send with a timeout
int64_t deadline = rtltcp_now_ms() + RTLTCP_TIMEOUT_MS;
while (sent < 5) {
if (rtltcp_wait(rtltcp_sock, POLLOUT, deadline) < 0) break;
ssize_t n = send(rtltcp_sock, buf + sent, 5 - sent, MSG_NOSIGNAL);
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
continue;
}
if (n <= 0) {
break;
}
sent += n;
}
//a failed or partial send ends the connection
if (sent < 5) {
shutdown(rtltcp_sock, SHUT_RDWR);
}
pthread_mutex_unlock(&rtltcp_m);
return sent < 5 ? -1 : 0;
}
//connect to an rtl_tcp server over IPv4 or IPv6, read its greeting, and store the socket in rtltcp_sock; prints the outcome and returns 0, or -1 on failure
int rtltcp_open(const char *hostname, int portno)
{
struct addrinfo hints, *res, *ai;
char portstr[16];
unsigned char header[12];
int sock = -1;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
snprintf(portstr, sizeof(portstr), "%d", portno);
if (getaddrinfo(hostname, portstr, &hints, &res) != 0) {
fprintf (stderr, "Failed to resolve rtl_tcp server %s.\n", hostname);
return -1;
}
//try each resolved address in turn until one connects
for (ai = res; ai != NULL && !exitflag; ai = ai->ai_next) {
//socket creation
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
continue;
}
//set nonblocking
int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
close(sock);
sock = -1;
continue;
}
//connect
int r = connect(sock, ai->ai_addr, ai->ai_addrlen);
//connect returns immediately; wait for the attempt to finish, then SO_ERROR says whether it succeeded
if (r < 0 && errno == EINPROGRESS && rtltcp_wait(sock, POLLOUT, rtltcp_now_ms() + RTLTCP_TIMEOUT_MS) == 0) {
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && error == 0) r = 0;
}
//successful connection
if (r == 0 && !exitflag) {
break;
}
//close socket and retry with different address
close(sock);
sock = -1;
}
freeaddrinfo(res);
if (sock < 0) {
fprintf (stderr, "Failed to connect to rtl_tcp server %s:%d.\n", hostname, portno);
return -1;
}
#ifdef SO_NOSIGPIPE
//macOS and the BSDs: a send to a closed connection returns an error instead of raising SIGPIPE
int one = 1;
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
#endif
size_t received = 0;
//non-blocking receive with a timeout
int64_t deadline = rtltcp_now_ms() + RTLTCP_TIMEOUT_MS;
while (received < sizeof(header)) {
if (rtltcp_wait(sock, POLLIN, deadline) < 0) break;
ssize_t n = recv(sock, header + received, sizeof(header) - received, 0);
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) continue;
if (n <= 0) break;
received += n;
}
//the greeting is "RTL0", the tuner type, and the number of tuner gain steps
if (received != sizeof(header) || memcmp(header, "RTL0", 4) != 0) {
fprintf (stderr, "%s:%d did not send an rtl_tcp greeting.\n", hostname, portno);
close(sock);
return -1;
}
//socket is valid and ready to use
rtltcp_sock = sock;
//store tuner type for later use in rtltcp_get_tuner_gains
rtltcp_tuner_type = ((uint32_t)header[4] << 24) | ((uint32_t)header[5] << 16) | ((uint32_t)header[6] << 8) | header[7];
fprintf (stderr, "Using rtl_tcp server %s:%d. \n", hostname, portno);
return 0;
}
//rtlsdr_get_tuner_gains for the server's tuner: copies its gain steps, in tenths of a dB, into gains unless gains is NULL, and returns the number of steps
static int rtltcp_get_tuner_gains(int *gains)
{
//rtl_tcp advertises only the gain count, not the values; librtlsdr's gain query requires a local device.
//Unfortunately this means we have to copy librtlsdr's tables here to select the nearest valid gain given the remote device.
//It would be better if rtl_tcp told us the actual list of supported gains, but it doesn't.
static const int e4k[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, 240, 290, 340, 420 };
static const int fc0012[] = { -99, -40, 71, 179, 192 };
static const int fc0013[] = { -99, -73, -65, -63, -60, -58, -54, 58, 61, 63, 65, 67, 68, 70, 71, 179, 181, 182, 184, 186, 188, 191, 197 };
static const int r82xx[] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, 207, 229, 254, 280, 297, 328, 338, 364, 372, 386, 402, 421, 434, 439, 445, 480, 496 };
static const int none[] = { 0 };
const int *table = none;
int count = 1;
//different tuners have different possible gains
switch (rtltcp_tuner_type) {
case RTLSDR_TUNER_E4000: table = e4k; count = sizeof(e4k) / sizeof(int); break;
case RTLSDR_TUNER_FC0012: table = fc0012; count = sizeof(fc0012) / sizeof(int); break;
case RTLSDR_TUNER_FC0013: table = fc0013; count = sizeof(fc0013) / sizeof(int); break;
case RTLSDR_TUNER_R820T:
case RTLSDR_TUNER_R828D: table = r82xx; count = sizeof(r82xx) / sizeof(int); break;
}
if (gains != NULL) {
memcpy(gains, table, count * sizeof(int));
}
return count;
}
//close the rtl_tcp connection; returns close's result
static int rtltcp_close(void)
{
int r = close(rtltcp_sock);
rtltcp_sock = -1;
return r;
}
//pass rtl_tcp samples to cb until the connection closes or exitflag is set, in the same block size that rtlsdr_read_async delivers by default
static int rtltcp_read_async(rtlsdr_read_async_cb_t cb, void *ctx)
{
static unsigned char buf[RTLTCP_BUF_LENGTH];
size_t received = 0;
//receive until exit is requested or the connection ends; no deadline, so an idle stream can wait indefinitely
while (rtltcp_wait(rtltcp_sock, POLLIN, -1) == 0) {
ssize_t n = recv(rtltcp_sock, buf + received, sizeof(buf) - received, 0);
if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) continue;
if (n <= 0) break;
received += n;
if (received == sizeof(buf)) {
cb(buf, sizeof(buf), ctx);
received = 0;
}
}
//we are finished (for a variety of reasons). ensure exitflag is set
if (!exitflag) {
fprintf (stderr, "rtl_tcp server closed the connection.\n");
exitflag = 1;
}
return 0;
}
//Each rtlsdr_or_tcp_* function takes the arguments of the librtlsdr function it is named after.
//While an rtl_tcp server is connected, the call goes to the server; otherwise it goes to librtlsdr.
int rtlsdr_or_tcp_close(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? rtltcp_close() : rtlsdr_close(dev);
}
int rtlsdr_or_tcp_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
return rtltcp_sock >= 0 ? rtltcp_read_async(cb, ctx) : rtlsdr_read_async(dev, cb, ctx, buf_num, buf_len);
}
int rtlsdr_or_tcp_cancel_async(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? shutdown(rtltcp_sock, SHUT_RDWR) : rtlsdr_cancel_async(dev);
}
int rtlsdr_or_tcp_reset_buffer(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? 0 : rtlsdr_reset_buffer(dev);
}
int rtlsdr_or_tcp_get_tuner_gains(rtlsdr_dev_t *dev, int *gains)
{
return rtltcp_sock >= 0 ? rtltcp_get_tuner_gains(gains) : rtlsdr_get_tuner_gains(dev, gains);
}
int rtlsdr_or_tcp_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_CENTER_FREQ, freq) : rtlsdr_set_center_freq(dev, freq);
}
int rtlsdr_or_tcp_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_SAMPLE_RATE, rate) : rtlsdr_set_sample_rate(dev, rate);
}
int rtlsdr_or_tcp_set_direct_sampling(rtlsdr_dev_t *dev, int on)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_DIRECT_SAMPLING, on) : rtlsdr_set_direct_sampling(dev, on);
}
int rtlsdr_or_tcp_set_offset_tuning(rtlsdr_dev_t *dev, int on)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_OFFSET_TUNING, on) : rtlsdr_set_offset_tuning(dev, on);
}
int rtlsdr_or_tcp_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_TUNER_GAIN_MODE, manual) : rtlsdr_set_tuner_gain_mode(dev, manual);
}
int rtlsdr_or_tcp_set_tuner_gain(rtlsdr_dev_t *dev, int gain)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_TUNER_GAIN, gain) : rtlsdr_set_tuner_gain(dev, gain);
}
int rtlsdr_or_tcp_set_freq_correction(rtlsdr_dev_t *dev, int ppm)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_FREQ_CORRECTION, ppm) : rtlsdr_set_freq_correction(dev, ppm);
}

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
//--------------------------------------------------------------------

209
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;
}
@@ -684,7 +595,7 @@ void pn95_lfsr() //test to see if this generates the correct bits now
}
void ysf_ehr (dsd_opts * opts, dsd_state * state, uint8_t dbuf[180], int start, int stop )
void ysf_ehr (dsd_opts * opts, dsd_state * state, uint8_t * dbuf, int start, int stop )
{
int i;
char ambe_fr[4][24];
@@ -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)
@@ -939,7 +850,7 @@ void processYSF(dsd_opts * opts, dsd_state * state)
}
// send VCH to ehr voice handler
ysf_ehr (opts, state, vbuf, 0, 4);
ysf_ehr (opts, state, vbuf, 0, 5);
//send DCH to decoder
ysf_conv_dch (opts, state, bn, bt, fn, ft, cm, dbuf);