108 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
lwvmobile
abf28593b1 Fix some Cygwin64 Build Script Issues and Instructions; #324
Use sed to replace line in ITPP CmakeLists.txt file to update the cmake_minimum_required to an acceptible version for newer cmake;

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

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

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

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

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

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

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

Don't use for future CLI arguments;

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

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

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

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

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -43,9 +43,9 @@ Debian/Mint/Ubuntu/Pi
```
sudo apt update
#Ubuntu 22.04 lts / Debian 11 and lower
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libcodec2-dev
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libcodec2-dev
#Ubuntu 24.04 lts / Debian 12
#sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncurses-dev libncurses6 libcodec2-dev
#sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncurses-dev libncurses6 libcodec2-dev
```
Fedora 36/37 -- from https://github.com/lwvmobile/dsd-fme/issues/99
@@ -60,8 +60,6 @@ Arch -- https://github.com/lwvmobile/dsd-fme/issues/153 and https://github.com/l
```
sudo pacman -Syu
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget rtl-sdr
wget https://mirror.archstrike.org/x86_64/archstrike/itpp-4.3.1-3-x86_64.pkg.tar.xz
sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
```
## Headless Ubuntu Server/Pi
@@ -69,24 +67,7 @@ sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
If running headless, swap out pavucontrol for pulsemixer, and also install pulseaudio as well. Attempting to install pavucontrol in a headless environment may attempt to install a minimal desktop environment. Note: Default behavior of pulseaudio in a headless environment may be to be muted, so check by opening pulsemixer and unmuting and routing audio appropriately.
```
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev pulsemixer pulseaudio libcodec2-dev
```
### Build and Install ITPP - ONLY IF NOT IN REPO!!
```
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
tar xjf itpp*
#if you can't cd into this folder, double check folder name first
cd itpp-4.3.1
mkdir build
cd build
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig
cd ..
cd ..
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libncursesw5-dev pulsemixer pulseaudio libcodec2-dev
```
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice")
@@ -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:

View File

@@ -1,4 +1,4 @@
Build Version: AW 2025-182-g24c1dd9
Build Version: AW 2026-47-ga6ea60b
MBElib Version: 1.3.4
CODEC2 Support Enabled
@@ -21,10 +21,9 @@ 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
m17udp for M17 UDP/IP socket bind input (default host 127.0.0.1; default port 17000)
m17udp:192.168.7.8:17001 for M17 UDP/IP bind input (Binding Address and Port
filename.bin for OP25/FME capture bin files
filename.wav for 48K/1 wav files (SDR++, GQRX)
filename.wav -s 96000 for 96K/1 wav files (DSDPlus)
@@ -37,8 +36,6 @@ Input/Output options:
null for no audio output
udp for UDP socket blaster output (default host 127.0.0.1; default port 23456)
udp:192.168.7.8:23470 for UDP socket blaster output (Target Address and Port
m17udp for M17 UDP/IP socket blaster output (default host 127.0.0.1; default port 17000)
m17udp:192.168.7.8:17001 for M17 UDP/IP blaster output (Target Address and Port
-d <dir> Create mbe data files, use this directory (TDMA version is experimental)
-r <files> Read/Play saved mbe data from file(s)
-g <float> Audio Digital Output Gain (Default: 0 = Auto; )
@@ -54,6 +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.
@@ -64,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
@@ -80,27 +78,12 @@ 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
Encoder options:
-fZ M17 Stream Voice Encoder
Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i pulse -6 m17signal.wav -8 -N 2> m17encoderlog.txt
Run M17 Encoding, listening to pulse audio server, with internal decode/playback and output to 48k/1 wav file
Example: dsd-fme -fZ -M M17:9:DSD-FME:LWVMOBILE -i tcp -o pulse -8 -N 2> m17encoderlog.txt
Run M17 Encoding, listening to default tcp input, without internal decode/playback and output to 48k/1 analog output device
-fP M17 Packet Encoder
Example: dsd-fme -fP -M M17:9:DSD-FME:LWVMOBILE -6 m17pkt.wav -8 -S 'Hello World'
-fB M17 BERT Encoder
Example: dsd-fme -fB -M M17:9:DSD-FME:LWVMOBILE -6 m17bert.wav -8
-M M17 Encoding User Configuration String: M17:CAN:SRC:DST:INPUT_RATE:VOX (see examples above).
CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.
BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'
Input Rate Default is 48000; Use Multiples of 8000 up to 48000.
VOX Enabled on 1; (Default = 0)
Values not entered into the M17: string are set to default values.
Decoder options:
-fa Auto Detection
-fA Passive Analog Audio Monitor
@@ -112,7 +95,6 @@ Decoder options:
-fx Decode only X2-TDMA
-fy Decode only YSF
-fz Decode only M17*
-fU Decode only M17 UDP/IP Frame***
-fi Decode only NXDN48* (6.25 kHz) / IDAS*
-fn Decode only NXDN96* (12.5 kHz)
-fp Decode only ProVoice*
@@ -124,19 +106,19 @@ 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
-xz Expect inverted M17 signal
-xn Expect inverted NXDN/DCR/IDAS signal
* denotes frame types that cannot be auto-detected.
** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.
*** configure UDP Input with -i m17:127.0.0.1:17000
Advanced Decoder options:
-X <hex> Manually Set P2 Parameters (WACN, SYSID, CC/NAC)
(-X BEE00ABC123)
-_ <dec> Manually Set NXDN PN95 Dibit Scrambler Seed (1-511)
(-* 261)
-D <dec> Manually Set TIII DMR Location Area n bit len (0-10)(10 max)
(Value defaults to max n bit value for site model size)
(Setting 0 will show full Site ID, no area/subarea)
@@ -150,10 +132,9 @@ Advanced Decoder options:
Use this feature to allow MAC_SIGNAL even if CRC errors.
-F Relax DMR RAS/CRC CSBK/DATA Pass/Fail
Enabling on some systems could lead to bad channel assignments/site data decoding if bad or marginal signal
-F Relax M17 LSF/PKT CRC Error Checking
-b <dec> Manually Enter Basic Privacy Key (Decimal Value of Key Number)
(NOTE: This used to be the 'K' option!
(NOTE: This used to be the 'K' option!)
-H <hex> Manually Enter Hytera 10/32/64 Char Basic Privacy Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
@@ -161,29 +142,69 @@ Advanced Decoder options:
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-H <hex> Manually Enter AES-128 or AES-256 Hex Key (see example below)
-H <hex> Manually Enter AES-128, AES-256, or Kirisun 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)
-2 <hex> Manually Enter and Enforce TYT 16-bit BP Key Value (DMR) (Hex Value)
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) AP Hex Key (see example below)
-! <hex> Manually Enter and Enforce TYT Advanced Privacy (PC4) 128 or 256 bit AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-! '736B9A9C5645288B 243AD5CB8701EF8A'
-! '1111111111111111 1111111111111111 1111111111111111 1111111111111111'
-@ <hex> Manually Enter and Enforce Retevis Advanced Privacy (RC2) 128 or 256 bit AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-@ '736B9A9C5645288B 243AD5CB8701EF8A'
-@ '1122334455667788 99AABBCCDDEEFF11 1122334455667788 99AABBCCDDEEFF11'
-5 <hex> Manually Enter and Enforce TYT Enhanced Privacy (AES-128) EP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-5 '736B9A9C5645288B 243AD5CB8701EF8A'
-+ <hex> Manually Enter and Enforce Baofeng Advanced Privacy (PC5-128 or PC5-256) AP Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC'
-+ 'ABCDABCDABCDABCD ABDCDABCDABCDABC EF01EF01EF01EF01 EF01EF01EF01EF01'
-^ <hex> Manually Enter and Enforce Connect Systems 72-bit (9-byte) Extended Encryption Hex Key (see example below)
-^ 3BBE782C0430008271
-9 <dec> Manually Enter and Enforce Kenwood 15-bit Scrambler Key Value (DMR) (Dec Value)
-A <hex> Manually Enter and Enforce Anytone 16-bit BP Key Value (DMR) (Hex Value)
-$ <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
@@ -191,6 +212,10 @@ Advanced Decoder options:
-S 49:123456789ABC80
For Example, enter 49-bit Keystream (MBP 70) as:
-S 49:ED0AED4AED4AED4A
For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:
-S 49:11AA22BB33CC44
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).
@@ -203,6 +228,8 @@ Advanced Decoder options:
-0 Force RC4 Key over Missing PI header/LE Encryption Identifiers (DMR)
-M <hex> Force Entered Alg ID Value over Missing PI header/LE Encryption Identifiers (DMR)
-3 Disable DMR Late Entry Encryption Identifiers (VC6 Single Burst)
Note: Disable this if false positives on Voice ENC occur.
@@ -219,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)

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,25 +355,26 @@ typedef struct
int uvquality;
int inverted_x2tdma;
int inverted_dmr;
int inverted_nxdn;
int mod_threshold;
int ssize;
int msize;
int playfiles;
int m17encoder;
int m17encoderbrt;
int m17encoderpkt;
int m17decoderip;
int delay;
int use_cosine_filter;
int unmute_encrypted_p25;
int rtl_dev_index;
int rtl_gain_value;
int rtl_gain_actual;
int rtl_squelch_level;
int rtl_volume_multiplier;
int rtl_udp_port;
int rtl_bandwidth;
int rtl_started;
long int rtl_rms;
int rtltcp_enabled;
char rtltcp_hostname[1024];
int rtltcp_portno;
int monitor_input_audio;
int analog_only;
int pulse_raw_rate_in;
@@ -414,7 +417,6 @@ typedef struct
short int aggressive_framesync;
int frame_m17;
int inverted_m17;
FILE *symbolfile;
int call_alert;
@@ -431,12 +433,6 @@ typedef struct
int udp_portno;
char udp_hostname[1024];
//M17 UDP for IP frame output
int m17_use_ip; //if enabled, open UDP and broadcast IP frame
int m17_portno; //default is 17000
int m17_udp_sock; //actual UDP socket for M17 to send to
char m17_hostname[1024];
//tcp socket for SDR++, etc
int tcp_sockfd;
int tcp_portno;
@@ -643,7 +639,7 @@ typedef struct
unsigned long long int K2;
unsigned long long int K3;
unsigned long long int K4;
int M;
uint8_t forced_alg_id;
int menuopen;
//AES Key Segments
@@ -656,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;
@@ -879,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
@@ -898,6 +902,7 @@ typedef struct
uint8_t nxdn_sacch_frame_segcrc[4];
uint8_t nxdn_alias_block_number;
char nxdn_alias_block_segment[4][4][8];
uint16_t nxdn_pn95_seed;
//site/srv/cch info
char nxdn_location_category[14];
@@ -924,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;
@@ -952,15 +977,11 @@ typedef struct
//M17 Storage
uint8_t m17_lsf[360];
uint8_t m17_pkt[850];
uint8_t m17_pbc_ct; //pbc packet counter
uint8_t m17_str_dt; //stream contents
unsigned long long int m17_dst;
unsigned long long int m17_src;
uint8_t m17_can; //can value that was decoded from signal
int m17_can_en; //can value supplied to the encoding side
int m17_rate; //sampling rate for audio input
int m17_vox; //vox enabled via RMS value
char m17_dst_csd[20];
char m17_src_csd[20];
@@ -968,18 +989,22 @@ typedef struct
char m17_src_str[50];
char m17_dst_str[50];
uint8_t m17_meta[16]; //packed meta
uint8_t m17_enc; //enc type
uint8_t m17_enc_st; //scrambler or data subtye
int m17encoder_tx; //if TX (encode + decode) M17 Stream is enabled
int m17encoder_eot; //signal if we need to send the EOT frame
uint8_t m17_meta[16]; //packed meta
uint8_t m17_aes_iv[16]; //aes iv
uint8_t m17_enc; //enc type
uint8_t m17_enc_st; //scrambler or data subtye
char m17_text_string[1024];
char m17_gnss_string[1024];
char m17_data_string[1024];
char m17_meta_string[1024];
float m17_viterbi_err;
//misc str storage
char str50a[50];
char str50b[50];
char str50c[50];
char m17dat[50]; //user supplied m17 data input string
char m17sms[800]; //user supplied sms text string
//Codec2
#ifdef USE_CODEC2
@@ -991,6 +1016,9 @@ typedef struct
int tyt_ap;
int tyt_bp;
int tyt_ep;
// retrevis rc2
int retevis_ap;
void *rc2_context;
//kenwood scrambler on DMR with forced application
int ken_sc;
@@ -998,9 +1026,16 @@ typedef struct
//anytone bp
int any_bp;
//baofeng ap
int baofeng_ap;
//connect systems ee
int csi_ee;
//generic ks
int straight_ks;
int straight_mod;
int straight_discard;
uint8_t static_ks_bits[2][882];
int static_ks_counter[2];
@@ -1014,13 +1049,6 @@ typedef struct
//M17 Sync Patterns
#define M17_LSF "11113313"
#define M17_STR "33331131"
//alternating with last symbol opposite of first symbol of LSF
#define M17_PRE "31313131"
#define M17_PIV "13131313"
#define M17_PRE_LSF "3131313133331131" //Preamble + LSF
#define M17_PIV_LSF "1313131311113313" //Preamble + LSF
#define M17_BRT "31331111"
#define M17_PKT "13113333"
#define FUSION_SYNC "31111311313113131131"
#define INV_FUSION_SYNC "13333133131331313313"
@@ -1190,7 +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);
@@ -1204,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);
@@ -1244,13 +1271,6 @@ void processDSTAR_HD (dsd_opts * opts, dsd_state * state); //DSTAR Header
void processDSTAR_SD (dsd_opts * opts, dsd_state * state, uint8_t * sd); //DSTAR Slow Data
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
void processM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR)
void processM17PKT(dsd_opts * opts, dsd_state * state); //M17 (PKT)
void processM17LSF(dsd_opts * opts, dsd_state * state); //M17 (LSF)
void processM17IPF(dsd_opts * opts, dsd_state * state); //M17 (IPF)
void encodeM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR) encoder
void encodeM17BRT(dsd_opts * opts, dsd_state * state); //M17 (BRT) encoder
void encodeM17PKT(dsd_opts * opts, dsd_state * state); //M17 (PKT) encoder
void decodeM17PKT(dsd_opts * opts, dsd_state * state, uint8_t * input, int len); //M17 (PKT) decoder
void processP2(dsd_opts * opts, dsd_state * state); //P2
void processTSBK(dsd_opts * opts, dsd_state * state); //P25 Trunking Single Block
void processMPDU(dsd_opts * opts, dsd_state * state); //P25 Multi Block PDU (SAP 0x61 FMT 0x15 or 0x17 for Trunking Blocks)
@@ -1276,25 +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_descramble (uint8_t dibits[], int len);
//nxdn deinterleaving/depuncturing functions
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
//type-d 'idas' deinterleaving/depuncturing functions
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction);
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
//DCR Mode
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
//MT and Voice
void nxdn_pn95_dibit_scrambler(dsd_state * state, uint8_t * dibits, int len);
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
//Osmocom OP25 12 Rate Trellis Decoder (for NXDN, M17, YSF, etc)
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf);
//All-in-One NXDN Soft Decision Viterbi Function based on libM17
uint32_t nxdn_soft_decision_viterbi(uint8_t * bits, const uint16_t * interleave, uint8_t * puncture, int d_len, int p_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
//NXDN Conventional and Type-C
void nxdn_facch1(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t frame);
void nxdn_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_cac(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
//type-d 'idas'
void idas_scch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t direction);
void idas_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
//Japanese DCR
void dcr_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void dcr_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t lich);
//OP25 NXDN CRC functions
int load_i(const uint8_t val[], int len);
@@ -1303,15 +1323,18 @@ uint16_t crc12f(const uint8_t buf[], int len);
uint16_t crc15(const uint8_t buf[], int len);
uint16_t crc16cac(const uint8_t buf[], int len);
uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
uint32_t nxdn_message_crc32(uint8_t * input, int len);
/* NXDN Convolution functions */
void CNXDNConvolution_start(void);
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
void CNXDNConvolution_init();
//YSF Soft Decision Viterbi
uint32_t ysf_soft_decision_viterbi(uint8_t * dbuf, int d_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
//libM17 viterbi decoder
//libm17 magic soft decision based viterbi
#define SYM_PER_PLD 184
void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD]);
void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2]);
void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_PLD*2]);
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len);
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len);
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos);
@@ -1319,6 +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,
@@ -1327,16 +1353,29 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message);
char * NXDN_Call_Type_To_Str(uint8_t CallType);
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
void NXDN_Data_Call_Option_To_Str(uint8_t DataCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
//added these
void NXDN_decode_Prop(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_Alias16(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_sdcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
//Type-D SCCH Message Decoder
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
void NXDN_decode_VCALL_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
uint32_t sjis_char_to_unicode(uint16_t sjis);
uint32_t big5_char_to_unicode(uint16_t big5);
int utf8_encode(int c, char *buf);
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
@@ -1350,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);
@@ -1367,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);
@@ -1376,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
@@ -1398,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);
@@ -1487,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);
@@ -1609,9 +1658,9 @@ int udp_socket_connect(dsd_opts * opts, dsd_state * state);
int udp_socket_connectA(dsd_opts * opts, dsd_state * state);
void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
int m17_socket_receiver(dsd_opts * opts, void * data);
int udp_socket_connectM17(dsd_opts * opts, dsd_state * state);
int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
//15-bit Scrambler
void pdu_scrambler_keystream_creation(uint8_t * ks, int lfsr, int len);
//RC4 function prototypes
void rc4_voice_decrypt (int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[]);
@@ -1629,25 +1678,58 @@ void aes_cfb_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in,
void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
//Tytera Encryption Modes
//Tytera / Retevis / Anytone / Kenwood / 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);
//Misc Other Encryption Modes
void retevis_rc2_keystream_creation(dsd_state *state, char *input);
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input);
void anytone_bp_keystream_creation(dsd_state * state, char * input);
void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input);
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]);
void straight_mod_xor_keystream_creation(dsd_state * state, char * input);
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 LFSR and General Keystream Creator
uint32_t kirisun_lfsr(unsigned long long int mi);
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
@@ -1662,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
@@ -1671,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

147
include/pc5.h Normal file
View File

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

43
include/rc2.h Normal file
View File

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

View File

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

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
}

1242
src/crypt-kir.c Normal file

File diff suppressed because it is too large Load Diff

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

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

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

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

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

File diff suppressed because it is too large Load Diff

View File

@@ -159,28 +159,96 @@ void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int
void tyt_ap_pc4_keystream_creation(dsd_state * state, char * input)
{
unsigned char key1[16] = {0};
unsigned char key2[16] = {0};
unsigned char key1[64]; memset(key1, 0, sizeof(key1));
unsigned char key2[64]; memset(key2, 0, sizeof(key2));
char buf[1024];
strncpy(buf, input, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
char *pEnd;
uint64_t K1 = strtoull(buf, &pEnd, 16);
uint64_t K2 = strtoull(pEnd, &pEnd, 16);
uint64_t K3 = strtoull(pEnd, &pEnd, 16);
uint64_t K4 = strtoull(pEnd, &pEnd, 16);
if (K3 != 0 || K4 != 0) //256-bit keys loaded as ASCII characters
{
//start loading K1, K2, K3, and K4 as ASCII to fill 64 bytes of key1
int k = 0; uint8_t x = 0;
u64_to_bytes_be(K1, &key1[0]);
u64_to_bytes_be(K2, &key1[8]);
for (int i = 0; i < 16; i++)
{
x = ((K1 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
for (int i=0;i<16;i++) key2[i] = key1[15-i];
for (int i = 0; i < 16; i++)
{
x = ((K2 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
/* Create key schedule */
create_keys(&ctx, key2, sizeof(key2));
ctx.rounds = nbround;
for (int i = 0; i < 16; i++)
{
x = ((K3 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
fprintf (stderr,"DMR TYT AP (PC4) 128-bit Key %016llX%016llX with Forced Application\n", (unsigned long long int)K1, (unsigned long long int)K2);
state->tyt_ap = 1;
for (int i = 0; i < 16; i++)
{
x = ((K4 >> (60-(i*4))) & 0xF);
if (x >= 0 && x <= 9) //numbers 0-9
x += 0x30; //ASCII representation of numbers is 0x30, 0x31....0x39
else x += 0x37; //Upper ASCII A,B,C,D,E,F is 0x41, 0x42...0x46
key1[k++] = x;
}
//debug
// fprintf (stderr, "ASCII: ");
// for (int i = 0; i < 64; i++)
// fprintf (stderr, " %02X", key1[i]);
// fprintf (stderr, "\n");
/* Create key schedule */
create_keys(&ctx, key1, 64);
ctx.rounds = nbround;
fprintf(stderr, "DMR TYT AP (PC4) 256-bit Key %016llX%016llX%016llX%016llX with Forced Application\n",
(unsigned long long)K1, (unsigned long long)K2, (unsigned long long)K3, (unsigned long long)K4);
state->tyt_ap = 1;
}
else //128-bit loaded as reverse bytes
{
u64_to_bytes_be(K1, &key1[0]);
u64_to_bytes_be(K2, &key1[8]);
// reverse load the key bytes into key2
for (int i=0;i<16;i++)
key2[i] = key1[15-i];
/* Create key schedule */
create_keys(&ctx, key2, 16);
ctx.rounds = nbround;
fprintf (stderr,"DMR TYT AP (PC4) 128-bit Key %016llX%016llX with Forced Application\n",
(unsigned long long int)K1, (unsigned long long int)K2);
state->tyt_ap = 1;
}
}
@@ -238,4 +306,4 @@ void tyt_ep_aes_keystream_creation(dsd_state * state, char * input)
fprintf (stderr,"DMR TYT EP (AES-128) Key %016llX%016llX with Forced Application\n", K1, K2);
state->tyt_ep = 1;
}
}

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.
@@ -335,7 +346,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
//The SAP found here is the actual SAP of the message (like a P25 ndary SAP, and can chain together according to ETSI)
fprintf (stderr, " - SAP %02d [%s] - MFID %02X [%s]", p_sap, sap_string, p_mfid, mfid_string);
//p_sap 1 on mfid 10 (moto) has been observed as the first block of LRRP data (unique fixed format?)
//p_sap 1 on mfid 10 MNIS Header
if (p_mfid == 0x10 && p_sap == 1)
{
//add the header to the first 10 bytes of the storage (sans this header's CRC)
@@ -365,7 +376,81 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
}
//Start Setting DMR Data Packet Encryption Variables
if (p_sap != 1 && p_mfid == 0x10)
if (p_sap != 1 && p_mfid == 0x77)
{
fprintf (stderr, "\n Vertex Standard PDU ENC Header;");
//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]
//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;
//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;
//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)
{
//check ENC bit, assuming this is an ENC bit, or SVC OPT like thing (or could be an opcode for the rest of the extended header)
@@ -376,7 +461,7 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
else state->dmr_soR = 0x100;
}
fprintf (stderr, "\n PDU ENC Header:");
fprintf (stderr, "\n DMRA PDU ENC Header:");
fprintf (stderr, " MFID: %02X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8));
fprintf (stderr, " ENC: %X;", (uint8_t)ConvertBitIntoBytes(&dheader_bits[20], 4));
@@ -993,15 +1078,30 @@ 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 still not akl, see if there is anything manually loaded into K1, K2, K3, K4 instead
if (akl == 0)
{
for (i = 0; i < 8; i++)
{
kaes[i+0] = (state->K1) >> (56-(i*8)) & 0xFF;
kaes[i+8] = (state->K2) >> (56-(i*8)) & 0xFF;
kaes[i+16] = (state->K3) >> (56-(i*8)) & 0xFF;
kaes[i+24] = (state->K4) >> (56-(i*8)) & 0xFF;
}
//if kaes is loaded with a key, then flag on the key loaded variable
if (memcmp(kaes, empt, sizeof(kaes)) != 0) akl = 1;
@@ -1022,19 +1122,27 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
//print alg/key and value if loaded
fprintf (stderr, "\n PDU ALG: %02X; Key ID: %02X;", alg, kid);
if (alg != 0) fprintf (stderr, " MI(32): %08llX;", mi);
if (alg != 0 && mi != 0) fprintf (stderr, " MI(32): %08llX;", mi);
if (alg == 0) fprintf (stderr, " Moto BP;");
if (alg == 1) fprintf (stderr, " RC4;");
if (alg == 2) fprintf (stderr, " DES;");
if (alg == 4) fprintf (stderr, " AES128;");
if (alg == 5) fprintf (stderr, " AES256;");
if (alg == 7) fprintf (stderr, " VTX STD;");
if (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
//expand 32-bit MI to 128-bit IV for AES mode data decryption
//we only want to do this at the moment of keystream generation
if (alg == 5)
if (alg == 4 || alg == 5)
{
//for the LFSR128d function to show the correct alg
if (state->currentslot == 0)
state->payload_algid += 0x20;
else state->payload_algidR += 0x20;
fprintf (stderr, "\n");
LFSR128d(state);
if (state->currentslot == 0)
@@ -1049,30 +1157,113 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
decrypted_pdu = 1;
}
if (alg == 5 && akl == 1) //AES-256
else if (alg == 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;
aes_ofb_keystream_output (maes, kaes, ob, 2, nblocks);
decrypted_pdu = 1;
}
else if (alg == 5 && akl == 1 && mi == 0) //AES-256 ECB (CCR without an IV)
{
//void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de)
int nblocks = (state->data_byte_ctr[slot] / 16) + 0; //was +1
for (i = 0; i < nblocks; i++)
{
aes_ecb_bytewise_payload_crypt(state->dmr_pdu_sf[slot]+start, kaes, state->dmr_pdu_sf[slot]+start, 2, 0);
start += 16;
}
decrypted_pdu = 1;
}
//other algs available, not sure if others will be used in any DMRA offerings
if (alg == 2 && R != 0) //Tait DES
else if (alg == 2 && R != 0) //Tait DES
{
int nblocks = (state->data_byte_ctr[slot] / 8) + 1;
des_multi_keystream_output (mi, R, ob, 1, nblocks);
decrypted_pdu = 1;
}
if (alg == 4 && akl == 1) //AES-128
else if (alg == 4 && akl == 1 && mi != 0) //AES-128 OFB
{
int nblocks = (state->data_byte_ctr[slot] / 16) + 1;
aes_ofb_keystream_output (maes, kaes, ob, 0, nblocks);
decrypted_pdu = 1;
}
else if (alg == 4 && akl == 1 && mi == 0) //AES-128 ECB (CCR without an IV)
{
//void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de)
int nblocks = (state->data_byte_ctr[slot] / 16) + 0; //was +1
for (i = 0; i < nblocks; i++)
{
aes_ecb_bytewise_payload_crypt(state->dmr_pdu_sf[slot]+start, kaes, state->dmr_pdu_sf[slot]+start, 0, 0);
start += 16;
}
decrypted_pdu = 1;
}
//NOTE: Observed that keystream should not be applied to pad bytes or CRC
//apply keystream here, only if alg is 1 or 4 AND key is available!
//apply keystream here, only if key is available and keystream created, not ECB.
if (alg == 1 && R != 0)
{
for (i = 0; i < end; i++)
@@ -1084,15 +1275,17 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
for (i = 0; i < end; i++)
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
}
else if (alg == 4 && akl != 0)
else if (alg == 4 && akl != 0 && mi != 0)
{
for (i = 0; i < end; i++)
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
state->dmr_pdu_sf[slot][i+start] ^= ob[(i+16)%3096];
}
else if (alg == 5 && akl != 0)
else if (alg == 5 && akl != 0 && mi != 0)
{
for (i = 0; i < end; i++)
state->dmr_pdu_sf[slot][i+start] ^= ob[i%3096];
state->dmr_pdu_sf[slot][i+start] ^= ob[(i+16)%3096];
}
//BP key application
@@ -1174,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
@@ -1512,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);
@@ -502,6 +514,20 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
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);
@@ -695,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)
@@ -706,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);
}
@@ -954,6 +980,20 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
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);
@@ -1055,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;
@@ -72,6 +73,71 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
so ^= 0x40;
}
//Kirisun
if (*IrrecoverableErrors == 0 && CRCCorrect == 1 && fid == 0x0A) //&& (so & 0x40) == 0x40
{
//disable late entry for DMRA, Flag 3 if encrypted, 0 if not
if (so & 0x40)
opts->dmr_le = 3;
else opts->dmr_le = 0;
}
//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
if (*IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68)
{
@@ -108,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) )
@@ -117,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);
@@ -131,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);
@@ -193,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
@@ -310,8 +408,8 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
}
//unknown other manufacturer or OTA ENC, etc.
//removed tait from the list, added hytera 0x08
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && is_kenwood_sc == 0)
//removed tait from the list, added hytera 0x08, added Kirisun and KW Scrambler LC
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08 && fid != 0x0A && is_kenwood_sc == 0)
{
if (type == 1) fprintf (stderr, "%s \n", KYEL);
if (type == 2) fprintf (stderr, "%s \n", KYEL);
@@ -444,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)
{
@@ -464,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;
}
}
@@ -491,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","");
@@ -519,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 ");
@@ -551,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++)
@@ -654,6 +764,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
}
//should rework this back into the upper portion
if (fid == 0x0A) fprintf (stderr, "Kirisun ");
if (fid == 0x68) fprintf (stderr, "Hytera ");
if (is_xpt) fprintf (stderr, "XPT ");
if (fid == 0x68 && flco == 0x00)
@@ -1181,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,29 +8,15 @@
#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])
{
uint8_t slot = state->currentslot;
//enforce RC4 due to missing PI header, but with valid SVC Opts
//ideally, this would be handled by VC-F single burst, but its not fully reliable compared to this
//due to multiple signalling occurring inside of it, depending on system type
if (state->M == 0x21)
{
if (slot == 0 && state->dmr_so & 0x40)
{
state->payload_algid = 0x21;
state->payload_keyid = 0xFF;
}
if (slot == 1 && state->dmr_soR & 0x40)
{
state->payload_algidR = 0x21;
state->payload_keyidR = 0xFF;
}
}
//collect our fragments and place them into storage
state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
@@ -103,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)
{
@@ -133,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)
{
@@ -236,6 +311,40 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s\n", KNRM);
}
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;", alg, state->payload_keyid);
fprintf (stderr, " MI(32): %08llX;", state->payload_mi);
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);
}
}
if (state->currentslot == 1)
{
@@ -266,6 +375,40 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s\n", KNRM);
}
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;", alg, state->payload_keyidR);
fprintf (stderr, " MI(32): %08llX;", state->payload_miR);
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);
}
}
}
@@ -314,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
@@ -321,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];
@@ -390,6 +570,56 @@ 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
if (opts->dmr_le == 3)
{
if (irr_err != 0)
{
uint32_t sbrcpl = 0;
for(i = 0; i < 32; i++)
{
sbrcpl = sbrcpl << 1;
sbrcpl |= sbrc_interleaved[i] & 1;
}
if (opts->payload == 0) fprintf (stderr, "\n");
fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
}
else if (irr_err == 0 && sbrc_hex != 0) //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");
//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 (opts->dmr_le == 1)
{
if (irr_err != 0)
@@ -466,7 +696,7 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
if (state->dmr_so & 0x40 && key != 0 && alg != 0)
{
//if we aren't forcing a particular alg or privacy key set
if (state->M == 0)
if (state->forced_alg_id == 0)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
@@ -476,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)
@@ -492,12 +723,17 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
if (state->dmr_soR & 0x40 && key != 0 && alg != 0)
{
//if we aren't forcing a particular alg or privacy key set
if (state->M == 0)
if (state->forced_alg_id == 0)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
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

@@ -253,6 +253,20 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
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);
@@ -564,6 +578,20 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
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);
}
@@ -482,6 +487,29 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
sprintf (state->dmr_lrrp_gps[slot], "ETSI TMS SRC: %d; DST: %d; ", src24, dst24);
utf16_to_text(state, 1, len, input+28);
}
//Vertex Standard TMS Port
else if (port1 == 5007 && port2 == 5007)
{
fprintf (stderr, "VTX STD TMS; ");
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; ", tmslen);
fprintf (stderr, "??: ");
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, tmslen*2, input+37);
}
else if (port1 == 5017 && port2 == 5017)
{
//sanity check
@@ -493,6 +521,12 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
lip_protocol_decoder(opts, state, bits);
}
//known P25 Ports
else if (port1 == 9361 && port2 == 9361)
{
sprintf (state->dmr_lrrp_gps[slot], "P25 Atlas SRC(IP): %d.%d.%d.%d; DST(IP): %d.%d.%d.%d; ",
input[12], input[13], input[14], input[15], input[16], input[17], input[18], input[19]);
fprintf (stderr, "Atlas Data Registration Server;"); //EF Johnson Atlas
}
else if (port1 == 49198 && port2 == 49198)
{
sprintf (state->dmr_lrrp_gps[slot], "P25 Tier 2 LOCN SRC(IP): %d.%d.%d.%d; DST(IP): %d.%d.%d.%d; ",
@@ -967,8 +1001,9 @@ void dmr_locn (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * DMR_P
//write to LRRP file
if (opts->lrrp_file_output == 1)
{
char * timestr = getTimeC();
char * datestr = getDateS();
char * timestr = getTimeC();
//open file by name that is supplied in the ncurses terminal, or cli
FILE * pFile; //file pointer

View File

@@ -12,7 +12,6 @@
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
{
UNUSED2(opts, CRCCorrect);
uint8_t MFID = PI_BYTE[1];
@@ -26,6 +25,74 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->last_cc_sync_time = time(NULL);
}
if (MFID == 0x0A && CRCCorrect == 1) //Kirisun
{
//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
//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 = 0x40;
state->payload_algid = alg;
state->payload_keyid = kid;
state->payload_mi = mi;
}
else
{
state->dmr_soR = 0x40;
state->payload_algidR = alg;
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; TGT: %d; KEY ID: %02X; MI(32): %08X;", alg, target, kid, mi);
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", KNRM);
//flag late entry SB for Kirisun
opts->dmr_le = 3;
}
if (MFID == 0x68) //Hytera Enhanced
{
if (state->currentslot == 0)
@@ -96,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");
@@ -132,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
@@ -149,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;
@@ -199,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);
@@ -217,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;
@@ -324,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)
@@ -423,13 +530,13 @@ unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len)
{
uint8_t bit = (mi[i] >> 7) & 1;
mi[i] <<= 1;
if (bit) mi[i] ^= taps[i%5];
if (bit) mi[i] ^= taps[i%len];
mi[i] |= bit;
}
unsigned long long int mi_value = 0;
for (uint8_t i = 0; i < 5; i++)
for (uint8_t i = 0; i < len; i++)
{
mi_value <<= 8;
mi_value |= mi[i];
@@ -470,4 +577,38 @@ void hytera_enhanced_alg_refresh(dsd_state * state)
if (state->currentslot == 0)
state->payload_mi = mi_value;
else state->payload_miR = mi_value;
}
}
uint32_t kirisun_lfsr(unsigned long long int mi)
{
uint32_t taps = 0xD459C4F1;
uint32_t lfsr = (uint32_t)mi;
uint32_t new_mi = 0;
for (int i = 0; i < 4; i++)
{
uint8_t byte = 0;
for (int j = 0; j < 8; j++)
{
uint32_t temp = (lfsr << 1);
uint8_t msb = (lfsr >> 31) & 1;
if (msb)
{
byte |= (1 << j);
lfsr = temp ^ taps;
}
else lfsr = temp; //was previously temp ^ 1, but that isn't a primitive
}
new_mi <<=8;
new_mi |= byte;
}
return new_mi;
}

View File

@@ -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
@@ -794,22 +798,18 @@ void dmr_talker_alias_lc_decode (dsd_opts * opts, dsd_state * state, uint8_t slo
}
else if (char_size == 16)
{
int ptr = 0;
setlocale(LC_ALL, ""); //needed when encoded alias contains Chinese (or probably any non-roman charset that isn't default on users terminal)
for (i = 0; i < end; i++)
{
uint16_t character = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][(i*16)], 16);
char ch[2]; ch[0] = character & 0xFF; ch[1] = 0;
if (character >= 0x20 && character != 0x7F)
fprintf (stderr, "%lc", character);
else fprintf (stderr, " ");
if (character == 0)
strcat (alias_string, " ");
else if (character >= 0x20 && character <= 0xFE)
strcat (alias_string, ch);
else
strcat (alias_string, "*");
//Encode to UTF-8
ptr += utf8_encode(character, alias_string+ptr);
//debug
// fprintf (stderr, " [%04X], ", character);

View File

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

View File

@@ -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)
{
@@ -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)
{
@@ -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
@@ -723,6 +761,21 @@ void playSynthesizedVoiceFM (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_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)
{
@@ -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)
{
@@ -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);
}
@@ -695,7 +695,7 @@ void watchdog_event_current (dsd_opts * opts, dsd_state * state, uint8_t slot)
if (state->ea_mode == 1)
{
sprintf (event_string, "%s %s %s TGT: %07d; SRC: %07d; LCN: %02d; SITE: %d:%d.%04X; %s;", datestr, timestr, sys_string, target_id, source_id, channel, sys_id1, sys_id2, sys_id3, sup_str);
sprintf (event_string, "%s %s %s TGT: %07d; SRC: %07d; LCN: %02d; SITE: %d:%d.%04X; %s; ", datestr, timestr, sys_string, target_id, source_id, channel, sys_id1, sys_id2, sys_id3, sup_str);
}
else
{

View File

@@ -18,6 +18,7 @@
#include "dsd.h"
#include "p25p1_const.h" //for imbe fr (7200)
#include "dmr_const.h" //for ambe+2 fr
#include "bp.h" //for bp key table
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
{
@@ -501,7 +502,7 @@ SNDFILE * close_wav_file(SNDFILE * wav_file)
return wav_file;
}
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, Event_History_I * event_struct)
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, char * custom_tag, Event_History_I * event_struct)
{
sf_close(wav_file);
@@ -537,11 +538,46 @@ SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename,
char new_filename[2000];
memset (new_filename, 0, sizeof(new_filename));
//parse any possible custom tags or aliasing (up to 3)
char custom_string_temp[100];
memset(custom_string_temp, 0, sizeof(custom_string_temp));
memcpy(custom_string_temp, custom_tag, sizeof(custom_string_temp));
char * token = strtok(custom_string_temp, ":");
char tag[3][25];
memset(tag, 0, sizeof(tag));
sprintf(tag[0], "%s", "");
sprintf(tag[1], "%s", "");
sprintf(tag[2], "%s", "");
for (int i = 0; i < 3; i++)
{
if (token != NULL)
{
tag[i][0] = '_';
strncpy(tag[i]+1, token, 23);
tag[i][24] = '\0';
token = strtok(NULL, ":");
//look for _TGT_, convert to strings
if (strncmp("_TGT_", tag[i], 5) == 0)
{
sprintf (tgt_str, "%s", tag[i]+5);
sprintf (src_str, "%d", source_id);
is_string = 1;
memset(tag[i], 0, sizeof(tag[i]));
sprintf(tag[i], "%s", "");
}
}
else break;
}
//check for String based TGT and SRC values (M17, YSF, DSTAR)
if (is_string == 1)
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tgt_str, src_str);
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s%s%s%s_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tag[0], tag[1], tag[2], tgt_str, src_str);
else //is a numerical value
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s_TGT_%d_SRC_%d.wav", dir, datestr, timestr, random_number, sys_str, gi_str, target_id, source_id);
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s%s%s%s_TGT_%d_SRC_%d.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tag[0], tag[1], tag[2], target_id, source_id);
if (timestr != NULL)
{
@@ -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)
{
@@ -262,21 +260,10 @@ processFrame (dsd_opts * opts, dsd_state * state)
processYSF(opts, state);
return;
}
//M17
else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) ||
(state->synctype == 76) || (state->synctype == 77) || (state->synctype == 86) || (state->synctype == 87) ||
(state->synctype == 99) || (state->synctype == 98) )
//M17 STR
else if (state->synctype == 16)
{
if (state->synctype == 98 || state->synctype == 99) //preamble only
skipDibit(opts, state, 8); //skip dibits to prime the demodulator
else if (state->synctype == 16 || state->synctype == 17)
processM17STR(opts, state);
else if (state->synctype == 76 || state->synctype == 77) {} //Not available yet
// processM17BRT(opts, state); //Not available yet
else if (state->synctype == 86 || state->synctype == 87)
processM17PKT(opts, state);
else
processM17LSF(opts, state);
processM17STR(opts, state);
return;
}
//P25 P2

View File

@@ -16,6 +16,7 @@
*/
#include "dsd.h"
#include <limits.h>
#include <locale.h>
void
@@ -162,10 +163,9 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
char synctest20[21]; //YSF
char synctest48[49]; //EDACS
char synctest8[9]; //M17
char synctest16[17]; //M17 Preamble
char modulation[8];
char *synctest_p;
char synctest_buf[10240]; //what actually is assigned to this, can't find its use anywhere?
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)
@@ -198,25 +198,23 @@ 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;
synctest10[10] = 0;
synctest[24] = 0;
synctest8[8] = 0; //M17, wasn't initialized or terminated (source of much pain and frustration in Cygwin)
synctest8[8] = 0;
synctest12[12] = 0;
synctest16[16] = 0; //M17, wasn't initialized or terminated (source of much pain and frustration in Cygwin)
synctest48[48] = 0;
synctest32[32] = 0;
synctest20[20] = 0;
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;
@@ -376,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)
{
@@ -566,139 +589,24 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
//end YSF sync
//M17 Sync -- Just STR and LSF for now
strncpy(synctest16, (synctest_p - 15), 16);
//M17 Sync -- Just STR
strncpy(synctest8, (synctest_p - 7), 8);
if(opts->frame_m17 == 1)
{
//preambles will skip dibits in an attempt to prime the
//demodulator but not attempt any decoding
if (strcmp(synctest8, M17_PRE) == 0)
if (strcmp(synctest8, M17_STR) == 0)
{
if (opts->inverted_m17 == 0)
printFrameSync (opts, state, "+M17 STR", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 16)
{
printFrameSync (opts, state, "+M17 PREAMBLE", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->lastsynctype = 98;
fprintf (stderr, "\n");
return (98);
}
}
else if (strcmp(synctest8, M17_PIV) == 0)
{
if (opts->inverted_m17 == 1)
{
printFrameSync (opts, state, "-M17 PREAMBLE", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->lastsynctype = 99;
fprintf (stderr, "\n");
return (99);
}
}
else if (strcmp(synctest8, M17_PKT) == 0)
{
if (opts->inverted_m17 == 0)
{
printFrameSync (opts, state, "+M17 PKT", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 86 || state->lastsynctype == 8)
{
state->lastsynctype = 86;
return (86);
}
state->lastsynctype = 86;
fprintf (stderr, "\n");
}
// else //unknown, -BRT?
// {
// printFrameSync (opts, state, "-M17 BRT", synctest_pos + 1, modulation);
// state->carrier = 1;
// state->offset = synctest_pos;
// state->max = ((state->max) + lmax) / 2;
// state->min = ((state->min) + lmin) / 2;
// if (state->lastsynctype == 77)
// {
// state->lastsynctype = 77;
// return (77);
// }
// state->lastsynctype = 77;
// fprintf (stderr, "\n");
// }
}
else if (strcmp(synctest8, M17_STR) == 0)
{
if (opts->inverted_m17 == 0)
{
printFrameSync (opts, state, "+M17 STR", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 16 || state->lastsynctype == 8)
{
state->lastsynctype = 16;
return (16);
}
state->lastsynctype = 16;
fprintf (stderr, "\n");
}
else
{
printFrameSync (opts, state, "-M17 LSF", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 99)
{
state->lastsynctype = 9;
return (9);
}
state->lastsynctype = 9;
fprintf (stderr, "\n");
}
}
else if (strcmp(synctest8, M17_LSF) == 0)
{
if (opts->inverted_m17 == 1)
{
printFrameSync (opts, state, "-M17 STR", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 17 || state->lastsynctype == 9)
{
state->lastsynctype = 17;
return (17);
}
state->lastsynctype = 17;
fprintf (stderr, "\n");
}
else
{
printFrameSync (opts, state, "+M17 LSF", synctest_pos + 1, modulation);
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 98)
{
state->lastsynctype = 8;
return (8);
}
state->lastsynctype = 8;
fprintf (stderr, "\n");
return (16);
}
state->lastsynctype = 16;
fprintf (stderr, "\n");
}
}
//end M17
@@ -1257,8 +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
@@ -1272,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);
}
@@ -1294,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);
@@ -1304,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
@@ -1412,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;

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -130,11 +130,13 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
closePulseOutput (opts);
}
#ifndef __APPLE__
//close OSS output
if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
{
close (opts->audio_out_fd);
}
#endif
if (opts->audio_in_type == 0) //close pulse input if it is the specified input method
{
@@ -692,8 +694,8 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
if (choicec == 12)
{
//TODO: Add Closing of RAW files as well?
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling
opts->wav_out_fileR[0] = 0;
opts->dmr_stereo_wav = 0;
@@ -921,11 +923,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
//toggle enforcement of basic privacy key over enc bit set on traffic
if (option == 4)
{
if (state->M == 1 || state->M == 0x21)
if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21)
{
state->M = 0;
state->forced_alg_id = 0;
}
else state->M = 1;
else state->forced_alg_id = 1;
}
if (option == 5)
@@ -951,11 +953,11 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
//toggle enforcement of rc4 key over missing pi header/le on DMR
// if (option == 6)
// {
// if (state->M == 1 || state->M == 0x21)
// if (state->forced_alg_id == 1 || state->forced_alg_id == 0x21)
// {
// state->M = 0;
// state->forced_alg_id = 0;
// }
// else state->M = 0x21;
// else state->forced_alg_id = 0x21;
// }
//load AES keys
@@ -1415,7 +1417,6 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
opts->inverted_dpmr = 1;
opts->inverted_x2tdma = 1;
opts->inverted_ysf = 1;
opts->inverted_m17 = 1;
}
else
{
@@ -1423,7 +1424,6 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
opts->inverted_dpmr = 0;
opts->inverted_x2tdma = 0;
opts->inverted_ysf = 0;
opts->inverted_m17 = 0;
}
}
@@ -1587,10 +1587,12 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
openPulseOutput (opts);
}
#ifndef __APPLE__
if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
{
openOSSOutput (opts);
}
#endif
if (opts->audio_in_type == 0) //reopen pulse input if it is the specified input method

View File

@@ -223,9 +223,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("| Symbol Float Input: %s \n", opts->audio_in_dev);
}
if (opts->m17decoderip == 1 && opts->udp_sockfd)
printw ("| M17 UDP IP Frame Input: %s:%d \n", opts->m17_hostname, opts->m17_portno);
if (opts->audio_in_type == 8)
{
printw ("| TCP Signal Input: %s:%d; %d kHz; 1 Ch; ", opts->tcp_hostname, opts->tcp_portno, opts->wav_sample_rate/1000);
@@ -247,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);
@@ -336,25 +335,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
}
if (opts->m17encoder == 1)
{
printw ("| M17 Encoder:");
if (state->m17encoder_tx == 1 && state->m17_vox == 0) printw (" Toggle TX (\\) ON ;");
if (state->m17encoder_tx == 0 && state->m17_vox == 0) printw (" Toggle TX (\\) OFF;");
if (state->m17_vox == 1) printw (" Vox Mode;");
printw (" Input Gain (/|*): %02.0f%% ", opts->audio_gainA);
if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |");
if (opts->use_hpf == 1) printw ("HP|"); else printw (" |");
if (opts->use_pbf == 1) printw ("PB|"); else printw (" |");
if (opts->audio_in_type != 3 && state->m17_vox == 1) printw ( " SQL: %04ld : %04d;", opts->rtl_rms, opts->rtl_squelch_level);
printw ("\n");
}
if (opts->m17_use_ip == 1)
printw ("| M17 UDP IP Frame Output: %s:%d \n", opts->m17_hostname, opts->m17_portno);
if (opts->mbe_out_dir[0] != 0 && opts->dmr_stereo == 0)
{
printw ("| Writing MBE data files to directory %s\n", opts->mbe_out_dir);
@@ -385,7 +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)");
@@ -404,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)");
@@ -517,18 +497,18 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
#endif
// if (opts->aggressive_framesync == 0) printw ("| Selective CRC ERR Bypass Enabled (RAS) \n");
if (state->M == 1)
if (state->forced_alg_id == 1)
{
if (state->R != 0) printw ("| Forcing Key Priority -- NXDN Sc Key: %05lld \n", state->R);
if (state->K != 0) printw ("| Forcing Key Priority -- Moto BP Key: %03lld \n", state->K);
if (state->K1 != 0) printw ("| Forcing Key Priority -- Hytera BP Key: %016llX \n", state->K1);
if (state->K != 0 && state->K1 != 0) printw ("| Warning! Multiple DMR Key Types Loaded! \n"); //warning may not be required
}
if (state->M == 0x21)
if (state->forced_alg_id == 0x21)
{
if (state->R != 0) printw ("| Forcing Key Priority -- RC4 Key: %010llX \n", state->R);
}
if (state->M == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H);
if (state->forced_alg_id == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H);
if (opts->scanner_mode == 1)
{
@@ -561,7 +541,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->mod_c4fm == 1) printw ("[C4FM]");
if (opts->mod_gfsk == 1) printw ("[GFSK]");
printw ( "[%d] \n", (48000*opts->wav_interpolator)/state->samplesPerSymbol);
if (opts->m17encoder == 1) printw ("| Encoding: [%s] \n", opts->output_name);
printw ("| Decoding: [%s] ", opts->output_name);
if (opts->aggressive_framesync == 0) printw ("CRC/(RAS) ");
//debug -- troubleshoot voice tuning after grant on DMR CC, subsequent grant may not tune because tuner isn't available
@@ -654,20 +633,22 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("\n");
printw ("| ");
//fill in any extra info, like Meta (IV, etc)
if (state->m17_enc == 1)
{
printw (" Scrambler - Type: %d", state->m17_enc_st);
attron(COLOR_PAIR(1));
printw ("Scrambler - Type: %d", state->m17_enc_st);
attron(COLOR_PAIR(3));
}
if (state->m17_enc == 2)
{
attron(COLOR_PAIR(1));
printw ("AES-CTR - IV: ");
//display packed meta as IV
printw ("AES-CTR IV: ");
//display AES IV
for (i = 0; i < 16; i++)
printw ("%02X", state->m17_meta[i]);
printw ("%02X", state->m17_aes_iv[i]);
attron(COLOR_PAIR(3));
}
if (state->m17_enc == 3)
@@ -676,6 +657,30 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
printw ("\n");
printw ("| ");
//decoded elements from stream
char shortm17[72];
memset(shortm17, 0, sizeof(shortm17));
strncpy (shortm17, state->m17_text_string, 70);
shortm17[71] = 0;
printw ("META TEXT: %s", shortm17);
printw ("\n");
printw ("| ");
memset(shortm17, 0, sizeof(shortm17));
strncpy (shortm17, state->m17_gnss_string, 70);
shortm17[71] = 0;
printw ("META GNSS: %s", shortm17);
printw ("\n");
printw ("| ");
memset(shortm17, 0, sizeof(shortm17));
strncpy (shortm17, state->m17_data_string, 70);
shortm17[71] = 0;
printw ("MISC DATA: %s", shortm17);
printw ("\n");
}
@@ -918,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
{
@@ -1101,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));
@@ -1109,6 +1127,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw(" Key: %010llX", state->R);
attron(COLOR_PAIR(3));
}
if (state->payload_algid >= 0x31 && state->payload_algid <= 0x3A)
{
attron(COLOR_PAIR(1));
printw("Kirisun Encryption ");
if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); }
attron(COLOR_PAIR(3));
}
printw ("\n");
@@ -1276,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));
@@ -1284,6 +1316,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw(" Key: %010llX", state->RR);
attron(COLOR_PAIR(3));
}
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));
}
printw ("\n");

View File

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

View File

@@ -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);
}
}
@@ -2299,6 +2299,10 @@ void eot_cc(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "EOT; \n");
//skip an arbitrary number of dibits so that the CC can cancel the grant after a dotting sequence
//prior to returning to the control channel and getting sent in a loopback to the call channel
skipDibit(opts, state, 240*8);
//watchdog event at this point
state->lastsynctype = 38;
watchdog_event_history(opts, state, 0);
@@ -2308,7 +2312,7 @@ void eot_cc(dsd_opts * opts, dsd_state * state)
if (opts->dmr_stereo_wav == 1)
{
if (opts->wav_out_f != NULL)
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
}

View File

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

3918
src/m17.c

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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,10 +1043,11 @@ 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);
else if (opts->analog_only == 1 || opts->m17encoder == 1)
else if (opts->analog_only == 1)
demod_init_analog(&demod);
else demod_init(&demod);
output_init(&output);
@@ -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);
}

4632
src/sjis_unicode.c Normal file

File diff suppressed because it is too large Load Diff

541
src/soft_viterbi_k5.c Normal file
View File

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

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