225 Commits
2025 ... 2026

Author SHA1 Message Date
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
lwvmobile
9acd8c981e Merge pull request #307 from arancormonk/fix/nxdn-cac-temp-buffer-overflow
Fix NXDN CAC decoder temp buffer overflow in nxdn_deperm_cac causing segfaults under -O2/-O3
2025-09-02 22:10:42 -04:00
lwvmobile
9353c489aa Merge pull request #306 from arancormonk/fix/p25p2-tdma-voice-vector-oob
Fix OOB writes in ez.cpp HB/HBS vectors causing P25p2 TDMA voice segfault
2025-09-02 21:22:54 -04:00
arancormonk
bc95dc1a1d Fix NXDN CAC decoder temp buffer overflow in nxdn_deperm_cac causing segfaults under -O2/-O3. 2025-09-02 15:53:26 -04:00
arancormonk
1ecd32e017 Fix OOB writes in ez.cpp HB/HBS vectors causing P25p2 TDMA voice segfault 2025-09-02 15:07:51 -04:00
lwvmobile
6ef823347b Fix Bug with -F switch requiring an argument;
Bug was introduced in commitc 888dfc1d6569c42aa45ca86aad00ed630410ddd ;
2025-08-30 04:31:21 -04:00
lwvmobile
bd75c1ab83 MBE: Prepare for changes to P25p1 ESS in SDRTrunk .mbe files (version, ks application); #298
MBE: Make tweaks to audio and wav file handling, allow writing to static wav files even if audio out device is NULL (writeSynthesizedVoiceMS);

DSTAR: Fix Issue with DSTAR where double writes would occur in single static .wav files;
2025-08-23 20:31:32 -04:00
lwvmobile
b3b943d330 Update Docs; 2025-08-22 14:27:13 -04:00
lwvmobile
a5151686ec Add Generic Static AMBE Keystream Support; #303
Adding a new method to allow a user to enter a generic keystream len and static keystream via a string that is loaded into a bit array to xor against ambe frames;

This method is intended to be used to allow
2025-08-22 14:02:09 -04:00
lwvmobile
24c1dd97df DMR: Add 16-bit Anytone BP Support; #303
Reset static_ks_counter as various places in code;

Remove the static_ks_counter reset from KWD SCR link control and tweak SO bits;

Fix Forced KS application to work in both slots;

Allow Static Wav File or Per Call Wav File in SDRTrunk MBE format playback; #298

Same as above for .amb, .dmb, and .imb format playback;

Update Usage and Docs;
2025-08-20 23:12:57 -04:00
lwvmobile
6f171620dc DMR: Reorganize TYT and KEN encrytion modes, putting them into the proper files; #302 #303 2025-08-19 19:18:01 -04:00
lwvmobile
7428a87d80 WAV: Fix Issues with Per Call;
Fix Per Call Issue introduced with commit re-enabling single static wav file where single channel audio decode types would not write to per call temp wav files and be deleted after event history cycled them;

Fix Issue with YSF where double writes would occur in .wav files;

Fix Issue with M17 where incorrect rate audio would write to static .wav file by converting to stereo output;

Seperate Per Call and Create Method to Allow EDACS Analog Calls to be written to static wav file by downsampling and stereo interleaving audio to proper spec;

Issues were introduced in commit # c7b25c60af
2025-08-19 02:35:17 -04:00
lwvmobile
79084f26c6 DMR: Fix Kenwood Scrambler SVC OPT muting call when key is forced; #303 2025-08-17 21:17:14 -04:00
lwvmobile
d5cd4b4273 DMR: Fix Kenwood Scrambler Flag in FLC #303; 2025-08-17 21:11:34 -04:00
lwvmobile
bafd74b225 DMR: Fix Wformat Warning; #303 2025-08-17 20:06:34 -04:00
lwvmobile
8df8ce85f2 DMR: Add 15-bit scrambler method for Kenwood Radios; #303 2025-08-17 18:57:32 -04:00
lwvmobile
a5b9984d78 TYT EP: Fix User Key to Input Register; #302
Bytes used as input register for TYT EP are loaded by byte reverse order, was undetected on some key patterns like all F's or all A's, but found out on 1234... patterns;
2025-08-17 15:24:22 -04:00
lwvmobile
4dd68f2221 Update Help / Usage; 2025-08-16 10:25:43 -04:00
lwvmobile
21c02acd8c WAV: Re-enable -w option for single file .wav for all decoded speech; 2025-08-16 10:22:31 -04:00
lwvmobile
c7b25c60af DMR TYT BP (16-bit) Encryption Mode; #302
Above Commit is by Anonymous Submission;
2025-08-16 10:18:10 -04:00
lwvmobile
41865f3af7 DMR TYT EP (AES128) Encryption Mode; #302
Above Commit is by Anonymous Submission;
2025-08-15 20:29:34 -04:00
lwvmobile
8f092e293f DMR TYT BP16 Encryption Mode WIP; #302
Code is not working as-is, committed for anonymous submitter to have access to certain items in code;
2025-08-15 12:21:27 -04:00
lwvmobile
c888dfc1d6 DMR TYT AP (PC4) Encryption Mode Support; #302
Above Commit is by Anonymous Submission;
2025-08-15 11:14:46 -04:00
lwvmobile
6ebe3eff58 NXDN: Fix Wformat Warnings 32; #299 2025-07-27 14:17:03 -04:00
lwvmobile
2ca3f0a356 NXDN: Fix Wformat Warnings; #299 2025-07-23 19:16:27 -04:00
lwvmobile
698a991262 NXDN: Misc #299
Fix Perpetual Sync Bug unless Squelch Break;

Add DCR MFID 30 F.C.R Data Preamble and Precoded Message PDU;

Misc Payload Tweaks;
2025-07-14 22:42:35 -04:00
lwvmobile
352bb98bc6 DMR: Fix UTF16 Talker Alias locale to allow Chinese or similar Non-Roman Charsets;
Note: This will only work to the console output, since ncurses wide chars are different. The alias string will replace extended utf16 chars with an * for now.
2025-07-14 06:15:55 -04:00
lwvmobile
8c34313720 NXDN: #299
Additional check for LICH "offbits" that should all be 1's as sync condition;

Add 7-bit Parity Check for known DCR Lich ("RICH") Codes;

Make Tweaks to Help Incorporate Lich Parity and Offbits into NXDN Sync Determination;
2025-07-13 11:32:15 -04:00
lwvmobile
ce805a7860 NXDN: Add DCR TCH / PICH Lich 0x48 Payload Only; #299
Misc Tweaks to Payload Format;
2025-07-13 10:25:40 -04:00
lwvmobile
86b8900b3d NXDN: Fix and Decode Some DCR Elements; #299
Elements include scrambler bits, UC bits, and other misc items;

Tweaks to payload output;
2025-07-08 00:59:41 -04:00
lwvmobile
eb1bcee301 NXDN: Disable Attempted Decoding of SACCH frames on Icom DCR; #299 2025-07-04 17:24:01 -04:00
lwvmobile
fd3b4b6a66 NXDN: DCR Payload Tweaks 2; #299 2025-07-02 15:33:06 -04:00
lwvmobile
b09de5f112 NXDN: DCR Payload Tweaks; #299 2025-07-01 17:00:02 -04:00
lwvmobile
8d1c92c490 NXDN: Add Lich Code 0x46 for Icom DCR Voice and SACCH Payload; #299 2025-06-30 20:25:47 -04:00
lwvmobile
bd98e3befb Re-enable the custom wav file directory option -7 for per-call wav files;
When playing back .amb or .imb files, print AMBE or IMBE when dumping payload on them;
2025-06-29 15:56:52 -04:00
lwvmobile
1aa71811b0 SDRTrunk .mbe File Replay: #298
Mute Audio if enc and no key provided;

Tweak Verbose Payload vs No Verbose Payload Output to console;

Misc Tweaks;
2025-06-24 13:34:45 -04:00
lwvmobile
4e1b1ac98e SDRTrunk .mbe File Replay:
Tweak RC4 Keystream Creation to only create second keystream when P25p1, and only make as much keystream as needed; #298
2025-06-23 02:37:45 -04:00
lwvmobile
ccbaef8541 SDRTrunk .mbe File Replay:
Fix P25p1 RC4 Decryption via keystream juggle and reverse LSFR for IV; #298
2025-06-23 02:27:03 -04:00
lwvmobile
8a30115398 SDRTrunk .mbe File Replay Initial Commit; #298 2025-06-22 15:16:19 -04:00
lwvmobile
3a1d393a0f Fix Playback Speed in Cygwin for .imb/.amb files; #297 2025-06-20 16:05:30 -04:00
lwvmobile
66844b1d4c P25: Provide a temporary fix to the issue of .imb files not writing when a key is presented during an encrypted voice session; #297 2025-06-19 17:03:07 -04:00
lwvmobile
722d7b77fb DMR: Yet Even More RC_TESTING Tweaks; #292 2025-05-29 20:27:15 -04:00
lwvmobile
5a2575c9a0 DMR: Even More RC_TESTING Tweaks; #292 2025-05-29 20:09:55 -04:00
lwvmobile
daad012466 DMR: More RC_TESTING Tweaks; #292 2025-05-29 19:47:25 -04:00
lwvmobile
f2cc686189 DMR: Fix CallTimers CSBK; #293 2025-05-29 15:55:27 -04:00
lwvmobile
175ccff7e9 DMR: Fix BParm Issues Mentioned in #293
Make some tweaks to DMR BS RC_TESTING; #292

Fix Talker Alias Block Num Enumeration (+1) on Valid Blocks;

Misc Typo and Format Fixes;

Tweak for NXDN Keyloading on VCALL;
2025-05-29 14:44:37 -04:00
lwvmobile
4a1e6f1540 Revert Previous Change Regarding DMR Talker Alias; #290 2025-05-22 22:52:39 -04:00
lwvmobile
d285865de5 DMR: Don't append spaces to talker alias string if encoded character is 0; #290
(usually indicates the full talker alias has yet to arrive and only doing a partial in progress decode);
2025-05-22 06:17:19 -04:00
lwvmobile
4bcc9f8aec Fix Coding Error for Compiler Warning / Error for ncursesClose; 2025-05-21 22:23:32 -04:00
lwvmobile
c6b2320cbd Ncurses:
Don't display the lines for Pulse Digital Output if it isn't opened (when using analog only -fA);
2025-05-17 15:00:09 -04:00
lwvmobile
87be5aa72e DMR:
Add FLCO 0x29 FID 0x10 to Unknown Moto Link Control Value to prevent it from setting incorrect tgt, src, svc values;
2025-05-17 01:59:41 -04:00
lwvmobile
3f0f675fd5 Pulse Audio:
Don't Open Audio Stream for Synthesized Digital Voice when using the -fA Analog Only Output Option;
2025-05-16 23:16:32 -04:00
lwvmobile
7c56fb836b Fix Wformat-overflow Issues Regarding Previous Commit; 2025-05-15 16:49:53 -04:00
lwvmobile
2891d7314b Dibit/Symbol Capture File:
When initiated from the ncurses 'R' key, it now rotates every hour;

Above is logged to event history when it occurs;
2025-05-15 16:42:21 -04:00
lwvmobile
ed13bb34d8 YSF: Fix Event History Placing Blank Entries between calls; Fix No Entries when sourced signal is inverted; 2025-05-14 23:03:26 -04:00
lwvmobile
b9097d9462 Event History Setup: #287;
P25p1: Set currentslot to 0 when decoding phase 1 specific duids (HDU, TDU, TDULC, LDU, TSBK, MPDU) so that we don't have a potential stale phase 2 slot value present when writing to event history, etc.

Update Glamor Shot to include the Event History Update;
2025-05-12 21:41:20 -04:00
lwvmobile
59fcfdae81 Event History Setup: #287
Allow Event History to be cycled from short string on, long string on, or of with the 'h' shortcut keyf;
2025-05-12 17:12:10 -04:00
lwvmobile
f85304471f Event History Setup: #287
P25p2: Misc Tweaks for Harris Things; Misc Event History P2 Tweaks;
2025-05-11 12:08:39 -04:00
lwvmobile
3fc67a5694 Event History Setup: #287;
Add customizable event history line (in ncurses) for various items, but left colors as the default cyan. (Remus Request);
2025-05-11 01:16:31 -04:00
lwvmobile
2314900829 Event History Setup: #287
Update Glamor Shots to Show new Event History; Update Misc Items and Examples;
2025-05-10 17:17:48 -04:00
lwvmobile
680b792b19 Event History Setup: #287
Fix Some GPS formats not writing to the event history gps_s string due to mismatching src values (UDT NMEA and LIP);

Fix bug in ComputeCrcCCITT16d where the len value cannot exceed 255 by recasting len as uint32_t instead of uint8_t; #288

Clarify when UDT Data Blocks (R12) is Confirmed or Unconfirmed Data;
2025-05-10 02:09:56 -04:00
lwvmobile
0d734b753f Event History Setup: #287
Fix Flicker Bug in Ncurses Printer by casting lls as static int;

Further Tweak to NcursesPrinter for responsiveness;
2025-05-09 21:11:23 -04:00
lwvmobile
fae33ca0a1 Event History Setup: #287;
Seperate dsd_ncurses.c into dsd_ncurses_printer.c , dsd_ncurses_menu.c , and dsd_ncurses_handler.c files;

Make adjustments as needed to accomodate above;

Work on Handling Special Escape Characters with Ncurses Input Handling to prevent rogue things from happening with scrool wheel, etc;
2025-05-09 18:36:22 -04:00
lwvmobile
dc7ff1136e Event History Setup: #287
Prepare to Seperate Ncurses Code and Event History Code into seperate .c files; Move common items to a .h file;

Move event history code to dsd_events.c; Move AFS code to edacs-fme.c; Move beeper code to dsd_audio2.c
2025-05-09 17:12:31 -04:00
lwvmobile
67ab05da63 Event History Setup: #287;
Trial Fix for ncurses randomly lagging when sync condition with printer vs no sync condition with printer and key input;
2025-05-09 16:18:05 -04:00
lwvmobile
54ba3b6513 Event History Setup: #287
Run ncursesPrinter more frequently during nosync condition to make ncurses user input a lot more responsive during quiet times.
2025-05-09 16:02:56 -04:00
lwvmobile
13739b18aa Event History Setup: #287
Expand Event History for EDACS to Include More Call Information; Misc Tweaks regarding;

Add watchdog and wav file closure to EOT_CC when returning from EDACS Analog or Digital Calls;

Fix local event_string size in watchdog_event_current;

Add watchdog to cleanupAndExit to catch any calls in progress when closing and push them out to event_history log;
2025-05-09 02:20:09 -04:00
lwvmobile
7a198e62f9 Fix UDT Mixed String Format Issue when assigned to event_history text_message; #288 2025-05-09 01:04:13 -04:00
lwvmobile
e37955448e Tweak Event History String Regarding Mixed Address and UTF Text Data; #288 2025-05-08 15:18:00 -04:00
lwvmobile
2dc9db17ab Fix UDT Data Misc Items found by mastodon987; #288 2025-05-08 14:35:22 -04:00
lwvmobile
a3eb6dd552 Event History Setup #287;
Clear some values on Trunking when nocarrier that weren't previously cleared right away;

Write User Lockout of TG to event history; Run noCarrier when Trunking and User Locks Out, prompting return to CC;
2025-05-07 18:19:06 -04:00
lwvmobile
35b782a10f Event History Setup: #287
Tweaks to P25p1 TDU and TDU/LC don't clear out items before they get into event log (bugfix for no crypt info logging);
2025-05-07 13:57:59 -04:00
lwvmobile
8c4dc6ed36 Hytera Enhanced Encrypt: Refresh When bad CACH or TACT forces resync(Run LFSR); 2025-05-06 23:50:00 -04:00
lwvmobile
1532fdcc8e Event History Setup: #287
Include a simple sh file to startup dsd-fme with opts line, and open log files for multi linux terminal support;

Fix bug with P25p1 MPDU Trunking Blocks setting address values to trigger event history watchdog;

Add an internal string into event history to log internal events of note (ENC LO, errors, etc);

Add a watchdog during any ENC LO event so the event_history will log the call with ENC LO;

Disable AHOY and RSP PDU's from Event History (clogs up the event history with extra info);

Fix DMR p_clear trigger to zero out src and tg values when tuning away;

Fix Hytera Enhanced RC4 (verified working); Misc Tweaks Regarding;
2025-05-06 21:19:11 -04:00
lwvmobile
68d959cb7a Event History Setup: #287;
Fix P25p1 LCW Unit to Unit to Private Call in Call History; Misc Tweaks in TDU and LCW;

Add DMR AHOY Info from CSBK and UDT from Data Bursts to Event History;

Send UDT Text Messages to textmessage string in event history and not to the event string; Fix UDT Text Len values;

Fix Misc Issues noted in #288 (IP6, Mixed Add/Text, NMEA ss3 * 10);
2025-05-06 15:06:19 -04:00
lwvmobile
c364117fc0 Event History Setup; #287
Fix NXDN site_code and sys_code order in event_history; Reset correct call type string on TX_REL,  TX_REL_EXT, and DISC messages when stale;

Have P25p1 PDUs write to event_history, regardless of decoded state; Include Enc info if available and not decrypted or decoded; Event History and Log RSP Packets;

Tweak and Add DMR RSP Packets to Event History and Log

Fix Event History Log File Display on Startup;

Add UDP Port 4069 for TRBOnet SCADA; (no decode of data);
2025-05-04 14:53:13 -04:00
lwvmobile
d376fcc227 Event History Setup: #287;
Misc Tweaks Regarding How Trunking Elements May Indirectly Interact with Event History;

DMR Alias Tweak: Allow Extra UTF8 characters (grave, diacritical leters, etc); Allow any UTF16 character that isn't an ASCII special char;

Add GI String (GROUP or PRIVATE) to wav file names, if set and known at close;

Add Encrypted PDU ALG/KID values if not decrypted/decoded;

Allow Manual RTL Input PPM Error Adjustment with { and } keys;

LRRP: Simple Request Handling; Report Handling Tweaks; Simple Handling for Unknown Formats;

Don't reset DMR Color Code on NoCarrier;
2025-05-03 15:59:45 -04:00
lwvmobile
93b297c2d5 Event History Setup: #287;
Don't write Short Data UTF8 to event log or event history in ncurses(garbage text);

Add unknown IP port and protocol lines for event_history;

Add GI from the ILLEGALLY_PARKED CSBK on standard DMR T3 trunking to make sure we get one set;
2025-05-02 17:01:57 -04:00
lwvmobile
d158db618d Fix Misc Minor Bugs and Clang Compiler Warnings; 2025-05-01 22:12:19 -04:00
lwvmobile
934b95cde8 Event History Setup: #287
Make Tweaks / Fixes to DMR Talker Alias;

Setup for Hytera Enhanced RC4 Cipher (LFSR Works, Cipher Untested / Unknown Working State);
2025-05-01 20:40:35 -04:00
lwvmobile
3563399126 Event History Setup Misc; #287
Change the -~ switch to debug mode and -J to the event history file;

Pass slot and swrite value to write_event_to_log_file; Add Slot value to written event log file when swrite is 1 (DMR BS and P25p2);

Misc Consistency Nits;
2025-04-30 19:47:06 -04:00
lwvmobile
6364009e45 Event History Setup; #287
Fix -Wformat-overflow warning regarding writing imported names into a char array;
2025-04-29 14:07:32 -04:00
lwvmobile
1739017303 Event History Setup; #287
Scrub call_matrix items form ncurses, rename for edacs use only;

Scrub a lot of unused local variables out of the ncurses printer;

Add NXDN Talker Aliases to Event History;

Add Remus' suggestion for Tone ID and AD values on the call alert beeper;
2025-04-29 13:58:20 -04:00
lwvmobile
27d416f38b Event History Setup; #287
Add groupName csv import items into the Event History;
2025-04-28 23:12:26 -04:00
lwvmobile
f9dac1c226 Double Duration of beeper function calls so that it actually will play reliably when using OSS audio output (Cygwin); #287 2025-04-28 15:40:56 -04:00
lwvmobile
d1334038df Migrate Call Alert Beeper to New Event Function; Ring different tones on voice start, end, and data calls; #287
Fix and Tweak Calloc Size to the size_t of Event_History * 600 total events (little extra overhead) ;
2025-04-28 15:01:33 -04:00
lwvmobile
4ebc6ab6c9 Event History, Add New .bat file for cygwin portable that will also open and tail an event_log file; #287
Add an initial system event for users to see when started;
2025-04-27 23:13:15 -04:00
lwvmobile
76905b095b Event History Bug Fix; #287
Initialize src_str and tgt_str properly each pass of watchdog_event_current so that wav files do not have a blank string instead of the correct numerical number for src and tgt when applicable; Fix tgt and src string to correct position;

Add dPMR string value to src and tgt for wav files + bugfix; Add Enc if applicable to event history for it;

Misc Nits;
2025-04-27 21:42:09 -04:00
lwvmobile
daf014df6a New Event History Setup and Logging to a Log File, part 3; #287
M17, YSF, DSTAR to save wav files with src and dst as call sign string value and not a numerical value;

Add P25p1 Tier 1 Location and DMR Short Data LOCN Report to Event History; Add MNIS sourced LRRP to Event History;

Don't append truncated TEXT strings to Event String since its full display is directly below it in the Event History;

Add new items to Event History to accomodate above feature;

Tweak misc items regarding above;
2025-04-27 20:30:50 -04:00
lwvmobile
ff4dd03379 Fix Misc Size and Format Warnings Regarding Last Push; #287 2025-04-26 23:39:01 -04:00
lwvmobile
114e305e25 New Event History Setup and Logging to a Log File, part 3; #287
Add Extra Call Information to Event History and Display to Ncurses and Write to Event Log;

Add P25 Known Talker Alias Formats to Event History and Display to Ncurses and Write to Event Log;

Remake the DMR Talker Alias Decoder for 7, 8, and 16 bit chars and go to Event History and Display and to Ncurses and Write to Event Log;

Add Some Embeddded and Misc (Non Data Call) GPS Formats to Event History and Display to Ncurses and Write to Event Log;

Add DMR UDT Data Calls to Event History;

Make Misc Tweaks to Event Item Strings;
2025-04-26 23:34:15 -04:00
lwvmobile
4e04d99780 New Event History Setup and Logging to a Log File, part 2; #287
Rework all Per Call Wav files to tie into Event History; Auto Delete Empty Wav files created; Wav Files in progress have no extension and have no hard set name until closing;

Delete all OLD patch files that are resolved directly or indirectly by new Event History, or have previously been incorporated into code.
2025-04-25 13:33:17 -04:00
lwvmobile
a9a88c5b13 Event History Better Scrolling in Ncurses; #287 2025-04-24 00:14:52 -04:00
lwvmobile
71d4d30f3a Event History: Move event_history_s from the stack to the heap; Calloc Plenty of Memory for it; #287 2025-04-23 16:27:25 -04:00
lwvmobile
8cbfb96141 TMS: Fix Termination / Overflow Issue; #287 2025-04-23 14:40:26 -04:00
lwvmobile
919100daad Fix Current Warnings Regarding, try 2. #287; 2025-04-23 00:05:48 -04:00
lwvmobile
d2f4c1ddd9 Fix Current Warnings Regarding #287; 2025-04-23 00:03:58 -04:00
lwvmobile
cb89fb69c1 New Event History Setup and Logging to a Log File, part 1; #186 2025-04-22 23:50:07 -04:00
lwvmobile
51727b815d P25: Initialize sap_string and rsp_string; 2025-04-17 16:15:04 -04:00
lwvmobile
8bfdeeebd0 P25: Fix printFrameInfo state->nac compiler warning on fprintf; 2025-04-07 22:42:18 -04:00
lwvmobile
b963b08469 P25: Redo printFrameInfo to make it consistent across Phase 1 and Phase 2; 2025-04-07 22:40:18 -04:00
lwvmobile
177551e081 DMR: Display Alg Type on Late Entry Single Burst; 2025-04-06 21:01:37 -04:00
lwvmobile
7ab5ec8b40 DMR: Fix More Lingering DMRA PI lfsr128 Mismatch Issues due to int to ulli conversion errors; 2025-04-06 21:00:57 -04:00
lwvmobile
8fd9c64c0c DMR: Change PDU MI to ulli to match and prevent possible issues related to int to ulli change in mi storage;
PI Header Display Nit;
2025-04-06 18:53:16 -04:00
lwvmobile
a9af3b9380 DMR: Fix Lingering DMRA PI and lfsr32 Mismatch Issues due to int to ulli conversion errors;
(0xFFFFFFFF12345678 vs 0x12345678) issue in display and late entry mismatch;
2025-04-06 18:50:14 -04:00
lwvmobile
fc21063652 Fix Variable Types for print;
Remove Unused Code;
2025-04-06 16:57:58 -04:00
lwvmobile
19864225f6 DMR: Fix Hytera Enhanced Not Muting Properly (OR SVC OPT enc bit); 2025-04-05 07:18:03 -04:00
lwvmobile
3312e14422 DMR: Fix DES Alg (0x22) Set;
Fix Late Entry Mismatch Bug due to variable type change (int to ulli);

More Fixes Tweaks for Hytera Enhanced Encryption Setup;
2025-04-04 17:35:48 -04:00
lwvmobile
425d477168 DMR: Fix common slot counter (csc) variable found in slow link control sys_parms; #285 2025-04-01 20:36:37 -04:00
lwvmobile
b351b6a20d P25p1: Fix Relationship of PDU Extended Addressing Header and ESS Header; 2025-03-31 05:03:14 -04:00
lwvmobile
4b75211ad0 DMR: Reconfigure dmr_le;
Reconfigure dmr_le so that it can be completely disabled for Hytera Enhanced, or Late Entry MI only for DMRA, or Late Entry MI and Key/Alg for DMRA;

Misc Hytera Enhanced Setup Tweaks;
2025-03-26 00:54:08 -04:00
lwvmobile
88c76c4a6c DMR: Enhanced Encryption PI / FLC Setup;
Fix some PI Elements for Consistency and List Known Alg Types;
2025-03-25 18:14:33 -04:00
lwvmobile
cfbd859d9e DMR: Fix PDU AlgID for AES; 2025-03-25 13:55:36 -04:00
lwvmobile
5851abbce8 Fix AES Key Segment (ULLI) Array Size; 2025-03-21 15:45:16 -04:00
lwvmobile
d35bed4348 Move MBP to header file; 2025-03-20 18:34:20 -04:00
lwvmobile
4e77bb05b9 CODE: Remove Whitespace (ezpwd); 2025-03-17 17:22:22 -04:00
lwvmobile
fdbc2ef0e2 CODE: Remove Whitespace; 2025-03-17 16:38:04 -04:00
lwvmobile
237cb3ffa5 P25p1: Fix Moto APX GPS LCW; 2025-03-17 13:57:12 -04:00
lwvmobile
7ed1a79b5b Misc; 2025-03-17 13:53:38 -04:00
lwvmobile
e36672f54c Fix Some Tab Alignment Issues;
NXDN: Make seperate AES IV expansion function;
2025-03-15 20:26:25 -04:00
lwvmobile
eac360571e YOLO:
Congratulations for clicking this link, you are now reading this description;

Add DES-OFB Decryption Support for P25, NXDN, DMR (Tested);

Add DES-XL Decryption Support for P25p1 (Tested);

Add AES-OFB Decryption Support for P25, DMR (Tested); NXDN(untested);

Add P25p1 PDU Decryption Support; (Untested)(Known Modes);

Add Remaining (AES, DES) DMR PDU Decryption Support (Known DMRA Modes)(Untested);

Decrypt Tytera 16-bit BP (works depending on model);

Add AES Keys example csv file for import;

Probably a few bugs that'll need to be massaged out now with all the code additions for several years of stashed code;
2025-03-14 23:54:58 -04:00
lwvmobile
9f59729fb1 Disable Debug Catch All UTF8 Decode on unknown PDU types; 2025-03-13 14:55:08 -04:00
lwvmobile
9f063cb4da Fix Some UTF8 String Overflow Issues; 2025-03-13 14:45:02 -04:00
lwvmobile
f593453328 PDU: Misc Catch All UTF8 Decodes on Unknown Format Types; 2025-03-13 13:58:09 -04:00
lwvmobile
871bdf5a82 TMS: Better Parsing (WIP); 2025-03-13 13:45:07 -04:00
lwvmobile
fbf40335aa DMR: Fix TDMA Direct Mode voice handling; 2025-03-13 12:43:03 -04:00
lwvmobile
0c8a11c607 P25p1: More Misc PDU Tweaks; 2025-03-12 12:16:31 -04:00
lwvmobile
c13ffbc9b2 P25p1: Misc PDU Tweaks; (WIP);
DMR: Fix TMS Message String for Ncurses & Tweaks;

Make the IP PDU more generic;

Make Cellocator and ARS decoder placeholder functions;

Add port for P25 Tier 2 Location Services (LRRP);
2025-03-11 11:59:55 -04:00
lwvmobile
31f9edefa5 P25p1: Reorganize Multiblock PDU Handling; 2025-03-10 10:11:13 -04:00
lwvmobile
fe0195ad10 DMR: Ncurses Display For Unknown IP / UDP Based Calls (port or protocol); 2025-03-05 14:57:39 -05:00
lwvmobile
e3e2fff6e5 DMR: Fix Rate 1 Encoded Packet Data Assembly; Fix Rate 1 Confirmed Data CRC9 Check; 2025-03-05 14:39:19 -05:00
lwvmobile
3b67ff52d5 DMR: Add Data Call Type to Ncurses; TMS to truncated ASCII range string for ncurses; UTF8 and UTF16 tweaks; 2025-03-05 10:47:02 -05:00
lwvmobile
17789f03f9 DMR: Bypass CRC Error on MNIS Data; Reset SAP and Format Storage After Use; #283 2025-03-05 05:13:34 -05:00
lwvmobile
6fb464f57d DMR: Add Support for the MNIS Header; #283 2025-03-05 02:35:42 -05:00
lwvmobile
6c277da869 DMR: Fix Corrected KS Offset Application on Chained P_Head for BP; 2025-03-04 07:03:50 -05:00
lwvmobile
4d5af32ce7 DMR: Add Handling for IPv4 UDP / ICMP / UNK Protocol PDUs; 2025-03-04 06:56:42 -05:00
lwvmobile
16a84b8d3e DMR: Fix Incorrect SRC and TGT on DPF 15 for ncurses display; 2025-03-04 04:09:03 -05:00
lwvmobile
eebb251d2f DMR: Tidy Up PDU Enc Code; 2025-03-03 23:09:02 -05:00
lwvmobile
4255a2f9ba DMR: Correct KS Offset Application on Chained P_Head; 2025-03-03 22:58:52 -05:00
lwvmobile
5fcfb141a6 DMR: Fix PDU MI variable type; 2025-03-03 21:53:52 -05:00
lwvmobile
304baca7c5 DMR: Fix Bug on PDU Info Display; 2025-03-03 09:09:38 -05:00
lwvmobile
fb2de46d88 Cygwin:
Add Linebreak to copy and paste installer prompts in Install_Notes.md;
2025-03-03 06:56:54 -05:00
lwvmobile
f551952bf6 Cygwin:
Remove the portable release builder code from download-and-install-cygwin.sh script, call cyg_portable.sh from said script instead (simplify);

Remove old .bat files or rename pulseaudio ones in case of need for referencing them;

Include a simple example_options.txt file so users can get an idea of options to pass to dsd-fme as opposed to having a bunch of bat files;
2025-03-03 06:17:52 -05:00
lwvmobile
ec8d74fabd Cygwin:
Cmake -DNOCOLORSLOGS=OFF for Cygwin Builds (or user option)

Tweak cygwin scripts to include touch.exe in portable release builder.

Rework original 1aa bat file to be a simplified / unified .bat file (old bat files still available in examples folder);
2025-03-03 05:17:04 -05:00
lwvmobile
f732ad9639 Add Float Symbol Input File Support (.raw or .sym); 2025-03-02 23:44:19 -05:00
lwvmobile
31bfd6a235 DMR: Misc Code Cleanup; 2025-03-02 08:14:43 -05:00
lwvmobile
a44c81a6bb Fix Compiler Warning; 2025-03-01 13:20:44 -05:00
lwvmobile
df56d421f8 DMR: Setup a KS start (offset) value to apply specifically for chained p_head where the second p_head is only partially encrypted; 2025-03-01 12:57:25 -05:00
lwvmobile
f5fad8d5e0 DMR: Misc PDU Tweaks/Fixes/Cleanup; 2025-03-01 03:32:16 -05:00
lwvmobile
f92c64c4d3 DMR: Fix Data Assembly on Moto Ext Header;
Make appropriate adjustments based on above;

Additional Tweaks for some LRRP Tokens;
2025-02-28 11:33:23 -05:00
lwvmobile
2ca15d8b53 DMR: Decode Additional IP Header Elements;
Adjustments to Moto EXT Header and its LRRP Variant;

Make additional adjustments to LRRP PDUs based on above;
2025-02-28 07:39:14 -05:00
lwvmobile
bc18db2bc2 DMR: Fix NMEA / LOCN:
Use DD.MMmmmm format;

Dump to File;

Disable Reponse Data Header (dpf == 1) to ncurses string;
2025-02-26 01:45:25 -05:00
lwvmobile
a006074459 DMR: Only Print LRRP Data to file if lat/lon is present; 2025-02-25 22:54:15 -05:00
lwvmobile
141eac9bc3 Remove pavucontrol from installer scripts; Add note under virtual sinks; #280 2025-02-25 15:01:23 -05:00
lwvmobile
69dd1d167b DMR: Fix LRRP Report to File Format; #281 2025-02-25 11:11:28 -05:00
lwvmobile
f6aba4a5a4 DMR: Fix Potential Overflo/Underflow in UTF8 Text; 2025-02-23 23:52:28 -05:00
lwvmobile
f0b5f2737f DMR: Disable break statement on UTF8 text; 2025-02-23 21:03:05 -05:00
lwvmobile
5c177d671c DMR: Disable LOCN unless SD PDU first octet is 0x01; 2025-02-23 20:34:30 -05:00
lwvmobile
1d112a21cc Merge pull request #278 from thesamesam/cmake
CMakeLists.txt: fix build w/ split tinfo
2025-02-23 18:10:44 -05:00
Sam James
13bbf08e6c CMakeLists.txt: further future-proofing of FindCurses use
`CURSES_INCLUDE_DIR` -> `CURSES_INCLUDE_DIRS` as `_DIR` is deprecated
per the `FindCurses` docs. It shouldn't matter like the `_LIBRARY` issue
though as it seems to gracefully populate both with the same value.
2025-02-23 23:08:24 +00:00
Sam James
7c180113cd CMakeLists.txt: fix build w/ split tinfo
Gentoo ships "split tinfo" (with `tinfo` in `libtinfo`). This is fine
if pkg-config or similar is used, but FindCurses unfortunately misbehaves
(see https://gitlab.kitware.com/cmake/cmake/-/issues/23236).

dsd-fme applies the workaround needed for that already since c917c61b85
but `CURSES_LIBRARY` was used instead of `CURSES_LIBRARIES`. Despite the
CMake docs only indicating that it's a deprecated alias, `_LIBRARY` indeed
only contains some of the needed libraries instead of all.

Fix that by using `CURSES_LIBRARIES` instead.

Closes: https://github.com/lwvmobile/dsd-fme/issues/276
2025-02-23 22:58:14 +00:00
lwvmobile
2ba4641796 DMR: Reset some elements on TD_LC; 2025-02-23 16:40:03 -05:00
lwvmobile
d11de2cbac DMR: PDU Misc Tweaks and Fixes; 2025-02-23 16:14:19 -05:00
lwvmobile
1725e45793 DMR: PDU RC4 and BP Decryption; 2025-02-23 02:58:45 -05:00
lwvmobile
035df461e1 Cygwin: Remove 'mode con: cols=90 lines=50' from bat files since its broken in PowerShell; 2025-02-22 23:02:34 -05:00
lwvmobile
f6f5380df4 DMR: Rework PDU Assembly; 2025-02-22 22:50:53 -05:00
lwvmobile
d8df5ceab5 DMR: PDU / LRRP Misc Fixes, Tweaks, and Cleanup; 2025-02-22 14:28:09 -05:00
lwvmobile
dfe744ef13 DMR: Rework PDUs; 2025-02-22 09:04:07 -05:00
lwvmobile
2fbdf46fd6 Fix .bat file permissions and home folder location for cygwin portable build; 2025-02-14 23:54:53 -05:00
lwvmobile
2c100dd121 Update Cygwin Scripts for:
Make a portable cygwin release as a user option in the shell script;

Add the bat files from the precompiled releases to the examples folder;

Add rebuild_cygwin.bat so cygwin users can easily update their versions;

Standalone cygwin_portable.sh version creator;
2025-02-14 23:13:43 -05:00
lwvmobile
42cb14b9d9 Cygwin SH Tweaks for multithread; 2025-02-12 19:35:56 -05:00
lwvmobile
c5cded87b1 Update Cygwin Instructions; 2025-02-12 12:22:27 -05:00
lwvmobile
b2ee4e1df0 Fix Cygwin Instructions; 2025-02-11 22:33:58 -05:00
lwvmobile
db73166da7 Cygwin SH File Tweaks; 2025-02-11 22:15:34 -05:00
lwvmobile
8d21574a93 Fixes For Cygwin Builds:
Remove AERO Build Define; Replace with __CYGWIN__;

Fix extern optarg,optind on __CYGWIN__;

Fix GIT_TAG versioning for __CYGWIN__;

Add Experimental Install sh script;
2025-02-11 19:17:30 -05:00
lwvmobile
35bd5ccc55 Code Arrangement and Additions:
Migrate Harris Alias for vPDU;

Setup Harris Alias for LCW;

Working Harris GPS for LCW;

Harris GRG Bitmap(speculative);

Migrate Tait Alias for LCW;

Misc Tweaks;
2025-02-10 23:45:56 -05:00
lwvmobile
6dd1410faf Moto Alias Dump Function;
Misc Tweaks for displaying and writing various P25 Aliases to File and Ncurses for display;

Misc String Safety Tweaks on Aliases and writing to csv files / display;
2025-02-07 17:33:08 -05:00
lwvmobile
0cbf42e4e6 P25 Moto Alias vPDU Working; Tweaks and Fixes; 2025-02-06 21:36:01 -05:00
lwvmobile
f0657ee5a8 Change P25 Moto Alias Block bn to num_bits value to make it work with multiple bit per block values; Misc Cleanup and Tweaks; 2025-02-06 00:11:46 -05:00
lwvmobile
29ee3a5f3a Moto Alias for MAC vPDU Setup;
Seperate P25 Moto Alias Block Function from Alias Decode Function;

Move Code to dsd_alias.c;
2025-02-05 23:39:25 -05:00
lwvmobile
8cfaced4b4 P25 Moto Talker Alias LCW WIP; 2025-02-05 09:52:30 -05:00
lwvmobile
61673222db Merge branch 'p25_moto_alias_setup' into aw_dev 2025-02-05 07:13:18 -05:00
lwvmobile
f59f0cbb7f P25 Moto Talker Alias LCW Setup and Test; 2025-02-05 07:12:41 -05:00
lwvmobile
20af28597d Fix Variable Print Format; 2025-02-04 17:26:00 -05:00
lwvmobile
942d83f54d Merge pull request #275 from pizzolante/fix/locale-independent-package-check
FIX: Locale indipendent libitpp-dev package check
2025-02-03 23:41:08 -05:00
lwvmobile
0c6f132520 Merge pull request #272 from VasylSamoilov/patch-1
Fix example file group.csv syntax
2025-02-03 23:40:13 -05:00
Leo Pizzolante
6319508937 FIX: Locale indipendent libitpp-dev package check 2025-02-03 23:56:42 +01:00
lwvmobile
ae6525e986 P25 Tait Talker Alias LCW; 2025-02-02 00:49:23 -05:00
lwvmobile
51d1690edc Fix Misc vPDU Manufacturer Len Values; 2025-02-01 16:32:54 -05:00
lwvmobile
14d00f9228 New Tag; 2025-01-26 13:34:40 -05:00
VasylSamoilov
6afa4ed327 Fix example file group.csv syntax 2024-12-25 16:18:24 +02:00
139 changed files with 27676 additions and 17138 deletions

View File

@@ -10,20 +10,20 @@ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
set(CURSES_NEED_NCURSES TRUE) set(CURSES_NEED_NCURSES TRUE)
set(CURSES_NEED_WIDE TRUE) set(CURSES_NEED_WIDE TRUE)
#use cmake option -DAERO=ON to enable Aero Builds (OSS support and specific tweaks) #use cmake option -DCOLORS=OFF to disable color output (ncurses)
option(AERO
"Build for Aero Environments (Cygwin)" OFF)
if (AERO)
add_definitions(-DAERO_BUILD)
endif ()
#use cmake option -DCOLORS=OFF to disable color output
option(COLORS option(COLORS
"Build with Colors Enabled" ON) "Build with Colors Enabled" ON)
if (COLORS) if (COLORS)
add_definitions(-DPRETTY_COLORS) add_definitions(-DPRETTY_COLORS)
endif () endif ()
#use cmake option -DCOLORSLOGS=OFF to disable color output (terminal/logs)
option(COLORSLOGS
"Build with Colors Enabled" ON)
if (COLORSLOGS)
add_definitions(-DPRETTY_COLORS_LOGS)
endif ()
#use cmake option -DPVC=ON to enable Provoice Conventional Frame Sync #use cmake option -DPVC=ON to enable Provoice Conventional Frame Sync
option(PVC option(PVC
"Build with Provoice Conventional Frame Sync Enabled" OFF) "Build with Provoice Conventional Frame Sync Enabled" OFF)
@@ -52,14 +52,13 @@ git_describe(GIT_TAG)
find_package(LibSndFile REQUIRED) find_package(LibSndFile REQUIRED)
find_package(MBE REQUIRED) find_package(MBE REQUIRED)
find_package(ITPP REQUIRED)
find_package(RTLSDR) find_package(RTLSDR)
find_package(Curses REQUIRED) find_package(Curses REQUIRED)
find_package(PulseAudio REQUIRED) find_package(PulseAudio REQUIRED)
find_package(CODEC2) find_package(CODEC2)
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIR}) include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIRS})
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARY}) set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARIES})
if(RTLSDR_FOUND) if(RTLSDR_FOUND)
find_package(Threads) find_package(Threads)

View File

@@ -1,8 +1,7 @@
# Digital Speech Decoder - Florida Man Edition # Digital Speech Decoder - Florida Man Edition
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and 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.
[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.
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, 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.

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)

244
cyg_portable.sh Normal file
View File

@@ -0,0 +1,244 @@
#! /bin/bash
#
cdir=$(pwd)
clear
printf "DSD-FME Digital Speech Decoder - Florida Man Edition Portable Cygwin Creator\n"
printf "Only use this in a Cygwin64 Environment. Will not work in a Linux Environment!\n"
read -p "Do you want to make a portable copy? y/N " RELEASE
RELEASE=$(printf "$RELEASE"|tr '[:upper:]' '[:lower:]')
if [ "$RELEASE" = "y" ]; then
printf "Copying files to dsd-fme-portable folder and making zip file. Please wait!\n"
cd ~
#create a dsd-fme-portable folder and copy all the needed items for a portable release version
mkdir dsd-fme-portable
cd dsd-fme-portable
#all of these folders may not be necessary
mkdir sourcecode
mkdir bin
mkdir dev
cd dev
mkdir shm
cd ..
mkdir dsd-fme
mkdir logs
mkdir WAV
mkdir etc
mkdir home
mkdir lib
mkdir tmp
mkdir usr
cd usr
mkdir share
cd share
mkdir terminfo
cd ~
#start copying things
#terminfo is needed for ncurses
cp -r /usr/share/terminfo/* dsd-fme-portable/usr/share/terminfo
#copy required items from the root bin folder to release bin folder
cp /bin/ls.exe dsd-fme-portable/bin/
cp /bin/bash.exe dsd-fme-portable/bin/
cp /bin/ncurses6-config dsd-fme-portable/bin/
cp /bin/ncursesw6-config dsd-fme-portable/bin/
cp /bin/pacat.exe dsd-fme-portable/bin/
cp /bin/pactl.exe dsd-fme-portable/bin/
cp /bin/padsp dsd-fme-portable/bin/
cp /bin/pamon dsd-fme-portable/bin/
cp /bin/paplay dsd-fme-portable/bin/
cp /bin/parec dsd-fme-portable/bin/
cp /bin/parecord dsd-fme-portable/bin/
cp /bin/pulseaudio.exe dsd-fme-portable/bin/
cp /bin/tty.exe dsd-fme-portable/bin/
#start copying things from the etc folder
cp -r /etc/pulse/ dsd-fme-portable/etc/
#start copying things to the lib folder
cp -r /lib/pulseaudio/ dsd-fme-portable/lib/
#start copying things into the dsd-fme folder
cp dsd-fme/build/dsd-fme.exe dsd-fme-portable/dsd-fme/
cp -r dsd-fme/examples dsd-fme-portable/dsd-fme/
cp .bash_profile dsd-fme-portable/dsd-fme/
cp .bashrc dsd-fme-portable/dsd-fme/
cp .inputrc dsd-fme-portable/dsd-fme/
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/*.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
#change .bat files permissions to be read/write/executable by all users (for some reason, this is not preserved)
chmod 777 dsd-fme-portable/*.bat
#copy the license, copyright, source code, and things that should be included in all portable releases
cp dsd-fme/COPYRIGHT dsd-fme-portable/sourcecode/
cp dsd-fme/DSD_Author.pgp dsd-fme-portable/sourcecode/
cp dsd-fme/dsd-fme-qgis-map.qgz dsd-fme-portable/sourcecode/
cp dsd-fme/README.md dsd-fme-portable/sourcecode/
cp dsd-fme/dsd-fme2.png dsd-fme-portable/sourcecode/
cp dsd-fme/dsd-fme3.png dsd-fme-portable/sourcecode/
cp dsd-fme/CMakeLists.txt dsd-fme-portable/sourcecode/
cp -r dsd-fme/src dsd-fme-portable/sourcecode/
cp -r dsd-fme/include dsd-fme-portable/sourcecode/
cp -r dsd-fme/cmake dsd-fme-portable/sourcecode/
cp -r dsd-fme/patch dsd-fme-portable/sourcecode/
#compiled items into release dsd-fme folder
cp codec2/build/src/cygcodec2* 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/
#bin items into release dsd-fme folder (uisng * where version numbering may exist, those may be upgraded if hard coded)
cp /bin/bash.exe dsd-fme-portable/dsd-fme/
cp /bin/cut.exe dsd-fme-portable/dsd-fme/
cp /bin/cygao* dsd-fme-portable/dsd-fme/
cp /bin/cygarchive* dsd-fme-portable/dsd-fme/
cp /bin/cygargp* dsd-fme-portable/dsd-fme/
cp /bin/cygassuan* dsd-fme-portable/dsd-fme/
cp /bin/cygasyncns* dsd-fme-portable/dsd-fme/
cp /bin/cygatomic* dsd-fme-portable/dsd-fme/
cp /bin/cygattr* dsd-fme-portable/dsd-fme/
cp /bin/cygavahi-client* dsd-fme-portable/dsd-fme/
cp /bin/cygavahi-common* dsd-fme-portable/dsd-fme/
cp /bin/cygavahi-wrap* dsd-fme-portable/dsd-fme/
cp /bin/cygblkid* dsd-fme-portable/dsd-fme/
cp /bin/cygbrotlicommon* dsd-fme-portable/dsd-fme/
cp /bin/cygbrotlidec* dsd-fme-portable/dsd-fme/
cp /bin/cygbz2* dsd-fme-portable/dsd-fme/
cp /bin/cygcares* dsd-fme-portable/dsd-fme/
cp /bin/cygcheck.exe dsd-fme-portable/dsd-fme/
cp /bin/cygcli.dll dsd-fme-portable/dsd-fme/
cp /bin/cygcom_err* dsd-fme-portable/dsd-fme/
cp /bin/cygcrypt* dsd-fme-portable/dsd-fme/
cp /bin/cygcrypto* dsd-fme-portable/dsd-fme/
cp /bin/cygcurl* dsd-fme-portable/dsd-fme/
cp /bin/cygdb_cxx* dsd-fme-portable/dsd-fme/
cp /bin/cygdb_sql* dsd-fme-portable/dsd-fme/
cp /bin/cygdb* dsd-fme-portable/dsd-fme/
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/cygfdisk* dsd-fme-portable/dsd-fme/
cp /bin/cygffi* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3_threads* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3f_threads* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3f* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3l_threads* dsd-fme-portable/dsd-fme/
cp /bin/cygfftw3l* dsd-fme-portable/dsd-fme/
cp /bin/cygfido2* dsd-fme-portable/dsd-fme/
cp /bin/cygFLAC* dsd-fme-portable/dsd-fme/
cp /bin/cygformw* dsd-fme-portable/dsd-fme/
cp /bin/cyggc* dsd-fme-portable/dsd-fme/
cp /bin/cyggdbm_compat* dsd-fme-portable/dsd-fme/
cp /bin/cyggdbm*.dll dsd-fme-portable/dsd-fme/
cp /bin/cyggio* dsd-fme-portable/dsd-fme/
cp /bin/cygglib* dsd-fme-portable/dsd-fme/
cp /bin/cyggmodule* dsd-fme-portable/dsd-fme/
cp /bin/cyggmp* dsd-fme-portable/dsd-fme/
cp /bin/cyggnutls* dsd-fme-portable/dsd-fme/
cp /bin/cyggobject* dsd-fme-portable/dsd-fme/
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/cyggsm* dsd-fme-portable/dsd-fme/
cp /bin/cyggssapi_krb5* dsd-fme-portable/dsd-fme/
cp /bin/cyggthread* dsd-fme-portable/dsd-fme/
cp /bin/cygguile* dsd-fme-portable/dsd-fme/
cp /bin/cyghistory* dsd-fme-portable/dsd-fme/
cp /bin/cyghogweed* dsd-fme-portable/dsd-fme/
cp /bin/cygICE* dsd-fme-portable/dsd-fme/
cp /bin/cygiconv* dsd-fme-portable/dsd-fme/
cp /bin/cygid3tag* dsd-fme-portable/dsd-fme/
cp /bin/cygidn* dsd-fme-portable/dsd-fme/
cp /bin/cygintl* dsd-fme-portable/dsd-fme/
cp /bin/cygisl* dsd-fme-portable/dsd-fme/
cp /bin/cygjsoncpp* dsd-fme-portable/dsd-fme/
cp /bin/cygk5crypto* dsd-fme-portable/dsd-fme/
cp /bin/cygkrb5* dsd-fme-portable/dsd-fme/
cp /bin/cyglber* dsd-fme-portable/dsd-fme/
cp /bin/cygldap* dsd-fme-portable/dsd-fme/
cp /bin/cygltdl* dsd-fme-portable/dsd-fme/
cp /bin/cyglz* dsd-fme-portable/dsd-fme/
cp /bin/cygma* dsd-fme-portable/dsd-fme/
cp /bin/cygmenuw* dsd-fme-portable/dsd-fme/
cp /bin/cygmeta* dsd-fme-portable/dsd-fme/
cp /bin/cygmp* dsd-fme-portable/dsd-fme/
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/cygobjc* dsd-fme-portable/dsd-fme/
cp /bin/cygogg* dsd-fme-portable/dsd-fme/
cp /bin/cygopus* dsd-fme-portable/dsd-fme/
cp /bin/cygorc* dsd-fme-portable/dsd-fme/
cp /bin/cygp11* dsd-fme-portable/dsd-fme/
cp /bin/cygpanel* dsd-fme-portable/dsd-fme/
cp /bin/cygpath.exe dsd-fme-portable/dsd-fme/
cp /bin/cygpcre* dsd-fme-portable/dsd-fme/
cp /bin/cygperl* dsd-fme-portable/dsd-fme/
cp /bin/cygpipe* dsd-fme-portable/dsd-fme/
cp /bin/cygpkg* dsd-fme-portable/dsd-fme/
cp /bin/cygpng* dsd-fme-portable/dsd-fme/
cp /bin/cygpopt* dsd-fme-portable/dsd-fme/
cp /bin/cygprotocol* dsd-fme-portable/dsd-fme/
cp /bin/cygpsl* dsd-fme-portable/dsd-fme/
cp /bin/cygpulse* dsd-fme-portable/dsd-fme/
cp /bin/cygquadmath* dsd-fme-portable/dsd-fme/
cp /bin/cygreadline* dsd-fme-portable/dsd-fme/
cp /bin/cygrhash* dsd-fme-portable/dsd-fme/
cp /bin/cygrunsrv.exe dsd-fme-portable/dsd-fme/
cp /bin/cygsamplerate* dsd-fme-portable/dsd-fme/
cp /bin/cygsasl* dsd-fme-portable/dsd-fme/
cp /bin/cygserver* dsd-fme-portable/dsd-fme/
cp /bin/cygSM* dsd-fme-portable/dsd-fme/
cp /bin/cygsmart* dsd-fme-portable/dsd-fme/
cp /bin/cygsndfile* dsd-fme-portable/dsd-fme/
cp /bin/cygsox* dsd-fme-portable/dsd-fme/
cp /bin/cygspeex* dsd-fme-portable/dsd-fme/
cp /bin/cygsqlite* dsd-fme-portable/dsd-fme/
cp /bin/cygssh* dsd-fme-portable/dsd-fme/
cp /bin/cygssl* dsd-fme-portable/dsd-fme/
cp /bin/cygstart.exe dsd-fme-portable/dsd-fme/
cp /bin/cygstdc* dsd-fme-portable/dsd-fme/
cp /bin/cygtasn* dsd-fme-portable/dsd-fme/
cp /bin/cygtdb* dsd-fme-portable/dsd-fme/
cp /bin/cygticw* dsd-fme-portable/dsd-fme/
cp /bin/cygtwo* dsd-fme-portable/dsd-fme/
cp /bin/cygunistring* dsd-fme-portable/dsd-fme/
cp /bin/cygusb* dsd-fme-portable/dsd-fme/
cp /bin/cyguuid* dsd-fme-portable/dsd-fme/
cp /bin/cyguv* dsd-fme-portable/dsd-fme/
cp /bin/cygvorbis* dsd-fme-portable/dsd-fme/
cp /bin/cygwavpack* dsd-fme-portable/dsd-fme/
cp /bin/cygwin1.dll dsd-fme-portable/dsd-fme/
cp /bin/cygwrap* dsd-fme-portable/dsd-fme/
cp /bin/cygX* dsd-fme-portable/dsd-fme/
cp /bin/cygx* dsd-fme-portable/dsd-fme/
cp /bin/cygz* dsd-fme-portable/dsd-fme/
cp /bin/grep.exe dsd-fme-portable/dsd-fme/
cp /bin/ls.exe dsd-fme-portable/dsd-fme/
cp /bin/mintty.exe dsd-fme-portable/dsd-fme/
cp /bin/pacat.exe dsd-fme-portable/dsd-fme/
cp /bin/pacmd.exe dsd-fme-portable/dsd-fme/
cp /bin/pactl.exe dsd-fme-portable/dsd-fme/
cp /bin/padsp dsd-fme-portable/dsd-fme/
cp /bin/pamon dsd-fme-portable/dsd-fme/
cp /bin/paplay dsd-fme-portable/dsd-fme/
cp /bin/parec dsd-fme-portable/dsd-fme/
cp /bin/pulseaudio.exe dsd-fme-portable/dsd-fme/
cp /bin/tail.exe dsd-fme-portable/dsd-fme/
cp /bin/touch.exe dsd-fme-portable/dsd-fme/
cp /bin/tty.exe dsd-fme-portable/dsd-fme/
zip -r dsd-fme-x86-64-cygwin-portable.zip dsd-fme-portable
fi

14
cyg_rebuild.sh Normal file
View File

@@ -0,0 +1,14 @@
#! /bin/bash
#
clear
printf "DSD-FME Digital Speech Decoder - Florida Man Edition
Automatic Git Pull and Rebuild for Cygwin\n\n"
sleep 1
##Open your clone folder##
git pull
sleep 2
##cd into your build folder##
cd build
cmake ..
make -j $(nproc)
make install

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 -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 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 git clone https://github.com/lwvmobile/mbelib
cd mbelib cd mbelib
git checkout ambe_tones git checkout ambe_tones

View File

@@ -0,0 +1,77 @@
#! /bin/bash
#
cdir=$(pwd)
clear
printf "Digital Speech Decoder: Florida Man Edition - Auto Installer For Cygwin\n
This will install the required packages, clone, build, and install DSD-FME only.
This has been tested on Cygwin x86-64 as of 20250211. This may take a while!\n
MBELib is considered a requirement on this build.
You must view the Patent Notice prior to continuing.
The Patent Notice can be found at the site below.
https://github.com/lwvmobile/mbelib#readme
Please confirm that you have viewed the patent notice by entering y below.\n\n"
read -p "Have you viewed the patent notice? y/N " ANSWER
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
if [ "$ANSWER" = "y" ]; then
#is this needed?
LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH
echo $LD_LIBRARY_PATH
#MBELIB
cd $cdir
printf "Installing mbelib\n Please wait!\n"
git clone https://github.com/lwvmobile/mbelib.git
cd mbelib
git checkout ambe_tones
mkdir build
cd build
cmake ..
make -j $(nproc)
make install
cp cygmbe-1.dll /bin
#CODEC2
cd $cdir
printf "Installing codec2\n Please wait!\n"
git clone https://github.com/drowe67/codec2.git
cd codec2
mkdir build
cd build
cmake ..
make -j $(nproc)
make install
#RTL-SDR
cd $cdir
printf "Installing RTL-SDR\n Please wait!\n"
git clone https://github.com/lwvmobile/rtl-sdr.git
cd rtl-sdr
mkdir build
cd build
cmake ..
make -j $(nproc)
make install
#DSD-FME
cd $cdir
printf "Installing DSD-FME\n Please wait!\n"
git clone https://github.com/lwvmobile/dsd-fme.git
cd dsd-fme
#git checkout aw_dev
mkdir build
cd build
cmake -DCOLORSLOGS=OFF ..
make -j $(nproc)
make install
cd $cdir
#call the cyg_portable script
sh dsd-fme/cyg_portable.sh
printf "Any issues, Please report to either:\nhttps://github.com/lwvmobile/dsd-fme/issues or\nhttps://forums.radioreference.com/threads/dsd-fme.438137/\n\n"
else
printf "Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.\n\n"
fi

View File

@@ -12,13 +12,7 @@ read -p "Have you viewed the patent notice? y/N " ANSWER
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]') ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
if [ "$ANSWER" = "y" ]; then if [ "$ANSWER" = "y" ]; then
sudo apt update sudo apt update
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 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
chkitpp=$(sudo apt list libitpp-dev 2>&1|grep -ci "installed")
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
git clone https://github.com/lwvmobile/mbelib git clone https://github.com/lwvmobile/mbelib
cd mbelib cd mbelib
git checkout ambe_tones 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:]') ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
if [ "$ANSWER" = "y" ]; then if [ "$ANSWER" = "y" ]; then
sudo apt update sudo apt update
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev libcodec2-dev 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
chkitpp=$(sudo apt list libitpp-dev 2>&1|grep -ci "installed")
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
git clone https://github.com/lwvmobile/mbelib git clone https://github.com/lwvmobile/mbelib
cd mbelib cd mbelib
git checkout ambe_tones git checkout ambe_tones

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 KiB

After

Width:  |  Height:  |  Size: 725 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

@@ -171,7 +171,7 @@ Some Keyboard Shortcuts have been implemented for testing to see how users like
esc or arrow keys - ncurses menu esc or arrow keys - ncurses menu
q - quit q - quit
c - toggle compact mode c - toggle compact mode
h - toggle call history h - cycle event history short, long, or off
z - toggle console payloads z - toggle console payloads
a - toggle call alert beep a - toggle call alert beep
4 - force privacy/scrambler key assertion over enc identifiers (dmr and nxdn) 4 - force privacy/scrambler key assertion over enc identifiers (dmr and nxdn)
@@ -216,6 +216,10 @@ V - Toggle Low Pass Filter on Analog Voice or Raw Signal Monitoring
B - Toggle High Pass Filter on Analog Voice or Raw Signal Monitoring B - Toggle High Pass Filter on Analog Voice or Raw Signal Monitoring
N - Toggle Passband Filter on Analog Voice or Raw Signal Monitoring N - Toggle Passband Filter on Analog Voice or Raw Signal Monitoring
[ - Scroll backwards through Event History
] - Scroll forwards through Event History
\ - Switch between Slot 1 and Slot 2 Event History
v - Cycle RTL Input Volume Multiplier 1X, 2X, 3X (lower v) v - Cycle RTL Input Volume Multiplier 1X, 2X, 3X (lower v)
``` ```

View File

@@ -43,9 +43,9 @@ Debian/Mint/Ubuntu/Pi
``` ```
sudo apt update sudo apt update
#Ubuntu 22.04 lts / Debian 11 and lower #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 #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 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 -Syu
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget rtl-sdr 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 ## 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. 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 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
```
### Build and Install ITPP - ONLY IF NOT IN REPO!!
```
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
tar xjf itpp*
#if you can't cd into this folder, double check folder name first
cd itpp-4.3.1
mkdir build
cd build
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig
cd ..
cd ..
``` ```
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice") 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")
@@ -105,22 +86,6 @@ cd ..
cd .. cd ..
``` ```
Note: Some environment/distro users (Cygwin, Arch, and others) may need to manually copy any libmbe files found in the build folder to the /usr/lib folder on root.
```
cp libmbe* /usr/lib
cp cygmbe* /usr/lib
cp cygmbe* /bin
```
or
```
sudo cp libmbe* /usr/lib
sudo cp cygmbe* /usr/lib
sudo cp cygmbe* /bin
```
Finish by running these steps to clone and build DSD-FME. Finish by running these steps to clone and build DSD-FME.
``` ```
@@ -135,34 +100,42 @@ sudo ldconfig
``` ```
### LD_PATH
Some environments may require an LD path to be set to /usr/local/lib, similar to:
```
LD_LIBRARY_PATH=/usr/local/lib
export LD_LIBRARY_PATH
```
### Windows Cygwin Builds ### Windows Cygwin Builds
If using RTL input support, you must install libusb-win32 before proceeding with Cygwin installation. You will also need to build and compile a version of librtlsdr, but this can be tricky, and personally, I had to resort to using a much older version of the source code to get it to build at all with the rtl support in the precompiled 'Aero' builds. Cygwin builds now have an experimental semi-automatic installer, to run, follow steps below:
Then install all dependencies available in the repo prior to building, either directly selecting them during setup with the setup-x86.exe or setup-x64.exe or by issuing a command similar to this: Open Windows PowerShell (not Command Prompt) and copy and paste all of this in all at once.
``` ```
setup-x64.exe --packages libpulse-devel,libpulse-mainloop-glib0,libpulse-simple0,libpulse0,pulseaudio,pulseaudio-debuginfo,pulseaudio-equalizer,pulseaudio-module-x11,pulseaudio-module-zeroconf,pulseaudio-utils,sox-fmt-pulseaudio,xfce-pulseaudio-plugin,libusb0,libusb1.0,libusb1.0-debuginfo,libusb1.0-devel,libncurses++w10,libncurses-devel,libncursesw10,ncurses,cmake,gcc-core,gcc-debuginfo,gcc-objc,git,make,socat,sox,sox-fmt-ao,unzip,wget,gcc-g++,libsndfile-devel Invoke-WebRequest https://cygwin.com/setup-x86_64.exe -OutFile setup-x86_64.exe
``` .\setup-x86_64.exe --packages nano,libpulse-devel,libpulse-mainloop-glib0,libpulse-simple0,libpulse0,pulseaudio,pulseaudio-debuginfo,pulseaudio-equalizer,pulseaudio-module-x11,pulseaudio-module-zeroconf,pulseaudio-utils,sox-fmt-pulseaudio,libusb0,libusb1.0,libusb1.0-debuginfo,libusb1.0-devel,libncurses++w10,libncurses-devel,libncursesw10,ncurses,cmake,gcc-core,gcc-debuginfo,gcc-objc,git,make,socat,sox,sox-fmt-ao,zip,unzip,wget,gcc-g++,libsndfile-devel
Then manually install ITPP and MBElib, see above install notes. Be sure to run `cp libmbe* /usr/lib` after compiling MBElib.
Then you can build and install using
``` ```
git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme Pick a Mirror. http://www.gtlib.gatech.edu mirror seems relatively fast. Nurse the Cygwin installer by clicking next and waiting for it to finish. Ignore the warning popup telling you to install libusb from sourceforge. Install the [Zadig](https://zadig.akeo.ie/ "Zadig") driver instead, if you haven't already and have an RTL Dongle. After Cygwin finishes installing, the installer sh script will download and run, be patient, it may also take a little while.
mkdir build
cd build Then:
# -DAERO=ON is optional, but recommended if you want the older OSS support as well
cmake -DAERO=ON ..
make
make install
``` ```
C:\cygwin64\bin\mintty.exe /bin/bash -l -c "wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/refs/heads/audio_work/download-and-install-cygwin.sh; sh download-and-install-cygwin.sh; dsd-fme;"
```
After the sh script finishes, dsd-fme should open. If not, then double click on the Cygwin Terminal desktop shortcut, and try running `dsd-fme`. If you chose to create a portable version, you will find the folder and zip file in the `C:\cygwin64\home\username` directory if using the default cygwin64 folder location.
You can also update your versions by using the cyg_rebuild.sh script `sh cyg_rebuild.sh`.
### Virtual Sinks ### Virtual Sinks
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired. Note: This setup is completely optional if using TCP Network Sink Audio and/or RTL Input. You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired. Note: This setup is completely optional if using TCP Network Sink Audio and/or RTL Input. You may need to manually install either of these programs manually, if desired, or find suitable alternatives for pipewire-pulse such as pwvucontrol, or similar.
``` ```
pactl load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink pactl load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink

View File

@@ -0,0 +1,14 @@
@REM Shutdown pulseaudio and clean out any remenants in /dev/shm if left behind
cd dsd-fme
pactl.exe exit
@REM now navigate to the /dev/shm folder and automatically remove any
@REM remnants from other times pulse wasn't shut down properly
cd ..
cd dev
cd shm
del pulse*
PAUSE

View File

@@ -0,0 +1,34 @@
@REM Set the console / terminal to 90 columns, 50 lines
@REM mode con: cols=90 lines=50
@ECHO OFF
@REM change directory into the dsd-fme folder
cd dsd-fme
@REM Start the Pulse Audio backend with a 10 minute time out
@REM start pulse audio server with 600 second idle time and no CPU limit (prevent closing while idle in menu)
@REM output from pulse server routed to NUL to supress "capabilities dropped, nag messages, etc" messages
pulseaudio.exe --start --no-cpu-limit=TRUE --exit-idle-time=600 2> NUL
@REM End pulse start up
ECHO The Pulse Audio Server should now be active!
@REM Setup Some null-sinks (if desired, remove @REM below)
@REM pactl load-module module-null-sink sink_name=virtual_sink1 sink_properties=device.description=Virtual_Sink1
@REM pactl load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
@REM Setup Default Sink to device 0 (output)
pactl set-default-sink 0
@REM List Avaiable Pulse Audio Sinks and Sources
@ECHO ON
dsd-fme -O
PAUSE

View File

@@ -0,0 +1,224 @@
Build Version: AW 2025-209-g14edf32
MBElib Version: 1.3.4
CODEC2 Support Enabled
Usage: dsd-fme [options] Decoder/Trunking Mode
or: dsd-fme [options] -r <files> Read/Play saved mbe data from file(s)
or: dsd-fme -h Show help
Display Options:
-N Use NCurses Terminal
dsd-fme -N 2> console_log.txt
-Z Log MBE/PDU Payloads to console
Device Options:
-O List All Pulse Audio Input Sources and Output Sinks (devices).
Input/Output options:
-i <device> Audio input device (default is pulse)
/dev/dsp for OSS audio (Depreciated: Will require padsp wrapper in Linux)
pulse for pulse audio signal input
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)
tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)
tcp:192.168.7.5:7355 for custom address and port
filename.bin for OP25/FME capture bin files
filename.wav for 48K/1 wav files (SDR++, GQRX)
filename.wav -s 96000 for 96K/1 wav files (DSDPlus)
(Use single quotes '/directory/audio file.wav' when directories/spaces are present)
-s <rate> Sample Rate of wav input files (48000 or 96000) Mono only!
-o <device> Audio output device (default is pulse)
/dev/dsp for OSS audio (Depreciated: Will require padsp wrapper in Linux)
pulse for pulse audio decoded voice or analog output
pulse:1 or pulse:alsa_output.pci-0000_0d_00.3.analog-stereo for pulse audio decoded voice or analog output on device (see -O)
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
-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; )
(Manual: 1 = 2%; 50 = 100%)
-n <float> Audio Analog Output Gain (Default: 0 = Auto; 0-100% )
-6 <file> Output raw audio .wav file (48K/1). (WARNING! Large File Sizes 1 Hour ~= 360 MB)
-7 <dir> Create/Use Custom directory for Per Call decoded .wav file saving.
(Use ./folder for Nested Directory!)
(Use /path/to/folder for hard coded directory!)
(Use Before the -P option!)
-8 Enable Source Audio Monitor
(Set Squelch in RTL, SDR++ or GQRX, etc, if monitoring mixed analog/digital)
-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!)
-a Enable Call Alert Beep
(Warning! Might be annoying.)
-J <file> Specify Filename for Event Log Output.
-L <file> Specify Filename for LRRP Data Output.
-Q <file> Specify Filename for OK-DMRlib Structured File Output. (placed in DSP folder)
-Q <file> Specify Filename for M17 Float Stream Output. (placed in DSP folder)
-c <file> Output symbol capture to .bin file
-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
RTL-SDR options:
Usage: rtl:dev:freq:gain:ppm:bw:sq:vol
NOTE: all arguments after rtl are optional now for trunking, but user configuration is recommended
dev <num> RTL-SDR Device Index Number or 8 Digit Serial Number, no strings! (default 0)
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
gain <num> RTL-SDR Device Gain (0-49)(default = 0; Hardware AGC recommended)
ppm <num> RTL-SDR PPM Error (default = 0)
bw <num> RTL-SDR Bandwidth kHz (default = 12)(4, 6, 8, 12, 16, 24)
sq <num> RTL-SDR Squelch Level vs RMS Value (Optional)
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
Decoder options:
-fa Auto Detection
-fA Passive Analog Audio Monitor
-ft TDMA Trunking P25p1 Control and Voice, P25p2 Trunked Channels, and DMR
-fs DMR TDMA BS and MS Simplex
-f1 Decode only P25 Phase 1
-f2 Decode only P25 Phase 2 (6000 sps) **
-fd Decode only DSTAR
-fx Decode only X2-TDMA
-fy Decode only YSF
-fz Decode only M17*
-fi Decode only NXDN48* (6.25 kHz) / IDAS*
-fn Decode only NXDN96* (12.5 kHz)
-fp Decode only ProVoice*
-fh Decode only EDACS Standard/ProVoice*
-fH Decode only EDACS Standard/ProVoice with ESK 0xA0*
-fh344 Decode only EDACS Standard/ProVoice and set AFS to 344 or similar custom 11-bit scheme*
-fH434 Decode only EDACS Standard/ProVoice and set AFS to custom 11-bit scheme with ESK 0xA0*
-fe Decode only EDACS EA/ProVoice*
-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
* 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.
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)
-ma Auto-select modulation optimizations
-mc Use only C4FM modulation optimizations (default)
-mg Use only GFSK modulation optimizations
-mq Use only QPSK modulation optimizations
-m2 Use P25p2 6000 sps QPSK modulation optimizations
-F Relax P25 Phase 2 MAC_SIGNAL CRC Checksum Pass/Fail
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
-b <dec> Manually Enter Basic Privacy Key (Decimal Value of Key Number)
(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.
-H 0B57935150
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-H <hex> Manually Enter AES-128, AES-256, or Kirisun-256 Hex Key (see example below)
Encapulate in Single Quotation Marks; Space every 16 chars.
-H '736B9A9C5645288B 243AD5CB8701EF8A'
-H '20029736A5D91042 C923EB0697484433 005EFC58A1905195 E28E9C7836AA2DB8'
-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) 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)
-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
For Example, enter 49-bit Keystream as:
-S 49:123456789ABC80
For Example, enter 49-bit Keystream (MBP 70) as:
-S 49:ED0AED4AED4AED4A
For Example, Baofeng 49-bit Keystream for 56-bit Custom Encryption as:
-S 49:11AA22BB33CC44
-k <file> Import Key List from csv file (Decimal Format) -- Lower Case 'k'.
Only supports NXDN, DMR Basic Privacy (decimal value).
(dPMR and Hytera 32/64 char not supported, DMR uses TG value as key id -- EXPERIMENTAL!!).
-K <file> Import Key List from csv file (Hexidecimal Format) -- Capital 'K'.
Use for Hex Value Hytera 10-char BP keys, RC4 10-char, DES 16-char Hex Keys, and AES128/256 32/64-char keys.
-4 Force Privacy Key over Encryption Identifiers (DMR MBP/HBP and NXDN Scrambler)
-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.
Trunking Options:
-C <file> Import Channel to Frequency Map (channum, freq) from csv file. (Capital C)
(See channel_map.csv for example)
-G <file> Import Group List Allow/Block and Label from csv file.
(See group.csv for example)
-T Enable Trunking Features (NXDN/P25/EDACS/DMR) with RIGCTL/TCP or RTL Input
-Y Enable Scanning Mode with RIGCTL/TCP or RTL Input
Experimental -- Can only scan for sync with enabled decoders, don't mix NXDN and DMR/P25!
This is not a Trunking Feature, just scans through conventional frequencies fast!
-W Use Imported Group List as a Trunking Allow/White List -- Only Tune with Mode A
-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
-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)
P25 - 12000; NXDN48 - 7000; NXDN96: 12000; DMR - 7000-12000; EDACS/PV - 12000-24000;
May vary based on system stregnth, etc.
-t <secs> Set Trunking or Scan Speed VC/sync loss hangtime in seconds. (default = 1 second)
Trunking Example TCP: dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.txt
Trunking Example RTL: dsd-fme -fs -i rtl:0:450M:26:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.txt

View File

@@ -0,0 +1,97 @@
This example options contains numerous options put into the start-dsd-fme.bat file in the option string
feel free to experiment and mix and match example options for your bat files option string.
Trunking Option Examples:
P25 (Phase 1 Control) Trunking with SDR++ Network Sink input, RIGCTL, /dev/dsp output,
per call wav files, ncurses terminal
-ft -i tcp:127.0.0.1:7355 -U 4532 -T -P -N
P25 (Phase 2 Control) Trunking with SDR++ Network Sink input, RIGCTL, /dev/dsp output,
per call wav files, ncurses terminal
-f2 -i tcp:127.0.0.1:7355 -U 4532 -T -P -N
DMR Trunking with RTL input, /dev/dsp output, channel map import,
per call wav files, ncurses terminal, payload dump to console
-fs -i rtl:0:450M:0:-2:12 -T -C examples\dmr_t3_chan.csv -P -N -Z
DMR Trunking with SDR++ input, RIGCTL, /dev/dsp output, RAS enabled CSBK (-F),
channel map import, per call wav files, ncurses terminal, payload dump to console
-fs -F -i tcp -U 4532 -T -C examples\capacity_plus_chan.csv -P -N -Z
NXDN48 v2.0 DFA (Direct Frequency Assignment) Trunking with SDR++ input, RIGCTL, /dev/dsp output,
scrambler key import (decimal), per call wav files, ncurses terminal, payload dump to console
-fi -i tcp -U 4532 -T -P -N -Z -k examples\nxdn_sc_key.csv
NXDN96 Trunking with SDR++ input, RIGCTL, /dev/dsp output,
channel map import, per call wav files, ncurses terminal, payload dump to console
-fn -i tcp -U 4532 -T -C examples\nxdn_chan_map.csv -P -N -Z
EDACS Standard Network Analog and Digital
-fh -i tcp -U 4532 -T -C edacs_channel_map.csv -G group.csv -N
-fh -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N
EDACS Extended Address Analog and Digital (with ESK)
-fE -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N
-fE -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N
Scanning Option Examples:
Scanning will not perform quickly like a real scanner, but can be used with any
supported decoding type and analog as well, and will require a channel_map to scan frequencies
channel numbers do not matter, but use unique ones for each frequeny
-t 0 will scan as fast as possible, but may miss frame sync, -t 1 is stable, but needs 1 second between tuning
P25/DMR/Analog Scanning with RTL input, /dev/dsp output, channel map import,
per call wav files, ncurses terminal, payload dump to console, raw audio signal monitoring
-ft -i rtl:0:450M:0:-2:12 -Y -t 1 -C examples\dmr_t3_chan.csv -P -8 -N -Z
P25/DMR/Analog Scanning with SDR++ input (use squelch), RIGCTL, /dev/dsp output, channel map import,
per call wav files, ncurses terminal, payload dump to console, raw audio signal monitoring
-ft -i tcp:127.0.0.1:7355 -U 4532 -Y -t 1 -C examples\dmr_t3_chan.csv -P -8 -N -Z
NXDN48/Analog Scanning with SDR++ input (use squelch), RIGCTL, /dev/dsp output, channel map import,
per call wav files, ncurses terminal, payload dump to console, raw audio signal monitoring
-fi -i tcp:127.0.0.1:7355 -U 4532 -Y -t 1-C examples\nxdn_chan_map.csv -P -8 -N -Z
Analog Scanning with SDR++ input (use squelch), RIGCTL, /dev/dsp output, channel map import
-fA -i tcp:127.0.0.1:7355 -U 4532 -Y -t 0 -C examples\nxdn_chan_map.csv -8
Encryption Option Examples:
DMR with default /dev/dsp input and output, use basic privacy key 70
-fs -b 70
DMR with default /dev/dsp input and output, load .csv file with decimal basic privacy keys loaded by TG value,
-fs -k examples\multi_key.csv
DMR with default /dev/dsp input and output, use Hytera basic privacy key (10, 32, and 64 hex char versions)
-fs -H 1111111111
-fs -H '2222222222222222 3333333333333333'
-fs -H '2222222222222222 3333333333333333 4444444444444444 5555555555555555'
DMR with default /dev/dsp input and output, use RC4 key 0x7777777777
-fs -1 7777777777
DMR with default /dev/dsp input and output, use RC4 key 0x7777777777,
Force RC4 Key over Missing PI header/LE Encryption Identifiers (-0)
Disable Late Entry Encryption Signalling Identifiers (-3)
-fs -0 -3 -1 7777777777
DMR with default /dev/dsp input and output, load RC4 and 10-char HBP keys from .csv file (hexidecimal format)
-fs -K dsd-fme\examples\multi_key_hex.csv
P25 with default /dev/dsp input and output, use RC4 key 0x7777777777
-ft -1 7777777777
NXDN48 with default /dev/dsp input and output, use Scrambler key 32767
-fi -R 32767
NXDN96 with default /dev/dsp input and output, use Scrambler key 15000
-fn -R 15000
dPMR with default /dev/dsp input and output, use Scrambler key 32767
-fm -R 32767
NXDN48 with default /dev/dsp input and output, load scrambler keys from .csv file (decimal format)
-fi -k dsd-fme\examples\nxdn_sc_key.csv

View File

@@ -0,0 +1 @@
These .bat files are ONLY for use on the portable cygwin builds, do not use these in any other build environments!

View File

@@ -0,0 +1,74 @@
@REM Digital Speech Decoder: Florida Man Edition
@REM DSD-FME Cygwin Windows Portable Builds
@REM
@REM The source code of this software can be downloaded here:
@REM https://github.com/lwvmobile/dsd-fme
@REM
@REM If you paid for, or were otherwise fleeced for this software, DEMAND YOUR MONEY BACK!
@REM
@REM This software is designed to be used with an SDR receiver (like SDR++ or SDR#) with TCP input,
@REM RTL Input, media file playback over virtual cables or null-sinks, or a Discriminator Tap input
@REM
@REM The complete options list can be seen by viewing complete_usage_options.txt
@REM Many examples can be found in example_options.txt
@REM
@REM Enjoy!
@REM Regarding the options string (or any string in .bat files)
@REM If your string may contain spaces, set the first '"' before options, and then an ending '"'
@REM Incorrect: set options="-fs -N -Z"
@REM Correct: set "options=-fs -N -Z"
@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)
@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%
@REM random number is used so that two log files don't share the same name
set rnd=%RANDOM%
@REM set log file relative filepath
set "clog=.\logs\console_log_%datetimestr%_%rnd%.txt"
set "elog=.\logs\event_log_%datetimestr%_%rnd%.txt"
@REM create the log file now with touch
.\dsd-fme\touch.exe %clog%
.\dsd-fme\touch.exe %elog%
@REM Launch Tail to display the console log and event log in a seperate console windows
start .\dsd-fme\tail.exe -n 40 -f %clog%
start .\dsd-fme\tail.exe -n 40 -f %elog%
@REM start dsd-fme with options and logs
.\dsd-fme\dsd-fme.exe %options% -J %elog% 2> %clog%
echo ----------------------------------------------------------------------------------
echo ----------------------------------------------------------------------------------
echo For any errors, see: %clog%
echo Forward %clog% and Options: "%options%"
echo to developer on Github or Radio Reference for troubleshooting.
echo ----------------------------------------------------------------------------------
echo ----------------------------------------------------------------------------------
@REM Set pause to diplay above message
PAUSE

View File

@@ -0,0 +1,7 @@
keyid(hex), keyvalue(hex)
0001,0000000000000000,0000000000000000,0000000000000000,0000002823d87923
0003,0101010101010101,0101010101010101
0004,0101010101010101,0101010101010101,0101010101010101,0101010101010101
C197,A753BC945DE5E0F1,EC1970F8565154E6,D9DF2FAC6278FA93,B531D2CC046E93A2
0067,0000000000000000,0000000000000000,0000000000000000,DEADBEEFBAADF00D
0000,0000000000000000,0000000000000000,0000000000000000,0000000000000001
1 keyid(hex), keyvalue(hex)
2 0001,0000000000000000,0000000000000000,0000000000000000,0000002823d87923
3 0003,0101010101010101,0101010101010101
4 0004,0101010101010101,0101010101010101,0101010101010101,0101010101010101
5 C197,A753BC945DE5E0F1,EC1970F8565154E6,D9DF2FAC6278FA93,B531D2CC046E93A2
6 0067,0000000000000000,0000000000000000,0000000000000000,DEADBEEFBAADF00D
7 0000,0000000000000000,0000000000000000,0000000000000000,0000000000000001

View File

@@ -1,4 +1,4 @@
DEC,Mode(A- Allow, B - Block, DE - Digital Enc),Name of Group,Tag (do not delete this line or won't import properly) DEC,Mode(A- Allow; B - Block; DE - Digital Enc),Name of Group,Tag (do not delete this line or won't import properly)
100,B,Example Name,Tag 100,B,Example Name,Tag
1449,A,Fire Dispatch,Fire 1449,A,Fire Dispatch,Fire
929,A,Fire Tac,Fire 929,A,Fire Tac,Fire
1 DEC,Mode(A- Allow, B - Block, DE - Digital Enc),Name of Group,Tag (do not delete this line or won't import properly) DEC Mode(A- Allow; B - Block; DE - Digital Enc) Name of Group Tag (do not delete this line or won't import properly)
2 100,B,Example Name,Tag 100 B Example Name Tag
3 1449,A,Fire Dispatch,Fire 1449 A Fire Dispatch Fire
4 929,A,Fire Tac,Fire 929 A Fire Tac Fire

View File

@@ -0,0 +1,112 @@
#!/bin/bash
# Force bash for better compatibility (I think)
#
# Digital Speech Decoder: Florida Man Edition
#
# The source code of this software can be downloaded here:
# https://github.com/lwvmobile/dsd-fme
#
# If you paid for, or were otherwise fleeced for this software, DEMAND YOUR MONEY BACK!
#
# This software is designed to be used with an SDR receiver (like SDR++ or SDR#) with TCP input,
# RTL Input, media file playback over virtual cables or null-sinks, or a Discriminator Tap input
#
# The complete options list can be seen by viewing complete_usage_options.ans
# Many examples can be found in example_options.ans
#
# Enjoy!
###
#### IMPORTANT! Set your dsd-fme options here! ####
# Example : opts=' -ft -i rtl:0:460.350M:0:-1:8 -N -Z '
####
# set options to pass to dsd-fme
opts=' -N -Z '
####
# Initialize variables
xe="0"
gt="0"
xt="0"
ko="0"
# Functions to check for available terminals
chk_xe(){
# Check if x-terminal-emulator is installed
xe=$(which x-terminal-emulator 2>/dev/null)
if [ -z $xe ];then xe="0";else xe="1";fi
}
chk_gt(){
# Check if gnome-terminal is installed
gt=$(which gnome-terminal 2>/dev/null)
if [ -z $gt ];then gt="0";else gt="1";fi
}
chk_ko(){
# Check if Konsole is installed
ko=$(which konsole 2>/dev/null)
if [ -z $ko ];then ko="0";else ko="1";fi
}
chk_xt(){
# Check if xterm is installed
xt=$(which xterm 2>/dev/null)
if [ -z $xt ];then xt="0";else xt="1";fi
}
# Check for and set terminal
chk_xe
if [ $xe = "1" ];then
term=$(which x-terminal-emulator 2>/dev/null)
printf "Using X-Terminal-Emulator!\n"
else
chk_gt
fi
if [ $gt = "1" ];then
term=$(which gnome-terminal 2>/dev/null)
printf "Using Gnome-Terminal!\n"
elif [ $xe = "0" ]&&[ $gt = "0" ];then
chk_xt
fi
if [ $xt = "1" ];then
term=$(which xterm 2>/dev/null)
printf "Using XTerm!\n"
elif [ $xe = "0" ]&&[ $gt = "0" ]&&[ $xt = "0" ];then
chk_ko
fi
if [ $ko = "1" ];then
term=$(which konsole 2>/dev/null)
printf "Using Konsole!\n"
fi
# Bail if no terminal found
if [ "$ko" -eq "0" ]&&[ "$gt" -eq "0" ]&&[ "$xt" -eq "0" ]&&[ "$xe" -eq "0" ];then printf "No known terminals available!\n";exit 1;fi
# Get current directory
wd=$(pwd)
# Set date/time
# dt=$(date +%F_%H%M-%S-%N) #with hyphens
dt=$(date +%Y%m%d_%H%M%S) #just an underscore between date and time
if [ ! -d "logs" ]; then
mkdir logs
fi
# set log file relative filepath
clog="$wd/logs/console_log_$dt.txt"
elog="$wd/logs/event_log_$dt.txt"
# create the log file now with touch
touch $clog
touch $elog
# open new terminal windows to watch live log files
if [ "$term" == "$(which konsole)" ]||[ "$term" == "$(which xterm)" ];then
$term -e "tail -n 40 -f $clog" 2>/dev/null
$term -e "tail -n 40 -f $elog" 2>/dev/null
else
$term --window -e "tail -n 40 -f $clog" 2>/dev/null
$term --window -e "tail -n 40 -f $elog" 2>/dev/null
fi
# start dsd-fme with options and log files
dsd-fme $opts -J $elog 2> $clog
echo -------------------------------------------------------------------------------
echo -------------------------------------------------------------------------------
echo For any errors, see: $clog
echo Forward $clog and
echo Options: \" $opts \"
echo to developer on Github or Radio Reference for troubleshooting.
echo -------------------------------------------------------------------------------
echo -------------------------------------------------------------------------------
# Set pause to diplay above message
read -p "Press ENTER to continue... " x

View File

@@ -7,8 +7,6 @@
* *
* Code adapted from http://www.aqdi.com/golay.htm * 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> #include <stdio.h>

View File

@@ -5,9 +5,6 @@
/** /**
* Code taken and adapted from www.eccpage.com/rs.c * Code taken and adapted from www.eccpage.com/rs.c
* Credit goes to Mr Simon Rockliff. * 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 /* This program is an encoder/decoder for Reed-Solomon codes. Encoding is in

71
include/bp.h Normal file
View File

@@ -0,0 +1,71 @@
#ifndef BP_H
#define BP_H
static const uint16_t BPK[256] = {
//000
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
//008
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
//016
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
//024
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E,
//032
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
//040
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
//048
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
//056
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36,
//064
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
//072
0x614C, 0x7E4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
//080
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
//088
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57,
//096
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
//104
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
//112
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
//120
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F,
//128
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
//136
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
//144
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
//152
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897,
//160
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
//168
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
//176
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
//184
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF,
//192
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
//200
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
//208
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
//216
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3,
//224
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
//232
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
//240
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
//248
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6
};
#endif

View File

@@ -17,8 +17,8 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
//defined by CMakeLists.txt -- Disable by using cmake -DCOLORS=OFF .. //ANSII Color Characters in Terminal -- Disable by using cmake -DCOLORSLOGS=OFF ..
#ifdef PRETTY_COLORS #ifdef PRETTY_COLORS_LOGS
#define KNRM "\x1B[0m" #define KNRM "\x1B[0m"
#define KRED "\x1B[31m" #define KRED "\x1B[31m"
#define KGRN "\x1B[32m" #define KGRN "\x1B[32m"
@@ -59,8 +59,9 @@
#include "p25p1_heuristics.h" #include "p25p1_heuristics.h"
//OSS support //OSS support
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__)
#include <sys/soundcard.h> #include <sys/soundcard.h>
#endif
#include <pulse/pulseaudio.h> //PULSE AUDIO #include <pulse/pulseaudio.h> //PULSE AUDIO
#include <pulse/simple.h> //PULSE AUDIO #include <pulse/simple.h> //PULSE AUDIO
#include <pulse/error.h> //PULSE AUDIO #include <pulse/error.h> //PULSE AUDIO
@@ -88,6 +89,48 @@
extern volatile uint8_t exitflag; //fix for issue #136 extern volatile uint8_t exitflag; //fix for issue #136
//event history (each item)
typedef struct {
uint8_t write; //if this event needs to be written to a log file
uint8_t color_pair; //this value corresponds to which color pair the line should be in ncurses
int8_t systype; //indentifier of which decoded system type this is from (P25, DMR, etc)
int8_t subtype; //subtype of systpe (VLC, TLC, PDU data, System Event, etc)
uint32_t sys_id1; //sys_id1 through 5 will be a hierarchy of system identifiers
uint32_t sys_id2; //for example, trunked P25 has WACN:SYS:CC:SITE_ID:RFSS_ID
uint32_t sys_id3; //conventional may only use NAC, RAN, or Color Codes
uint32_t sys_id4; //
uint32_t sys_id5; //
int8_t gi; //group or individual
uint8_t enc; //clear or encrypted
uint8_t enc_alg; //alg if encrypted
uint16_t enc_key; //enc key id value, if encrypted (not key value or key variable)
uint64_t mi; //mi, or iv base value from OTA if provided
uint16_t svc; //other relevant svc opts if applicable
uint32_t source_id; //source radio id or other source value
uint32_t target_id; //group or individual target, or destination value
char src_str[200]; //source, expressed as a string for M17, YSF, DSTAR, dPMR
char tgt_str[200]; //target, expressed as a string for M17, YSF, DSTAR, dPMR
char t_name[200]; //this is the string present from any csv groupName import
char s_name[200]; //same as above, but if loaded from a src value and not tg value
char t_mode[200]; //mode, or A,B,D,DE from csv group import file
char s_mode[200]; //mode, or A,B,D,DE from csv group import file
uint32_t channel; //if this occurs on a trunking channel, which channel
time_t event_time; //time event occurred
uint8_t pdu[128*24]; //relevant link control, or full PDU if data call (in bytes)
char sysid_string[200]; //string comprised of system unique identifiers
char alias[2000]; //if this event has a source radio talker alias or similar
char gps_s[2000]; //gps, if returned, expressed as a string
char text_message[2000]; //if this event is a decoded text message, then it goes here
char event_string[2000]; //user legible and printable string for the event that happened
char internal_str[2000]; //string that relates to a DSD-FME generated event (ENC LO, error notices, etc)
} Event_History;
//event history for number of each items above
typedef struct {
Event_History Event_History_Items[255];
} Event_History_I;
//new audio filter stuff from: https://github.com/NedSimao/FilteringLibrary //new audio filter stuff from: https://github.com/NedSimao/FilteringLibrary
typedef struct { typedef struct {
float coef[2]; float coef[2];
@@ -269,17 +312,21 @@ typedef struct
FILE *mbe_out_f; FILE *mbe_out_f;
FILE *mbe_out_fR; //second slot on a TDMA system FILE *mbe_out_fR; //second slot on a TDMA system
FILE *symbol_out_f; FILE *symbol_out_f;
time_t symbol_out_file_creation_time; //time the symbol out file was created
uint8_t symbol_out_file_is_auto; //if the user hit the R key
float audio_gain; float audio_gain;
float audio_gainR; float audio_gainR;
float audio_gainA; float audio_gainA;
int audio_out; int audio_out;
int dmr_stereo_wav; int dmr_stereo_wav; //per-call wav file use (rename later)
int static_wav_file; //single static wav file for decoding duration
char wav_out_dir[512]; char wav_out_dir[512];
char wav_out_file[1024]; char wav_out_file[1024];
char wav_out_fileR[1024]; char wav_out_fileR[1024];
char wav_out_file_raw[1024]; char wav_out_file_raw[1024];
char symbol_out_file[1024]; char symbol_out_file[1024];
char lrrp_out_file[1024]; char lrrp_out_file[1024];
char event_out_file[1024];
char szNumbers[1024]; //**tera 10/32/64 char str char szNumbers[1024]; //**tera 10/32/64 char str
short int mbe_out; //flag for mbe out, don't attempt fclose more than once short int mbe_out; //flag for mbe out, don't attempt fclose more than once
short int mbe_outR; //flag for mbe out, don't attempt fclose more than once short int mbe_outR; //flag for mbe out, don't attempt fclose more than once
@@ -311,10 +358,6 @@ typedef struct
int ssize; int ssize;
int msize; int msize;
int playfiles; int playfiles;
int m17encoder;
int m17encoderbrt;
int m17encoderpkt;
int m17decoderip;
int delay; int delay;
int use_cosine_filter; int use_cosine_filter;
int unmute_encrypted_p25; int unmute_encrypted_p25;
@@ -344,9 +387,9 @@ typedef struct
pa_simple *pulse_digi_dev_outR; pa_simple *pulse_digi_dev_outR;
char pa_input_idx[100]; char pa_input_idx[100];
char pa_output_idx[100]; char pa_output_idx[100];
int use_ncurses_terminal; uint8_t use_ncurses_terminal;
int ncurses_compact; uint8_t ncurses_compact;
int ncurses_history; uint8_t ncurses_history;
int reset_state; int reset_state;
int payload; int payload;
char output_name[1024]; char output_name[1024];
@@ -368,7 +411,6 @@ typedef struct
short int aggressive_framesync; short int aggressive_framesync;
int frame_m17; int frame_m17;
int inverted_m17;
FILE *symbolfile; FILE *symbolfile;
int call_alert; int call_alert;
@@ -385,12 +427,6 @@ typedef struct
int udp_portno; int udp_portno;
char udp_hostname[1024]; 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 //tcp socket for SDR++, etc
int tcp_sockfd; int tcp_sockfd;
int tcp_portno; int tcp_portno;
@@ -546,6 +582,9 @@ typedef struct
int lasttgR; int lasttgR;
int lastsrc; int lastsrc;
int lastsrcR; int lastsrcR;
int8_t gi[2]; //group, or private call, per slot
uint8_t eh_index;
uint8_t eh_slot;
int nac; int nac;
int errs; int errs;
int errs2; int errs2;
@@ -584,8 +623,8 @@ typedef struct
int payload_keyidR; int payload_keyidR;
int payload_mfid; int payload_mfid;
int payload_mfidR; int payload_mfidR;
int payload_mi; unsigned long long int payload_mi;
int payload_miR; unsigned long long int payload_miR;
unsigned long long int payload_miN; unsigned long long int payload_miN;
unsigned long long int payload_miP; unsigned long long int payload_miP;
int p25vc; int p25vc;
@@ -594,9 +633,19 @@ typedef struct
unsigned long long int K2; unsigned long long int K2;
unsigned long long int K3; unsigned long long int K3;
unsigned long long int K4; unsigned long long int K4;
int M; uint8_t forced_alg_id;
int menuopen; int menuopen;
//AES Key Segments
unsigned long long int A1[2];
unsigned long long int A2[2];
unsigned long long int A3[2];
unsigned long long int A4[2];
int aes_key_loaded[2];
//xl specific, we need to know if the ESS is from HDU, or from LDU2
int xl_is_hdu;
unsigned int debug_audio_errors; unsigned int debug_audio_errors;
unsigned int debug_audio_errorsR; unsigned int debug_audio_errorsR;
unsigned int debug_header_errors; unsigned int debug_header_errors;
@@ -617,6 +666,7 @@ typedef struct
short pulse_raw_out_buffer; //HERE HERE short pulse_raw_out_buffer; //HERE HERE
unsigned int dmr_color_code; unsigned int dmr_color_code;
unsigned int dmr_t3_syscode;
unsigned int nxdn_last_ran; unsigned int nxdn_last_ran;
unsigned int nxdn_last_rid; unsigned int nxdn_last_rid;
unsigned int nxdn_last_tg; unsigned int nxdn_last_tg;
@@ -625,6 +675,7 @@ typedef struct
char nxdn_call_type[1024]; char nxdn_call_type[1024];
unsigned long long int dmr_lrrp_source[2]; unsigned long long int dmr_lrrp_source[2];
unsigned long long int dmr_lrrp_target[2];
NxdnElementsContent_t NxdnElementsContent; NxdnElementsContent_t NxdnElementsContent;
@@ -661,6 +712,9 @@ typedef struct
//new stuff below here //new stuff below here
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
uint8_t data_block_poc[2]; //padding octets in the header (needed for Data PDU Decryption)
uint16_t data_byte_ctr[2]; //number of bytes acculumated
uint8_t data_ks_start[2]; //where the start of the keystream should be applied to PDU data
uint8_t dmr_pdu_sf[2][24*128]; //unified pdu 'superframe' //[slot][byte] -- increased capacity to 127(+1) full rate blocks uint8_t dmr_pdu_sf[2][24*128]; //unified pdu 'superframe' //[slot][byte] -- increased capacity to 127(+1) full rate blocks
uint8_t cap_plus_csbk_bits[2][12*8*8]; //CSBK Cap+ FL initial and appended block bit storage, by slot uint8_t cap_plus_csbk_bits[2][12*8*8]; //CSBK Cap+ FL initial and appended block bit storage, by slot
uint8_t cap_plus_block_num[2]; //received block number storage -- per timeslot uint8_t cap_plus_block_num[2]; //received block number storage -- per timeslot
@@ -672,17 +726,23 @@ typedef struct
//dmr talker alias new/fixed stuff //dmr talker alias new/fixed stuff
uint8_t dmr_alias_format[2]; //per slot uint8_t dmr_alias_format[2]; //per slot
uint8_t dmr_alias_len[2]; //per slot uint8_t dmr_alias_block_len[2]; //per slot
uint8_t dmr_alias_char_size[2]; //per slot
char dmr_alias_block_segment[2][4][7][16]; //2 slots, by 4 blocks, by up to 7 alias bytes that are up to 16-bit chars char dmr_alias_block_segment[2][4][7][16]; //2 slots, by 4 blocks, by up to 7 alias bytes that are up to 16-bit chars
char dmr_embedded_gps[2][200]; //2 slots by 99 char string for string embedded gps char dmr_embedded_gps[2][600]; //2 slots by 99 char string for string embedded gps
char dmr_lrrp_gps[2][200]; //2 slots by 99 char string for string lrrp gps char dmr_lrrp_gps[2][600]; //2 slots by 99 char string for string lrrp gps
char dmr_site_parms[200]; //string for site/net info depending on type of DMR system (TIII or Con+) char dmr_site_parms[200]; //string for site/net info depending on type of DMR system (TIII or Con+)
char call_string[2][200]; //string for call information char call_string[2][200]; //string for call information
char active_channel[31][200]; //string for storing and displaying active trunking channels char active_channel[31][200]; //string for storing and displaying active trunking channels
//Generic Talker Alias String
char generic_talker_alias[2][500];
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame; dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
//event history itemized per slot
Event_History_I * event_history_s;
//new audio filter structs //new audio filter structs
LPFilter RCFilter; LPFilter RCFilter;
HPFilter HRCFilter; HPFilter HRCFilter;
@@ -712,6 +772,23 @@ typedef struct
int DMRvcL; int DMRvcL;
int DMRvcR; int DMRvcR;
//keystream octet and bit arrays
uint8_t ks_octetL[129*18]; //arbitary size, but large enough for the largest packed PDUs
uint8_t ks_octetR[129*18]; //arbitary size, but large enough for the largest packed PDUs
uint8_t ks_bitstreamL[128*18*8]; //arbitary size, but large enough for the largest PDUs
uint8_t ks_bitstreamR[129*18*8]; //arbitary size, but large enough for the largest PDUs
int octet_counter;
long int bit_counterL;
long int bit_counterR;
//AES Specific Variables
uint8_t aes_key[32]; //was 64 for some reason
uint8_t aes_iv[16];
uint8_t aes_ivR[16];
//NXDN DES and AES, signal new VCALL_IV and new IV
uint8_t nxdn_new_iv; //1 when a new IV comes in, else 0
short int dmr_encL; short int dmr_encL;
short int dmr_encR; short int dmr_encR;
@@ -761,6 +838,8 @@ typedef struct
unsigned short esk_mask; unsigned short esk_mask;
unsigned long long int edacs_site_id; unsigned long long int edacs_site_id;
uint32_t edacs_sys_id;
uint32_t edacs_area_code;
int edacs_lcn_count; //running tally of lcn's observed on edacs system int edacs_lcn_count; //running tally of lcn's observed on edacs system
int edacs_cc_lcn; //current lcn for the edacs control channel int edacs_cc_lcn; //current lcn for the edacs control channel
int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to) int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to)
@@ -809,6 +888,7 @@ typedef struct
uint8_t nxdn_sacch_frame_segcrc[4]; uint8_t nxdn_sacch_frame_segcrc[4];
uint8_t nxdn_alias_block_number; uint8_t nxdn_alias_block_number;
char nxdn_alias_block_segment[4][4][8]; char nxdn_alias_block_segment[4][4][8];
uint16_t nxdn_pn95_seed;
//site/srv/cch info //site/srv/cch info
char nxdn_location_category[14]; char nxdn_location_category[14];
@@ -822,7 +902,7 @@ typedef struct
uint8_t nxdn_bw; uint8_t nxdn_bw;
//multi-key array //multi-key array
unsigned long long int rkey_array[0xFFFF]; unsigned long long int rkey_array[0x1FFFF];
int keyloader; //let us know the keyloader is active int keyloader; //let us know the keyloader is active
//dmr late entry mi //dmr late entry mi
@@ -863,15 +943,11 @@ typedef struct
//M17 Storage //M17 Storage
uint8_t m17_lsf[360]; uint8_t m17_lsf[360];
uint8_t m17_pkt[850]; uint8_t m17_pkt[850];
uint8_t m17_pbc_ct; //pbc packet counter
uint8_t m17_str_dt; //stream contents uint8_t m17_str_dt; //stream contents
unsigned long long int m17_dst; unsigned long long int m17_dst;
unsigned long long int m17_src; unsigned long long int m17_src;
uint8_t m17_can; //can value that was decoded from signal 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_dst_csd[20];
char m17_src_csd[20]; char m17_src_csd[20];
@@ -880,17 +956,21 @@ typedef struct
char m17_dst_str[50]; char m17_dst_str[50];
uint8_t m17_meta[16]; //packed meta 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; //enc type
uint8_t m17_enc_st; //scrambler or data subtye 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 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 //misc str storage
char str50a[50]; char str50a[50];
char str50b[50]; char str50b[50];
char str50c[50]; char str50c[50];
char m17dat[50]; //user supplied m17 data input string
char m17sms[800]; //user supplied sms text string
//Codec2 //Codec2
#ifdef USE_CODEC2 #ifdef USE_CODEC2
@@ -898,6 +978,33 @@ typedef struct
struct CODEC2 *codec2_1600; //M17 halfrate struct CODEC2 *codec2_1600; //M17 halfrate
#endif #endif
//tyt_ap=1 active
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;
//anytone bp
int any_bp;
//baofeng ap
int baofeng_ap;
//connect systems ee
int csi_ee;
//generic ks
int straight_ks;
int straight_mod;
uint8_t static_ks_bits[2][882];
int static_ks_counter[2];
} dsd_state; } dsd_state;
/* /*
@@ -907,13 +1014,6 @@ typedef struct
//M17 Sync Patterns //M17 Sync Patterns
#define M17_LSF "11113313" #define M17_LSF "11113313"
#define M17_STR "33331131" #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 FUSION_SYNC "31111311313113131131"
#define INV_FUSION_SYNC "13333133131331313313" #define INV_FUSION_SYNC "13333133131331313313"
@@ -1019,6 +1119,7 @@ void closePulseInput (dsd_opts * opts);
void closePulseOutput (dsd_opts * opts); void closePulseOutput (dsd_opts * opts);
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state); void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state);
void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state); void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state);
void writeSynthesizedVoiceMS (dsd_opts * opts, dsd_state * state); //short mono to stereo wav file
void playSynthesizedVoice (dsd_opts * opts, dsd_state * state); //short mono output slot 1 void playSynthesizedVoice (dsd_opts * opts, dsd_state * state); //short mono output slot 1
void playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state); //short mono output slot 2 void playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state); //short mono output slot 2
//new float stuff //new float stuff
@@ -1050,6 +1151,9 @@ void pa_sourcelist_cb (pa_context *c, const pa_source_info *l, int eol, void *us
int pa_get_devicelist (pa_devicelist_t *input, pa_devicelist_t *output); int pa_get_devicelist (pa_devicelist_t *input, pa_devicelist_t *output);
int pulse_list(); int pulse_list();
//parse a user string into a uint8_t array
uint16_t parse_raw_user_string (char * input, uint8_t * output);
int getDibit (dsd_opts * opts, dsd_state * state); int getDibit (dsd_opts * opts, dsd_state * state);
int get_dibit_and_analog_signal (dsd_opts * opts, dsd_state * state, int * out_analog_signal); int get_dibit_and_analog_signal (dsd_opts * opts, dsd_state * state, int * out_analog_signal);
int digitize (dsd_opts* opts, dsd_state* state, int symbol); int digitize (dsd_opts* opts, dsd_state* state, int symbol);
@@ -1062,6 +1166,11 @@ void PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d);
void PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d); void PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d);
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d); int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
int readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d); int readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
void keyring(dsd_opts * opts, dsd_state * state);
void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state);
void ambe2_codeword_print_f (dsd_opts * opts, char ambe_fr[4][24]);
void ambe2_codeword_print_b (dsd_opts * opts, char ambe_fr[4][24]);
void ambe2_codeword_print_i (dsd_opts * opts, char ambe_fr[4][24]);
void openMbeInFile (dsd_opts * opts, dsd_state * state); void openMbeInFile (dsd_opts * opts, dsd_state * state);
void closeMbeOutFile (dsd_opts * opts, dsd_state * state); void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
void closeMbeOutFileR (dsd_opts * opts, dsd_state * state); //tdma slot 2 void closeMbeOutFileR (dsd_opts * opts, dsd_state * state); //tdma slot 2
@@ -1070,9 +1179,15 @@ void openMbeOutFileR (dsd_opts * opts, dsd_state * state); //tdma slot 2
void openWavOutFile (dsd_opts * opts, dsd_state * state); void openWavOutFile (dsd_opts * opts, dsd_state * state);
void openWavOutFileL (dsd_opts * opts, dsd_state * state); void openWavOutFileL (dsd_opts * opts, dsd_state * state);
void openWavOutFileR (dsd_opts * opts, dsd_state * state); void openWavOutFileR (dsd_opts * opts, dsd_state * state);
void openWavOutFileLR (dsd_opts * opts, dsd_state * state); //stereo wav file for tdma decoded speech
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state); 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_delete_wav_file(SNDFILE * wav_file, char * wav_out_filename);
void openSymbolOutFile (dsd_opts * opts, dsd_state * state); void openSymbolOutFile (dsd_opts * opts, dsd_state * state);
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state); void closeSymbolOutFile (dsd_opts * opts, dsd_state * state);
void rotate_symbol_out_file (dsd_opts * opts, dsd_state * state);
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample); void writeRawSample (dsd_opts * opts, dsd_state * state, short sample);
void closeWavOutFile (dsd_opts * opts, dsd_state * state); void closeWavOutFile (dsd_opts * opts, dsd_state * state);
void closeWavOutFileL (dsd_opts * opts, dsd_state * state); void closeWavOutFileL (dsd_opts * opts, dsd_state * state);
@@ -1122,13 +1237,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 processDSTAR_SD (dsd_opts * opts, dsd_state * state, uint8_t * sd); //DSTAR Slow Data
void processYSF(dsd_opts * opts, dsd_state * state); //YSF void processYSF(dsd_opts * opts, dsd_state * state); //YSF
void processM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR) 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 processP2(dsd_opts * opts, dsd_state * state); //P2
void processTSBK(dsd_opts * opts, dsd_state * state); //P25 Trunking Single Block 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) void processMPDU(dsd_opts * opts, dsd_state * state); //P25 Multi Block PDU (SAP 0x61 FMT 0x15 or 0x17 for Trunking Blocks)
@@ -1150,11 +1258,13 @@ void unpack_ambe (uint8_t * input, char * ambe);
void ncursesOpen (dsd_opts * opts, dsd_state * state); void ncursesOpen (dsd_opts * opts, dsd_state * state);
void ncursesPrinter (dsd_opts * opts, dsd_state * state); void ncursesPrinter (dsd_opts * opts, dsd_state * state);
void ncursesMenu (dsd_opts * opts, dsd_state * state);
uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c);
void ncursesClose (); void ncursesClose ();
//new NXDN Functions start here! //new NXDN Functions start here!
void nxdn_frame (dsd_opts * opts, dsd_state * state); void nxdn_frame (dsd_opts * opts, dsd_state * state);
void nxdn_descramble (uint8_t dibits[], int len); void nxdn_pn95_dibit_scrambler(dsd_state * state, uint8_t * dibits, int len);
//nxdn deinterleaving/depuncturing functions //nxdn deinterleaving/depuncturing functions
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]); 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_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
@@ -1163,7 +1273,10 @@ void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[3
//type-d 'idas' deinterleaving/depuncturing functions //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_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); void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
//end //DCR Mode
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t bits[144], uint8_t lich);
//MT and Voice
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType); void nxdn_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]); 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) //Osmocom OP25 12 Rate Trellis Decoder (for NXDN, M17, YSF, etc)
@@ -1185,6 +1298,12 @@ void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsign
void CNXDNConvolution_init(); void CNXDNConvolution_init();
//libM17 viterbi decoder //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(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); 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); void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos);
@@ -1210,6 +1329,10 @@ void NXDN_decode_site_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_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
//Type-D SCCH Message Decoder //Type-D SCCH Message Decoder
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction); 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);
int utf8_encode(int c, char *buf);
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state); void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
@@ -1235,7 +1358,7 @@ rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword);
//DMR CRC Functions //DMR CRC Functions
uint16_t ComputeCrcCCITT(uint8_t * DMRData); uint16_t ComputeCrcCCITT(uint8_t * DMRData);
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len); uint16_t ComputeCrcCCITT16d(const uint8_t * buf, uint32_t len);
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask); uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
uint8_t ComputeCrc5Bit(uint8_t * DMRData); uint8_t ComputeCrc5Bit(uint8_t * DMRData);
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData); uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData);
@@ -1245,23 +1368,36 @@ uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData);
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst); void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
void dmr_data_sync (dsd_opts * opts, dsd_state * state); void dmr_data_sync (dsd_opts * opts, dsd_state * state);
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors); void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors, uint8_t type); void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t * IrrecoverableErrors, uint8_t type);
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_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[]); 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]); 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 dmr_34(uint8_t * input, uint8_t treturn[18]); //simplier trellis decoder
void beeper (dsd_opts * opts, dsd_state * state, int lr); //the tone beeper function 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 void dmr_gateway_identifier (uint32_t source, uint32_t target); //translate special addresses
//Embedded Alias and GPS reports //Embedded Alias and GPS reports
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]); void dmr_talker_alias_lc_header (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits);
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]); void dmr_talker_alias_lc_blocks (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t block_num, uint8_t * lc_bits);
void dmr_talker_alias_lc_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t block_num, uint8_t char_size, uint16_t end);
void apx_embedded_alias_test_phase1 (dsd_opts * opts, dsd_state * state);
void apx_embedded_alias_header_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits);
void apx_embedded_alias_header_phase2 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits);
void apx_embedded_alias_blocks_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits);
void apx_embedded_alias_blocks_phase2 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits);
void apx_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t num_bits, uint8_t * input);
void apx_embedded_alias_dump (dsd_opts * opts, dsd_state * state, uint8_t slot, uint16_t num_bytes, uint8_t * input, uint8_t * decoded);
void l3h_embedded_alias_blocks_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits);
void l3h_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t len, uint8_t * input);
void tait_iso7_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t len, uint8_t * input);
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]); 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 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 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_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 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_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input);
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);
//"DMR STEREO" //"DMR STEREO"
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state); void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
@@ -1273,18 +1409,53 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified) //dmr data header and multi block types (header, 1/2, 3/4, 1, Unified)
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors); void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type); void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type);
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
//dmr pdu handling
void dmr_sd_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * DMR_PDU);
void dmr_udp_comp_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * DMR_PDU);
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state); void dmr_reset_blocks (dsd_opts * opts, dsd_state * state);
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]); void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint16_t len, uint32_t source, uint32_t dest, uint8_t * DMR_PDU);
void dmr_locn (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]); void dmr_locn (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * DMR_PDU);
//p25 pdu handling
uint8_t p25_decrypt_pdu(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t alg_id, uint16_t key_id, unsigned long long int mi, int len);
uint8_t p25_decode_es_header(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t * sap, int * ptr, int len);
uint8_t p25_decode_es_header_2(dsd_opts * opts, dsd_state * state, uint8_t * input, int * ptr, int len);
void p25_decode_extended_address(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t * sap, int * ptr);
void p25_decode_pdu_trunking(dsd_opts * opts, dsd_state * state, uint8_t * mpdu_byte);
void p25_decode_pdu_header(dsd_opts * opts, dsd_state * state, uint8_t * input);
void p25_decode_pdu_data(dsd_opts * opts, dsd_state * state, uint8_t * input, int len);
void p25_decode_rsp(uint8_t C, uint8_t T, uint8_t S, char * rsp_string);
void p25_decode_sap(uint8_t SAP, char * sap_string);
//misc pdu
void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * input);
void decode_cellocator(dsd_opts * opts, dsd_state * state, uint8_t * input, int len);
void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len);
//Time and Date Functions //Time and Date Functions
char * getTime(); char * getTime();
char * getTimeC(); char * getTimeC();
char * getTimeN(time_t t); char * getTimeN(time_t t);
char * getTimeF(time_t t);
char * getDate(); char * getDate();
char * getDateH(); char * getDateH();
char * getDateS();
char * getDateN(time_t t); char * getDateN(time_t t);
char * getDateF(time_t t);
//event history functions
void init_event_history (Event_History_I * event_struct, uint8_t start, uint8_t stop);
void push_event_history (Event_History_I * event_struct);
void write_event_to_log_file (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t swrite, char * event_string);
void watchdog_event_history (dsd_opts * opts, dsd_state * state, uint8_t slot);
void watchdog_event_current (dsd_opts * opts, dsd_state * state, uint8_t slot);
void watchdog_event_datacall (dsd_opts * opts, dsd_state * state, uint32_t src, uint32_t dst, char * data_string, uint8_t slot);
//edacs AFS things
int isCustomAfsString(dsd_state * state);
int getAfsStringLength(dsd_state * state);
int getAfsString(dsd_state * state, char * buffer, int a, int f, int s);
//dmr alg stuff //dmr alg stuff
void dmr_alg_reset (dsd_opts * opts, dsd_state * state); void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
@@ -1434,9 +1605,50 @@ int udp_socket_connect(dsd_opts * opts, dsd_state * state);
int udp_socket_connectA(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_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); 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); //RC4 function prototypes
int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data); void rc4_voice_decrypt (int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[]);
void rc4_block_output (int drop, int keylen, int meslen, uint8_t * key, uint8_t * output_blocks);
//DES function prototypes
void des_multi_keystream_output (unsigned long long int mi, unsigned long long int key_ulli, uint8_t * output, int type, int len);
void tdea_multi_keystream_output (unsigned long long int mi, uint8_t * key, uint8_t * output, int type, int len);
//AES function prototypes
void aes_ofb_keystream_output (uint8_t * iv, uint8_t * key, uint8_t * output, int type, int nblocks);
void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de);
void aes_cbc_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in, uint8_t * out, int type, int nblocks, int de);
void aes_cfb_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in, uint8_t * out, int type, int nblocks, int de);
void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type);
//Tytera / Retevis / Anytone / Kenwood / Misc DMR Encryption Modes
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);
void retevis_rc2_keystream_creation(dsd_state *state, char *input);
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input);
void anytone_bp_keystream_creation(dsd_state * state, char * input);
void baofeng_ap_pc5_keystream_creation(dsd_state *state, char *input);
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24]);
void straight_mod_xor_keystream_creation(dsd_state * state, char * input);
//Kirisun
uint32_t kirisun_lfsr(unsigned long long int mi);
void kirisun_adv_keystream_creation(dsd_state *state);
void kirisun_uni_keystream_creation(dsd_state *state);
//Hytera Enhanced
void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value);
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len);
void hytera_enhanced_alg_refresh(dsd_state * state);
//LFSR to expand either a DMR 32-bit or P25/NXDN 64-bit MI into a 128-bit IV for AES
void lfsr_64_to_128(uint8_t * iv);
void LFSR128(dsd_state * state);
void LFSR128n(dsd_state * state);
void LFSR128d(dsd_state * state);
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -1459,6 +1671,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 ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
int isch_lookup (uint64_t isch); //isch map lookup 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 #ifdef __cplusplus
} }
#endif #endif

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

95
include/pc4.h Normal file
View File

@@ -0,0 +1,95 @@
#include <stdint.h>
#include <stddef.h>
#ifndef TYT_AP
#define TYT_AP
#define nbround 254
#define n1 264
#define UNUSEDPC4(x) ((void)x)
/* Structure holding all PC4 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];
} PC4Context;
/* Global context accessible from all .c files */
extern PC4Context ctx;
/* Public API */
void create_keys(PC4Context *ctx, unsigned char key1[], size_t size1);
void pc4encrypt(PC4Context *ctx);
void pc4decrypt(PC4Context *ctx);
void binhex(PC4Context *ctx, short *z, int length);
void hexbin(PC4Context *ctx, short *q, uint8_t w, uint8_t hex);
static void u64_to_bytes_be(uint64_t val, unsigned char *out);
/* Encrypt a 49-bit frame (original flow) */
static void encrypt_frame_49(short frame_bits_in[49]) {
for (int i = 0; i < 49; i++) ctx.bits[i] = frame_bits_in[i];
for (int i = 0; i < 49; i++) ctx.temp[i] = ctx.bits[ctx.array[i]];
for (int i = 0; i < 49; i++) ctx.bits[i] = ctx.temp[i];
ctx.ptconvert = 0;
binhex(&ctx, ctx.bits, 48);
pc4encrypt(&ctx);
for (int q = 0; q < 6; q++) {
uint8_t w = (uint8_t)(q * 8);
hexbin(&ctx, ctx.bits, w, ctx.convert[q]);
}
ctx.bits[48] = (short)(ctx.bits[48] ^ ctx.totb);
for (int i = 0; i < 49; i++) ctx.temp[ctx.array2[i]] = ctx.bits[i];
for (int i = 0; i < 49; i++) ctx.bits[i] = ctx.temp[i];
}
/* Decrypt a 49-bit frame (original flow) */
static void decrypt_frame_49(short frame_bits_in[49]) {
for (int i = 0; i < 49; i++) ctx.bits[i] = frame_bits_in[i];
for (int i = 0; i < 49; i++) ctx.temp[i] = ctx.bits[ctx.array2[i]];
for (int i = 0; i < 49; i++) ctx.bits[i] = ctx.temp[i];
ctx.ptconvert = 0;
binhex(&ctx, ctx.bits, 48);
pc4decrypt(&ctx);
for (int q = 0; q < 6; q++) {
uint8_t w = (uint8_t)(q * 8);
hexbin(&ctx, ctx.bits, w, ctx.convert[q]);
}
ctx.bits[48] = (short)(ctx.bits[48] ^ ctx.totb);
for (int i = 0; i < 49; i++) ctx.temp[ctx.array[i]] = ctx.bits[i];
for (int i = 0; i < 49; i++) ctx.bits[i] = ctx.temp[i];
}
/* 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);
}
}
#endif

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

48
include/synctype.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef SYNCTYPE_H
#define SYNCTYPE_H
static const char * SyncTypes[44] = {
"P25p1",
"P25p1",
"X2TDMA DATA",
"X2TDMA DATA",
"X2TDMA VOICE",
"X2TDMA VOICE",
"DSTAR", //voice
"DSTAR", //voice
"M17",
"M17",
"DMR", //10
"DMR",
"DMR",
"DMR",
"EDACS/PV",
"EDACS/PV",
"M17", //M17 Voice Stream
"M17", //M17 Voice Stream
"DSTAR", //header
"DSTAR", //header
"dPMR", //20
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"NXDN",
"NXDN",
"YSF", //30
"YSF", //31
"DMR",
"DMR",
"DMR",
"P25p2",
"P25p2",
"EDACS/PV", //37
"EDACS/PV", //38
"ANALOG", //39 Generic Sync Types
"DIGITAL" //40 Generic Sync Types
};
#endif

View File

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

View File

@@ -1,51 +0,0 @@
diff --git a/src/dmr_pi.c b/src/dmr_pi.c
index 55dfb83..33fb3ba 100644
--- a/src/dmr_pi.c
+++ b/src/dmr_pi.c
@@ -14,6 +14,9 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
{
UNUSED2(opts, CRCCorrect);
+ //force slot to 0 if using dmr mono handling
+ if (opts->dmr_mono == 1) state->currentslot = 0;
+
if((IrrecoverableErrors == 0))
{
diff --git a/src/dsd_frame.c b/src/dsd_frame.c
index 3b2a3e1..ba32618 100644
--- a/src/dsd_frame.c
+++ b/src/dsd_frame.c
@@ -160,7 +160,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
sprintf (state->slot2light, " slot2 ");
//we can safely open MBE on any MS or mono handling
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
- if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state);
+ // if (opts->p25_trunk == 0) //line disabled to allow mono mode while trunking
+ dmrMSBootstrap (opts, state);
}
if (opts->dmr_mono == 1 && state->synctype == 32)
{
diff --git a/src/dsd_main.c b/src/dsd_main.c
index 7be5912..8db6dc3 100644
--- a/src/dsd_main.c
+++ b/src/dsd_main.c
@@ -2302,13 +2302,13 @@ main (int argc, char **argv)
state.rf_mod = 0; //
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 2;
- opts.dmr_mono = 0;
- opts.dmr_stereo = 1;
+ opts.dmr_mono = 1;
+ opts.dmr_stereo = 0; //not sure if this will break anything
state.dmr_stereo = 0; //0
// opts.setmod_bw = 7000;
- sprintf (opts.output_name, "DMR Stereo");
- fprintf (stderr,"-fr / DMR Mono switch has been deprecated.\n");
- fprintf (stderr,"Decoding DMR Stereo BS/MS Simplex\n");
+ sprintf (opts.output_name, "DMR Mono");
+ // fprintf (stderr,"-fr / DMR Mono switch enabled. \n NOTE: Some ENC options in Slot 2 may not work properly.\n");
+ fprintf (stderr,"\n Decoding DMR Stereo BS/MS Simplex in Mono Single Slot Only Configuration.\n");
}
else if (optarg[0] == 'm')

View File

@@ -1,152 +0,0 @@
diff --git a/src/dmr_pi.c b/src/dmr_pi.c
index 7542c61..d1c8e39 100644
--- a/src/dmr_pi.c
+++ b/src/dmr_pi.c
@@ -14,6 +14,9 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
{
UNUSED2(opts, CRCCorrect);
+ //force slot to 0 if using dmr mono handling
+ if (opts->dmr_mono == 1) state->currentslot = 0;
+
if((IrrecoverableErrors == 0))
{
diff --git a/src/dsd_audio2.c b/src/dsd_audio2.c
index 756e468..13316df 100644
--- a/src/dsd_audio2.c
+++ b/src/dsd_audio2.c
@@ -988,6 +988,9 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
memset (stereo_samp2, 0, sizeof(stereo_samp2));
memset (stereo_samp3, 0, sizeof(stereo_samp3));
+ if (opts->dmr_mono == 1)
+ memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4));
+
//dmr enc checkdown for whether or not to fill the stereo sample or not for playback or writing
encL = encR = 0;
encL = (state->dmr_so >> 6) & 0x1;
@@ -1070,8 +1073,16 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
}
//flag either left or right as 'enc' to mute if B
- if (strcmp(modeL, "B") == 0) encL = 1;
- if (strcmp(modeR, "B") == 0) encR = 1;
+ if (strcmp(modeL, "B") == 0)
+ {
+ encL = 1;
+ encR = 1;
+ }
+ if (strcmp(modeR, "B") == 0)
+ {
+ encR = 1;
+ encL = 1;
+ }
//check to see if we need to enable slot and toggle slot preference here
//this method will always favor slot 2 (this is a patch anyways, so....meh)
diff --git a/src/dsd_frame.c b/src/dsd_frame.c
index 577169c..275abb6 100644
--- a/src/dsd_frame.c
+++ b/src/dsd_frame.c
@@ -160,7 +160,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
sprintf (state->slot2light, " slot2 ");
//we can safely open MBE on any MS or mono handling
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
- if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state);
+ // if (opts->p25_trunk == 0) //line disabled to allow mono mode while trunking
+ dmrMSBootstrap (opts, state);
}
if (opts->dmr_mono == 1 && state->synctype == 32)
{
diff --git a/src/dsd_main.c b/src/dsd_main.c
index 3ca7788..22462a0 100644
--- a/src/dsd_main.c
+++ b/src/dsd_main.c
@@ -1645,9 +1645,9 @@ main (int argc, char **argv)
}
#ifdef AERO_BUILD
- fprintf (stderr, "Build Version: AW (20231015) \n");
+ fprintf (stderr, "Build Version: AW MTL (20240201) \n");
#else
- fprintf (stderr, "Build Version: AW %s \n", GIT_TAG);
+ fprintf (stderr, "Build Version: AW MTL %s \n", GIT_TAG);
#endif
fprintf (stderr,"MBElib Version: %s\n", versionstr);
@@ -2474,14 +2474,18 @@ main (int argc, char **argv)
opts.mod_qpsk = 0;
opts.mod_gfsk = 0;
state.rf_mod = 0;
- opts.dmr_stereo = 1;
- opts.dmr_mono = 0;
+ // opts.dmr_stereo = 1;
+ // opts.dmr_mono = 0;
// opts.setmod_bw = 7000;
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 2;
- sprintf (opts.output_name, "DMR");
-
- fprintf (stderr,"Decoding DMR Stereo BS/MS Simplex\n");
+ opts.dmr_mono = 1;
+ opts.dmr_stereo = 0; //not sure if this will break anything
+ state.dmr_stereo = 0; //0
+ // opts.setmod_bw = 7000;
+ sprintf (opts.output_name, "DMR Mono");
+ // fprintf (stderr,"-fr / DMR Mono switch enabled. \n NOTE: Some ENC options in Slot 2 may not work properly.\n");
+ fprintf (stderr,"\n Decoding DMR Stereo BS/MS Simplex in Mono Single Slot Only Configuration.\n");
}
//change ft to only do P25 and DMR (TDMA trunking modes)
else if (optarg[0] == 't')
@@ -2558,13 +2562,13 @@ main (int argc, char **argv)
state.rf_mod = 0; //
opts.pulse_digi_rate_out = 8000;
opts.pulse_digi_out_channels = 2;
- opts.dmr_mono = 0;
- opts.dmr_stereo = 1;
+ opts.dmr_mono = 1;
+ opts.dmr_stereo = 0; //not sure if this will break anything
state.dmr_stereo = 0; //0
// opts.setmod_bw = 7000;
- sprintf (opts.output_name, "DMR Stereo");
- fprintf (stderr,"-fr / DMR Mono switch has been deprecated.\n");
- fprintf (stderr,"Decoding DMR Stereo BS/MS Simplex\n");
+ sprintf (opts.output_name, "DMR Mono");
+ // fprintf (stderr,"-fr / DMR Mono switch enabled. \n NOTE: Some ENC options in Slot 2 may not work properly.\n");
+ fprintf (stderr,"\n Decoding DMR Stereo BS/MS Simplex in Mono Single Slot Only Configuration.\n");
}
else if (optarg[0] == 'm')
diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c
index 73965dc..e5b38aa 100644
--- a/src/dsd_ncurses.c
+++ b/src/dsd_ncurses.c
@@ -2297,8 +2297,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->ncurses_compact == 1)
{
printw ("------------------------------------------------------------------------------\n");
- printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20231015)");
- printw ("------------------------------------------------------------------------------\n");
+ printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW MTL (20240328)");
+ printw ("------------------------------------------------------------------------------\n");
}
#elif LIMAZULUTWEAKS
if (opts->ncurses_compact == 1)
@@ -2336,12 +2336,12 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
#endif
#ifdef AERO_BUILD
if (i == 5) printw (" %s ", "Aero Build");
- if (i == 6) printw (" AW (20231015) \n");
+ if (i == 6) printw (" AW MTL (20240328) \n");
#elif ZDEV_BUILD
- if (i == 5) printw (" %s ", "AW ");
+ if (i == 5) printw (" %s ", "AW MTL");
if (i == 6) printw (" %s \n", GIT_TAG);
- #else
- if (i == 5) printw (" %s ", "AW ");
+ #else
+ if (i == 5) printw (" %s ", "AW MTL");
if (i == 6) printw (" %s \n", GIT_TAG);
#endif
else printw ("\n");

View File

@@ -1,277 +0,0 @@
diff --git a/include/dsd.h b/include/dsd.h
index 9ebf3f1..c5ac68a 100644
--- a/include/dsd.h
+++ b/include/dsd.h
@@ -18,16 +18,16 @@
*/
//defined by CMakeLists.txt -- Disable by using cmake -DCOLORS=OFF ..
-#ifdef PRETTY_COLORS
-#define KNRM "\x1B[0m"
-#define KRED "\x1B[31m"
-#define KGRN "\x1B[32m"
-#define KYEL "\x1B[33m"
-#define KBLU "\x1B[34m"
-#define KMAG "\x1B[35m"
-#define KCYN "\x1B[36m"
-#define KWHT "\x1B[37m"
-#else
+// #ifdef PRETTY_COLORS
+// #define KNRM "\x1B[0m"
+// #define KRED "\x1B[31m"
+// #define KGRN "\x1B[32m"
+// #define KYEL "\x1B[33m"
+// #define KBLU "\x1B[34m"
+// #define KMAG "\x1B[35m"
+// #define KCYN "\x1B[36m"
+// #define KWHT "\x1B[37m"
+// #else
#define KNRM ""
#define KRED ""
#define KGRN ""
@@ -36,7 +36,7 @@
#define KMAG ""
#define KCYN ""
#define KWHT ""
-#endif
+// #endif
#include "config.h"
#include <stdio.h>
diff --git a/src/dmr_bs.c b/src/dmr_bs.c
index bdab262..17a9e70 100644
--- a/src/dmr_bs.c
+++ b/src/dmr_bs.c
@@ -266,14 +266,14 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
{
- fprintf (stderr,"%s ", timestr);
+ // fprintf (stderr,"%s ", timestr);
if (internalslot == 0)
{
if (opts->inverted_dmr == 0)
{
- fprintf (stderr,"Sync: +DMR ");
+ // fprintf (stderr,"Sync: +DMR ");
}
- else fprintf (stderr,"Sync: -DMR ");
+ else {}//fprintf (stderr,"Sync: -DMR ");
vc1 = 7; //set to 7 so we can see that we should not be on a VC unless a framesync comes in for it first
@@ -284,9 +284,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
{
if (opts->inverted_dmr == 0)
{
- fprintf (stderr,"Sync: +DMR ");
+ // fprintf (stderr,"Sync: +DMR ");
}
- else fprintf (stderr,"Sync: -DMR ");
+ else {}//fprintf (stderr,"Sync: -DMR ");
vc2 = 7; //set to 7 so we can see that we should not be on a VC unless a framesync comes in for it first
diff --git a/src/dmr_data.c b/src/dmr_data.c
index 87f4dfd..57e5647 100644
--- a/src/dmr_data.c
+++ b/src/dmr_data.c
@@ -232,10 +232,10 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
sprintf(state->slot2light, "[sLoT2]");
}
- if (state->dmr_ms_mode == 0)
- {
- fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
- }
+ // if (state->dmr_ms_mode == 0)
+ // {
+ // fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
+ // }
// Slot type - Second part - Parity bit
for (i = 0; i < 5; i++)
@@ -292,6 +292,13 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
info[(2*i) + 99] = (1 & dibit); // bit 0
}
+
+ // if (state->dmr_ms_mode == 0 && burst != 0x09)
+ if (burst != 0x09)
+ {
+ printFrameSync (opts, state, "+DMR ", 90, NULL); //don't think the values matter
+ fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
+ }
//don't run on MS Reverse Channel Data
if (state->dmr_ms_rc == 0) dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
@@ -303,15 +310,16 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
}
//ending line break
- fprintf(stderr, "\n");
+ if (burst != 0x09)
+ fprintf(stderr, "\n");
END:
if (SlotTypeOk == 0 || cach_okay != 1)
{
- fprintf (stderr, "%s", KRED);
- fprintf (stderr, "| CACH/Burst FEC ERR");
- fprintf (stderr, "%s", KNRM);
- fprintf (stderr, "\n");
+ // fprintf (stderr, "%s", KRED);
+ // fprintf (stderr, "| CACH/Burst FEC ERR");
+ // fprintf (stderr, "%s", KNRM);
+ // fprintf (stderr, "\n");
dmr_reset_blocks (opts, state); //failsafe to reset all data header and blocks when bad tact or slottype
}
diff --git a/src/dmr_dburst.c b/src/dmr_dburst.c
index fe81158..078524d 100644
--- a/src/dmr_dburst.c
+++ b/src/dmr_dburst.c
@@ -205,7 +205,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
//flag off prop head when not looking at data blocks
if (databurst != 0x6 && databurst != 0x7 && databurst != 0x8 && databurst != 0xA && databurst != 0xB) state->data_p_head[slot] = 0;
- if (databurst != 0xEB)
+ if (databurst != 0xEB && databurst != 0x09)
{
if (state->dmr_ms_mode == 0)
{
diff --git a/src/dmr_flco.c b/src/dmr_flco.c
index 839b085..e43b568 100644
--- a/src/dmr_flco.c
+++ b/src/dmr_flco.c
@@ -790,21 +790,23 @@ uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25])
crc = crc8_ok(slco_bits, 36);
//only run SLCO on good everything
- if (h1 && h2 && h3 && crc) dmr_slco (opts, state, slco_bits);
- else
- {
- //this line break issue is wracking on my OCD for clean line breaks
- if (opts->payload == 1 && state->dmrburstL == 16 && state->currentslot == 0) ; //no line break if current slot is voice with payload enabled
- else if (opts->payload == 1 && state->dmrburstR == 16 && state->currentslot == 1) ; //no line break if current slot is voice with payload enabled
- else fprintf (stderr, "\n");
- fprintf (stderr, "%s", KRED);
- fprintf (stderr, " SLCO CRC ERR");
- fprintf (stderr, "%s", KNRM);
- if (opts->payload == 1 && state->dmrburstL == 16 && state->currentslot == 0) //if current slot is voice with payload enabled
- fprintf (stderr, "\n");
- else if (opts->payload == 1 && state->dmrburstR == 16 && state->currentslot == 1) //if current slot is voice with payload enabled
- fprintf (stderr, "\n");
- }
+ if (h1 && h2 && h3 && crc) {} //dmr_slco (opts, state, slco_bits);
+ // else
+ // {
+ // //this line break issue is wracking on my OCD for clean line breaks
+ // if (opts->payload == 1 && state->dmrburstL == 16 && state->currentslot == 0) {} //no line break if current slot is voice with payload enabled
+ // else if (opts->payload == 1 && state->dmrburstR == 16 && state->currentslot == 1) {} //no line break if current slot is voice with payload enabled
+ // else {} //fprintf (stderr, "\n");
+ // // fprintf (stderr, "%s", KRED);
+ // // fprintf (stderr, " SLCO CRC ERR");
+ // // fprintf (stderr, "%s", KNRM);
+ // if (opts->payload == 1 && state->dmrburstL == 16 && state->currentslot == 0) //if current slot is voice with payload enabled
+ // fprintf (stderr, "\n");
+ // else if (opts->payload == 1 && state->dmrburstR == 16 && state->currentslot == 1) //if current slot is voice with payload enabled
+ // fprintf (stderr, "\n");
+ // }
+
+ UNUSED(opts);
}
return (err); //return err value based on success or failure, even if we aren't checking it
@@ -935,8 +937,11 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
uint8_t xpt_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 8); //priority TG hash
//initial line break
- fprintf (stderr, "\n");
- fprintf (stderr, "%s", KYEL);
+ if (slco != 0)
+ {
+ fprintf (stderr, "\n");
+ fprintf (stderr, "%s", KYEL);
+ }
if (slco == 0x2) //C_SYS_Parms
{
@@ -973,7 +978,8 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
else sprintf (state->dmr_site_parms, "TIII %s:%d-%d;%04X; ", model_str, net, site, syscode);
}
else if (slco == 0x0) //null
- fprintf (stderr, " SLCO NULL ");
+ // fprintf (stderr, " SLCO NULL ");
+ {}
else if (slco == 0x1)
{
fprintf (stderr, " Activity Update"); //102 361-2 7.1.3.2
diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c
index 062bb20..f47a447 100644
--- a/src/dsd_frame_sync.c
+++ b/src/dsd_frame_sync.c
@@ -940,7 +940,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
sprintf(state->ftype, "DMR ");
if (opts->errorbars == 1)
{
- printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
+ // printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
}
state->lastsynctype = 10;
state->last_cc_sync_time = time(NULL);
@@ -1068,7 +1068,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
sprintf(state->ftype, "DMR ");
if (opts->errorbars == 1) //&& opts->dmr_stereo == 0
{
- printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
+ // printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
}
state->lastsynctype = 13;
state->last_cc_sync_time = time(NULL);
@@ -1443,7 +1443,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
{
if ((opts->errorbars == 1) && (opts->verbose > 1) && (state->carrier == 1))
{
- fprintf (stderr,"Sync: no sync\n");
+ // fprintf (stderr,"Sync: no sync\n");
// fprintf (stderr,"Press CTRL + C to close.\n");
}
diff --git a/src/dsd_main.c b/src/dsd_main.c
index 98f2ead..b09cb81 100644
--- a/src/dsd_main.c
+++ b/src/dsd_main.c
@@ -1687,7 +1687,7 @@ main (int argc, char **argv)
}
#ifdef AERO_BUILD
- fprintf (stderr, "Build Version: AW (20231015) \n");
+ fprintf (stderr, "Build Version: AW (20240523) \n");
#else
fprintf (stderr, "Build Version: AW %s \n", GIT_TAG);
#endif
diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c
index a5442af..514b9ae 100644
--- a/src/dsd_ncurses.c
+++ b/src/dsd_ncurses.c
@@ -2423,7 +2423,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->ncurses_compact == 1)
{
printw ("------------------------------------------------------------------------------\n");
- printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20231015)");
+ printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20240523)");
printw ("------------------------------------------------------------------------------\n");
}
#elif LIMAZULUTWEAKS
@@ -2462,8 +2462,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (i == 4 && opts->frame_m17 == 1) printw (" CODEC2");
#endif
#ifdef AERO_BUILD
- if (i == 5) printw (" %s ", "Aero Build");
- if (i == 6) printw (" AW (20231015) \n");
+ if (i == 5) printw (" %s ", "Cretu Build");
+ if (i == 6) printw (" AW (20240523) \n");
#elif ZDEV_BUILD
if (i == 5) printw (" %s ", "AW ");
if (i == 6) printw (" %s \n", GIT_TAG);

View File

@@ -1,120 +0,0 @@
diff --git a/src/dmr_block.c b/src/dmr_block.c
index 893b0e8..e7f9de7 100644
--- a/src/dmr_block.c
+++ b/src/dmr_block.c
@@ -783,6 +783,6 @@ void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
//reset some strings -- resetting call string here causes random blink on ncurses terminal (cap+)
// sprintf (state->call_string[0], "%s", " "); //21 spaces
// sprintf (state->call_string[1], "%s", " "); //21 spaces
- sprintf (state->dmr_lrrp_gps[0], "%s", "");
- sprintf (state->dmr_lrrp_gps[1], "%s", "");
+ // sprintf (state->dmr_lrrp_gps[0], "%s", ""); //disabled for dmr data call persistence #174
+ // sprintf (state->dmr_lrrp_gps[1], "%s", ""); //disabled for dmr data call persistence #174
}
\ No newline at end of file
diff --git a/src/dmr_data.c b/src/dmr_data.c
index 3541152..3cae906 100644
--- a/src/dmr_data.c
+++ b/src/dmr_data.c
@@ -267,7 +267,20 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
state->color_code_ok = SlotTypeOk;
//not sure why I still have two variables for this, need to look and see what state->color_code still ties into
- if (SlotTypeOk == 1) state->dmr_color_code = state->color_code;
+ if (SlotTypeOk == 1)
+ {
+ if (state->dmr_color_code != state->color_code)
+ {
+ //Zero out values on color code change #174
+ state->lasttg = 0;
+ state->lasttgR =0;
+ state->lastsrc = 0;
+ state->lastsrcR = 0;
+ sprintf (state->dmr_lrrp_gps[0], "%s", "");
+ sprintf (state->dmr_lrrp_gps[1], "%s", "");
+ state->dmr_color_code = state->color_code;
+ }
+ }
/* Reconstitute the burst type */
burst = (uint8_t)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
diff --git a/src/dsd_main.c b/src/dsd_main.c
index 5531ef3..5598793 100644
--- a/src/dsd_main.c
+++ b/src/dsd_main.c
@@ -257,10 +257,10 @@ if(opts->frame_m17 == 1) //&& opts->audio_in_type == 5
//zero out right away if not trunking
if (opts->p25_trunk == 0)
{
- state->lasttg = 0;
- state->lastsrc = 0;
- state->lasttgR = 0;
- state->lastsrcR = 0;
+ // state->lasttg = 0; //disabled for dmr voice call persistence #174
+ // state->lastsrc = 0; //disabled for dmr voice call persistence #174
+ // state->lasttgR = 0; //disabled for dmr voice call persistence #174
+ // state->lastsrcR = 0; //disabled for dmr voice call persistence #174
//zero out vc frequencies?
state->p25_vc_freq[0] = 0;
@@ -290,10 +290,10 @@ if(opts->frame_m17 == 1) //&& opts->audio_in_type == 5
//mainly bugfix for ncurses and per call wavs (edacs) and also signal fade, etc
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && time(NULL) - state->last_cc_sync_time > opts->trunk_hangtime)
{
- state->lasttg = 0;
- state->lastsrc = 0;
- state->lasttgR = 0;
- state->lastsrcR = 0;
+ // state->lasttg = 0; //disabled for dmr voice call persistence #174
+ // state->lastsrc = 0; //disabled for dmr voice call persistence #174
+ // state->lasttgR = 0; //disabled for dmr voice call persistence #174
+ // state->lastsrcR = 0; //disabled for dmr voice call persistence #174
}
@@ -431,7 +431,7 @@ if(opts->frame_m17 == 1) //&& opts->audio_in_type == 5
memset(state->dmr_alias_len, 0, sizeof(state->dmr_alias_len));
memset(state->dmr_alias_block_segment, 0, sizeof(state->dmr_alias_block_segment));
memset(state->dmr_embedded_gps, 0, sizeof(state->dmr_embedded_gps));
- memset(state->dmr_lrrp_gps, 0, sizeof(state->dmr_lrrp_gps));
+ // memset(state->dmr_lrrp_gps, 0, sizeof(state->dmr_lrrp_gps));
// memset(state->active_channel, 0, sizeof(state->active_channel));
diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c
index 4646e77..e9ecab1 100644
--- a/src/dsd_ncurses.c
+++ b/src/dsd_ncurses.c
@@ -3104,7 +3104,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
//LRRP
- if(state->dmrburstL != 16) //only during data and no trunking
+ // if(state->dmrburstL != 16) //only during data and no trunking //disabled for dmr data call persistence #174
{
attron(COLOR_PAIR(4));
printw ("%s", state->dmr_lrrp_gps[0]);
@@ -3298,7 +3298,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
//LRRP
- if(state->dmrburstR != 16) //only during data and no trunking
+ // if(state->dmrburstR != 16) //only during data and no trunking //disabled for dmr data call persistence #174
{
attron(COLOR_PAIR(4));
printw ("%s", state->dmr_lrrp_gps[1]);
@@ -3978,10 +3978,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
dmr_reset_blocks (opts, state);
//zero out additional items
- state->lasttg = 0;
- state->lasttgR = 0;
- state->lastsrc = 0;
- state->lastsrcR = 0;
+ // state->lasttg = 0; //disabled for dmr voice call persistence #174
+ // state->lasttgR = 0; //disabled for dmr voice call persistence #174
+ // state->lastsrc = 0; //disabled for dmr voice call persistence #174
+ // state->lastsrcR = 0; //disabled for dmr voice call persistence #174
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;

View File

@@ -1,499 +0,0 @@
diff --git a/src/dmr_csbk.c b/src/dmr_csbk.c
index 6ee44d6..45502a4 100644
--- a/src/dmr_csbk.c
+++ b/src/dmr_csbk.c
@@ -520,6 +520,26 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
//rigctl
if (opts->use_rigctl == 1)
{
+
+ if (state->currentslot == 0)
+ {
+ //close any perCall wav files that are open
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
+ {
+ closeWavOutFileL (opts, state);
+ opts->wav_out_f = NULL;
+ }
+ }
+ else if (state->currentslot == 1)
+ {
+ //close any perCall wav files that are open
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
+ {
+ closeWavOutFileR (opts, state);
+ opts->wav_out_fR = NULL;
+ }
+ }
+
//Guess I forgot to add this condition here
if (GetCurrentFreq(opts->rigctl_sockfd) != state->p25_cc_freq)
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away from current frequency
@@ -554,6 +574,26 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
else if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
+
+ if (state->currentslot == 0)
+ {
+ //close any perCall wav files that are open
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
+ {
+ closeWavOutFileL (opts, state);
+ opts->wav_out_f = NULL;
+ }
+ }
+ else if (state->currentslot == 1)
+ {
+ //close any perCall wav files that are open
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
+ {
+ closeWavOutFileR (opts, state);
+ opts->wav_out_fR = NULL;
+ }
+ }
+
//Guess I forgot to add this condition here
uint32_t tempf = (uint32_t)state->p25_cc_freq;
if (opts->rtlsdr_center_freq != tempf)
diff --git a/src/dmr_flco.c b/src/dmr_flco.c
index 839b085..637debf 100644
--- a/src/dmr_flco.c
+++ b/src/dmr_flco.c
@@ -298,20 +298,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
if (type != 2) //VLC and EMB, set our target, source, so, and fid per channel
{
- if (state->currentslot == 0)
- {
- state->dmr_fid = fid;
- state->dmr_so = so;
- state->lasttg = target;
- state->lastsrc = source;
- }
- if (state->currentslot == 1)
- {
- state->dmr_fidR = fid;
- state->dmr_soR = so;
- state->lasttgR = target;
- state->lastsrcR = source;
- }
+ //moved per call further down past the call_string stuff
//update cc amd vc sync time for trunking purposes (particularly Con+)
if (opts->p25_is_tuned == 1)
@@ -337,6 +324,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
//reset gain
if (opts->floating_point == 1)
state->aout_gain = opts->audio_gain;
+ //close any perCall wav files that are open
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
+ {
+ closeWavOutFileL (opts, state);
+ opts->wav_out_f = NULL;
+ }
}
if (state->currentslot == 1)
{
@@ -350,6 +343,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
//reset gain
if (opts->floating_point == 1)
state->aout_gainR = opts->audio_gain;
+ //close any perCall wav files that are open
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
+ {
+ closeWavOutFileR (opts, state);
+ opts->wav_out_fR = NULL;
+ }
}
}
@@ -612,6 +611,69 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
fprintf (stderr, "Key %010llX ", state->RR);
fprintf (stderr, "%s ", KNRM);
}
+
+ if (type != 2)
+ {
+ if (state->currentslot == 0)
+ {
+ state->dmr_fid = fid;
+ state->dmr_so = so;
+ state->lasttg = target;
+ state->lastsrc = source;
+ char * timestr = NULL;
+ char * datestr = NULL;
+ timestr = getTime();
+ datestr = getDate();
+
+ //setup a call string for the per call (group/private and/or emergency)
+ char cs[200]; memset (cs, 0, 200*sizeof(char));
+ int x = 0;
+ for (int i = 0; i < 200; i++)
+ {
+ //copy over non space and non termination values from call string
+ if (i != 0 && state->call_string[0][i] != ' ' && state->call_string[0][i] != 0)
+ cs[x++] = state->call_string[0][i];
+ }
+ cs[x] = 0; //terminate
+
+ //open wav file here if not already opened
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f == NULL)
+ {
+ sprintf (opts->wav_out_file, "%s/%s %s CC %d - %s - TG %d - RD %d", opts->wav_out_dir, datestr, timestr, state->dmr_color_code, cs, target, source);
+ openWavOutFileL (opts, state);
+ }
+ free (timestr); free(datestr);
+ }
+ if (state->currentslot == 1)
+ {
+ state->dmr_fidR = fid;
+ state->dmr_soR = so;
+ state->lasttgR = target;
+ state->lastsrcR = source;
+ char * timestr = NULL;
+ char * datestr = NULL;
+ timestr = getTime();
+ datestr = getDate();
+
+ //setup a call string for the per call (group/private and/or emergency)
+ char cs[200]; memset (cs, 0, 200*sizeof(char));
+ int x = 0;
+ for (int i = 0; i < 200; i++)
+ {
+ //copy over non space and non termination values from call string
+ if (i != 0 && state->call_string[1][i] != ' ' && state->call_string[1][i] != 0)
+ cs[x++] = state->call_string[1][i];
+ }
+ cs[x] = 0; //terminate
+
+ //open wav file here if not already opened
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR == NULL)
+ {
+ sprintf (opts->wav_out_fileR, "%s/%s %s CC %d - %s - TG %d - RD %d", opts->wav_out_dir, datestr, timestr, state->dmr_color_code, cs, target, source);
+ openWavOutFileR (opts, state);
+ }
+ }
+ }
}
diff --git a/src/dsd_file.c b/src/dsd_file.c
index 6f4ecaa..f4b201d 100644
--- a/src/dsd_file.c
+++ b/src/dsd_file.c
@@ -555,6 +555,16 @@ void closeWavOutFile (dsd_opts * opts, dsd_state * state)
UNUSED(state);
sf_close(opts->wav_out_f);
+ char newfilename[1032];
+ int result; UNUSED(result);
+ sprintf (newfilename, "%s.wav", opts->wav_out_file);
+ result = rename (opts->wav_out_file, newfilename);
+ // fprintf (stderr, "\n Close WAV FileF %s; Result: %d \n", newfilename, result);
+
+ //copy back to wav_out_file string for ncurses display
+ memcpy (opts->wav_out_file, newfilename, 1023);
+ opts->wav_out_file[1023] = 0;
+
}
void closeWavOutFileL (dsd_opts * opts, dsd_state * state)
@@ -562,6 +572,16 @@ void closeWavOutFileL (dsd_opts * opts, dsd_state * state)
UNUSED(state);
sf_close(opts->wav_out_f);
+ char newfilename[1032];
+ int result; UNUSED(result);
+ sprintf (newfilename, "%s.wav", opts->wav_out_file);
+ result = rename (opts->wav_out_file, newfilename);
+ // fprintf (stderr, "\n Close WAV FileL %s; Result: %d \n", newfilename, result);
+
+ //copy back to wav_out_file string for ncurses display
+ memcpy (opts->wav_out_file, newfilename, 1023);
+ opts->wav_out_file[1023] = 0;
+
}
void closeWavOutFileR (dsd_opts * opts, dsd_state * state)
@@ -569,6 +589,15 @@ void closeWavOutFileR (dsd_opts * opts, dsd_state * state)
UNUSED(state);
sf_close(opts->wav_out_fR);
+ char newfilename[1032];
+ int result; UNUSED(result);
+ sprintf (newfilename, "%s.wav", opts->wav_out_fileR);
+ result = rename (opts->wav_out_fileR, newfilename);
+ // fprintf (stderr, "\n Close WAV FileR %s; Result: %d \n", newfilename, result);
+
+ //copy back to wav_out_file string for ncurses display
+ memcpy (opts->wav_out_fileR, newfilename, 1023);
+ opts->wav_out_fileR[1023] = 0;
}
void closeWavOutFileRaw (dsd_opts * opts, dsd_state * state)
diff --git a/src/dsd_main.c b/src/dsd_main.c
index 3fb0893..a6d45e5 100644
--- a/src/dsd_main.c
+++ b/src/dsd_main.c
@@ -107,6 +107,32 @@ void
noCarrier (dsd_opts * opts, dsd_state * state)
{
+ //close any perCall wav files that are open
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
+ {
+ closeWavOutFileL (opts, state);
+ opts->wav_out_f = NULL;
+ }
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
+ {
+ closeWavOutFileR (opts, state);
+ opts->wav_out_fR = NULL;
+ }
+ //end perCall wav file close
+
+ //close any perCall wav files that are open
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
+ {
+ closeWavOutFileL (opts, state);
+ opts->wav_out_f = NULL;
+ }
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
+ {
+ closeWavOutFileR (opts, state);
+ opts->wav_out_fR = NULL;
+ }
+ //end perCall wav file close
+
// #ifdef AERO_BUILD //NOTE: Blame seems to be synctest8 not being initialized (will continue to test)
// //TODO: Investigate why getSymbol needs to be run first in this context...truly confused here
// if(opts->frame_m17 == 1) //&& opts->audio_in_type == 5
@@ -1692,9 +1718,9 @@ main (int argc, char **argv)
}
#ifdef AERO_BUILD
- fprintf (stderr, "Build Version: AW (20231015) \n");
+ fprintf (stderr, "Build Version: AW (20240601) G Build\n");
#else
- fprintf (stderr, "Build Version: AW %s \n", GIT_TAG);
+ fprintf (stderr, "Build Version: AW %s G Build\n", GIT_TAG);
#endif
fprintf (stderr,"MBElib Version: %s\n", versionstr);
diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c
index fe71539..2702b5d 100644
--- a/src/dsd_mbe.c
+++ b/src/dsd_mbe.c
@@ -1197,7 +1197,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
{
//write wav to per call on left channel Slot 1
- writeSynthesizedVoice (opts, state);
+ if (opts->wav_out_f != NULL)
+ writeSynthesizedVoice (opts, state);
}
}
@@ -1207,7 +1208,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
{
//write wav to per call on right channel Slot 2
- writeSynthesizedVoiceR (opts, state);
+ if (opts->wav_out_fR != NULL)
+ writeSynthesizedVoiceR (opts, state);
}
}
diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c
index a5442af..0cd538d 100644
--- a/src/dsd_ncurses.c
+++ b/src/dsd_ncurses.c
@@ -1071,7 +1071,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
if (choicec == 12)
{
//flesh out all closewavs and sprint "" wav filenames
- closeWavOutFile (opts, state);
+ // closeWavOutFile (opts, state);
closeWavOutFileL (opts, state);
closeWavOutFileR (opts, state);
//closeWavOutFileRaw (opts, state);
@@ -2153,7 +2153,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
char cs[200]; memcpy (cs, state->call_string[0], 200*sizeof(char));
//close old first, assign name based on time and radio, open wav file
- closeWavOutFileL (opts, state);
+ if (opts->wav_out_f != NULL)
+ closeWavOutFileL (opts, state);
// sprintf (opts->wav_out_file, "%s/%s %s NXDN - RAN %d - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, rn, tgn, src); //original
sprintf (opts->wav_out_file, "%s/%s %s NXDN - RAN %d - %s - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, rn, cs, tgn, src); //with call string
@@ -2198,27 +2199,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
call_matrix[9][5] = time(NULL);
//open wav file if enabled and both rd and tg are not 0
- if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
- {
- //setup a call string for the per call (group/private and/or emergency)
- char cs[200]; memset (cs, 0, 200*sizeof(char));
- int x = 0;
- for (int i = 0; i < 200; i++)
- {
- //copy over non space and non termination values from call string
- if (i != 0 && state->call_string[0][i] != ' ' && state->call_string[0][i] != 0)
- cs[x++] = state->call_string[0][i];
- }
- cs[x] = 0; //terminate
-
- //close old first, assign name based on time and radio, open wav file
- closeWavOutFileL (opts, state);
-
- // sprintf (opts->wav_out_file, "%s/%s %s MS - CC %d - TG %d - RD %d.wav", opts->wav_out_dir, datestr, timestr, dcc, tg, rd); //original
- sprintf (opts->wav_out_file, "%s/%s %s MS - CC %d - %s - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, dcc, cs, tg, rd); //with cs call string
-
- openWavOutFileL (opts, state); //testing for now, will want to move to per call later
- }
+ // if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
+ // {
+ // //close old first, assign name based on time and radio, open wav file
+ // if (opts->wav_out_f != NULL)
+ // closeWavOutFileL (opts, state);
+ // sprintf (opts->wav_out_file, "./%s/%s %s MS - CC %d - TG %d - RD %d.wav", opts->wav_out_dir, getDateN(), timestr, dcc, tg, rd);
+ // openWavOutFileL (opts, state); //testing for now, will want to move to per call later
+ // }
if (opts->call_alert == 1 && rd != 0 && tg != 0)
{
@@ -2233,7 +2221,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
//DMR BS Slot 1 - matrix 0-4
- if ( call_matrix[4][2] != rd && (lls == 11 || lls == 12 || lls == 10 || lls == 13 || lls == 35 || lls == 36) )
+ if ( call_matrix[4][2] != rd && (lls == 11 || lls == 12 || lls == 10 || lls == 13 ) ) //|| lls == 35 || lls == 36
{
for (short int k = 0; k < 4; k++)
@@ -2254,27 +2242,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
call_matrix[4][5] = time(NULL);
//open wav file if enabled and both rd and tg are not 0
- if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
- {
- //setup a call string for the per call (group/private and/or emergency)
- char cs[200]; memset (cs, 0, 200*sizeof(char));
- int x = 0;
- for (int i = 0; i < 200; i++)
- {
- //copy over non space and non termination values from call string
- if (i != 0 && state->call_string[0][i] != ' ' && state->call_string[0][i] != 0)
- cs[x++] = state->call_string[0][i];
- }
- cs[x] = 0; //terminate
-
- //close old first, assign name based on time and radio, open wav file
- closeWavOutFileL (opts, state);
-
- // sprintf (opts->wav_out_file, "%s/%s %s CC %d - TG %d - RD %d.wav", opts->wav_out_dir, datestr, timestr, dcc, tg, rd); //original
- sprintf (opts->wav_out_file, "%s/%s %s CC %d - %s - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, dcc, cs, tg, rd); //with call string
-
- openWavOutFileL (opts, state); //testing for now, will want to move to per call later
- }
+ // if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
+ // {
+ // //close old first, assign name based on time and radio, open wav file
+ // if (opts->wav_out_f != NULL)
+ // closeWavOutFileL (opts, state);
+ // sprintf (opts->wav_out_file, "./%s/%s %s CC %d - TG %d - RD %d", opts->wav_out_dir, getDateN(), timestr, dcc, tg, rd);
+ // openWavOutFileL (opts, state); //testing for now, will want to move to per call later
+ // }
if (opts->call_alert == 1 && rd != 0 && tg != 0)
{
@@ -2310,34 +2285,21 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
call_matrix[9][5] = time(NULL);
//open wav file if enabled and both rdR and tgR are not 0
- if (opts->dmr_stereo_wav == 1 && rdR != 0 && tgR != 0)
- {
- //setup a call string for the per call (group/private and/or emergency)
- char cs[200]; memset (cs, 0, 200*sizeof(char));
- int x = 0;
- for (int i = 0; i < 200; i++)
- {
- //copy over non space and non termination values from call string
- if (i != 0 && state->call_string[1][i] != ' ' && state->call_string[1][i] != 0)
- cs[x++] = state->call_string[1][i];
- }
- cs[x] = 0; //terminate
-
- //close old first, assign name based on time and radio, open wav file
- closeWavOutFileR (opts, state);
-
- // sprintf (opts->wav_out_fileR, "%s/%s %s CC %d - TG %d - RD %d.wav", opts->wav_out_dir, datestr, timestr, dcc, tgR, rdR); //original
- sprintf (opts->wav_out_fileR, "%s/%s %s CC %d - %s - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, dcc, cs, tgR, rdR); //with call string
-
- openWavOutFileR (opts, state); //testing for now, will want to move to per call later
- }
+ // if (opts->dmr_stereo_wav == 1 && rdR != 0 && tgR != 0)
+ // {
+ // //close old first, assign name based on time and radio, open wav file
+ // if (opts->wav_out_fR != NULL)
+ // closeWavOutFileR (opts, state);
+ // sprintf (opts->wav_out_fileR, "./%s/%s %s CC %d - TG %d - RD %d", opts->wav_out_dir, getDateN(), timestr, dcc, tgR, rdR);
+ // openWavOutFileR (opts, state); //testing for now, will want to move to per call later
+ // }
- if (opts->call_alert == 1 && rdR != 0 && tgR != 0)
- {
- //fprintf (stderr, "BEEP 1 BS RIGHT\n");
- beeper (opts, state, 1);
- state->dmr_end_alert[1] = 0; //new voice frame, okay to beep at the end of it
- }
+ // if (opts->call_alert == 1 && rdR != 0 && tgR != 0)
+ // {
+ // //fprintf (stderr, "BEEP 1 BS RIGHT\n");
+ // beeper (opts, state, 1);
+ // state->dmr_end_alert[1] = 0; //new voice frame, okay to beep at the end of it
+ // }
memset(state->dmr_alias_block_segment[1], 0, sizeof(state->dmr_alias_block_segment[1]));
sprintf (state->dmr_embedded_gps[1], "%s", "");
@@ -2423,8 +2385,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->ncurses_compact == 1)
{
printw ("------------------------------------------------------------------------------\n");
- printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20231015)");
- printw ("------------------------------------------------------------------------------\n");
+ printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20240601)");
+ printw ("------------------------------------------------------------------------------\n");
}
#elif LIMAZULUTWEAKS
if (opts->ncurses_compact == 1)
@@ -2462,13 +2424,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (i == 4 && opts->frame_m17 == 1) printw (" CODEC2");
#endif
#ifdef AERO_BUILD
- if (i == 5) printw (" %s ", "Aero Build");
- if (i == 6) printw (" AW (20231015) \n");
+ if (i == 5) printw (" %s ", "G Build");
+ if (i == 6) printw (" AW (20240601) \n");
#elif ZDEV_BUILD
- if (i == 5) printw (" %s ", "AW ");
+ if (i == 5) printw (" %s ", "AW G Build");
if (i == 6) printw (" %s \n", GIT_TAG);
- #else
- if (i == 5) printw (" %s ", "AW ");
+ #else
+ if (i == 5) printw (" %s ", "AW G Build");
if (i == 6) printw (" %s \n", GIT_TAG);
#endif
else printw ("\n");
@@ -4426,7 +4388,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (c == 112) //'p' key - stop all per call wav files
{
//hope this one doesn't cause random crashing or garbage writing
- closeWavOutFile (opts, state);
+ // closeWavOutFile (opts, state);
closeWavOutFileL (opts, state);
closeWavOutFileR (opts, state);
sprintf (opts->wav_out_file, "%s", "");

View File

@@ -1,7 +0,0 @@
/*-------------------------------------------------------------------------------
* 1.c
* Placeholder File 1 -- This file is for temporary code storage
*
*
*
*-----------------------------------------------------------------------------*/

View File

@@ -1,7 +0,0 @@
/*-------------------------------------------------------------------------------
* 2.c
* Placeholder File 2 -- This file is for temporary code storage
*
*
*
*-----------------------------------------------------------------------------*/

View File

@@ -1,7 +0,0 @@
/*-------------------------------------------------------------------------------
* 4.c
* Placeholder File 4 -- This file is for temporary code storage
*
*
*
*-----------------------------------------------------------------------------*/

View File

@@ -1,7 +0,0 @@
/*-------------------------------------------------------------------------------
* 5.c
* Placeholder File 5 -- This file is for temporary code storage
*
*
*
*-----------------------------------------------------------------------------*/

922
src/crypt-aes.c Normal file
View File

@@ -0,0 +1,922 @@
/*-------------------------------------------------------------------------------
* aes.c Tinier AES
* Easy To Use (and configurable) AES Output and Utility Bit/Byte Functions
*
* Modified Tiny AES code for more variable nk/nr/nb values, all in one file
* https://github.com/kokke/tiny-AES-c
*-----------------------------------------------------------------------------*/
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include "dsd.h"
#define AES_BLOCKLEN 16
unsigned Nb = 4;
unsigned Nk = 8;
unsigned Nr = 14;
struct AES_ctx
{
uint8_t RoundKey[240];
uint8_t Iv[16];
};
//internal function prototypes
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
typedef uint8_t state_t[4][4];
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
#define getSBoxValue(num) (sbox[(num)])
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];
}
if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
if (Nk == 8) //only run if using AES256 (Nk == 8)
{
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
}
j = i * 4; k=(i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
//input bit array, return output as up to a 64-bit value
uint64_t convert_bits_into_output_ta(uint8_t * input, int len)
{
int i;
uint64_t output = 0;
for(i = 0; i < len; i++)
{
output <<= 1;
output |= (uint64_t)(input[i] & 1);
}
return output;
}
//take x amount of bits and pack into len amount of bytes (symmetrical)
void pack_bit_array_into_byte_array_ta (uint8_t * input, uint8_t * output, int len)
{
int i;
for (i = 0; i < len; i++)
output[i] = (uint8_t)convert_bits_into_output_ta(&input[i*8], 8);
}
//take len amount of bytes and unpack back into a bit array
void unpack_byte_array_into_bit_array_ta (uint8_t * input, uint8_t * output, int len)
{
int i = 0, k = 0;
for (i = 0; i < len; i++)
{
output[k++] = (input[i] >> 7) & 1;
output[k++] = (input[i] >> 6) & 1;
output[k++] = (input[i] >> 5) & 1;
output[k++] = (input[i] >> 4) & 1;
output[k++] = (input[i] >> 3) & 1;
output[k++] = (input[i] >> 2) & 1;
output[k++] = (input[i] >> 1) & 1;
output[k++] = (input[i] >> 0) & 1;
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#define getSBoxInvert(num) (rsbox[(num)])
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
// Cipher is the main function that encrypts the PlainText,
// or produces a keystream, depending on application.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without MixColumns()
for (round = 1; ; ++round)
{
SubBytes(state);
ShiftRows(state);
if (round == Nr) {
break;
}
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// Add round key to last round
AddRoundKey(Nr, state, RoundKey);
}
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without InvMixColumn()
for (round = (Nr - 1); ; --round)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
if (round == 0) {
break;
}
InvMixColumns(state);
}
}
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i)
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
uint8_t buffer[AES_BLOCKLEN];
size_t i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer,ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
//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
//input type is the type/key len of AES required (0-128, 1-192, 2-256)
//input nblocks is the number of rounds of 16-byte keystream output blocks requried
//output is a uint8_t bytewise array, each round filled with 16-bytes from aes keystream output
void aes_ofb_keystream_output (uint8_t * iv, uint8_t * key, uint8_t * output, int type, int nblocks)
{
int i;
uint8_t input_register[16]; //OFB Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//load first round of input_register with received IV (OFB First Input Register)
memcpy (input_register, iv, 16*sizeof(uint8_t) );
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//execute the cipher function, and copy ciphered input_register to output for required number of rounds
for (i = 0; i < nblocks; i++)
{
Cipher((state_t*)input_register, ctx.RoundKey); //input_register is returned as output, and is put back in as object feedback
memcpy (output+(i*16), input_register, 16*sizeof(uint8_t) ); //copy ciphered input_register to output
}
}
//byte-wise AES CFB (Cipher Feedback) Payload operating in 128-bit block mode
//input in is a uint8_t bytewise array,is the input to be ciphered (encrypted or decrypted)
//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
//input type is the type/key len of AES required (0-128, 1-192, 2-256)
//input nblocks is the number of rounds of 16-byte payload blocks requried (last block will need padding if not flush)
//output out is a uint8_t bytewise array, each round filled with 16-bytes of cfb ciphered output (encrypted or decrypted)
//de is a bit-flag signalling to run Cipher (encrypt) on 1, or InvCipher (decrypt) on 0
void aes_cfb_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in, uint8_t * out, int type, int nblocks, int de)
{
int i, j;
uint8_t input_register[16]; //Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//load first round of input_register with received IV (CFB First Input Register)
memcpy (input_register, iv, 16*sizeof(uint8_t) );
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//execute the cipher function, and copy ciphered input_register to output for required number of rounds
for (i = 0; i < nblocks; i++)
{
//the cipher is always run in the foward, or encryption mode
Cipher((state_t*)input_register, ctx.RoundKey);
//xor the current input 'in' to the current state of the input_register for cipher feedback
for (j = 0; j < 16; j++)
input_register[j] ^= in[j+(i*16)];
//copy ciphered/xor'd input_register to output 'out'
memcpy (out+(i*16), input_register, 16*sizeof(uint8_t) );
//if running in decryption mode, we feed in the next round of input
if (!de)
memcpy(input_register, in+(i*16), 16*sizeof(uint8_t));
}
}
//byte-wise AES CBC (Cipher Block Chaining) //Yeah, I know this already exists, but wanted a custom convenience wrapper version
//input in is a uint8_t bytewise array,is the input to be ciphered (encrypted or decrypted)
//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
//input type is the type/key len of AES required (0-128, 1-192, 2-256)
//input nblocks is the number of rounds of 16-byte payload blocks requried (last block will need padding if not flush)
//output out is a uint8_t bytewise array, each round filled with 16-bytes of cfb ciphered output (encrypted or decrypted)
//de is a bit-flag signalling to run Cipher (encrypt) on 1, or InvCipher (decrypt) on 0
void aes_cbc_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * in, uint8_t * out, int type, int nblocks, int de)
{
int i, j;
uint8_t input_register[16]; //Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//load first round of input_register accordingly
if (de)
memcpy (input_register, iv, 16*sizeof(uint8_t) );
else memcpy (input_register, in, 16*sizeof(uint8_t) );
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//
for (i = 0; i < nblocks; i++)
{
//run encryption or decryption depending on de value
if (de) //encrypt
{
//xor the current input 'in' pt to the current state of the input_register for cbc feedback
for (j = 0; j < 16; j++)
input_register[j] ^= in[j+(i*16)];
Cipher((state_t*)input_register, ctx.RoundKey);
//copy ciphered input_register to output 'out'
memcpy (out+(i*16), input_register, 16*sizeof(uint8_t) );
}
else //decrypt
{
InvCipher((state_t*)input_register, ctx.RoundKey);
//copy ciphered input_register to output 'out'
memcpy (out+(i*16), input_register, 16*sizeof(uint8_t) );
//xor the current output by IV, or by last received CT
if (i == 0)
{
for (j = 0; j < 16; j++)
out[j] ^= iv[j];
}
else
{
for (j = 0; j < 16; j++)
out[j+(i*16)] ^= in[j+((i-1)*16)];
}
//copy in next segment for input_register (if not last)
if (i < nblocks)
memcpy(input_register, in+((i+1)*16), 16*sizeof(uint8_t) );
}
}
}
//byte-wise AES CBC_MAC (Cipher Block Chaining Message Authentication) //This is slightly different than above, no IV is present,
//but if iv is desireable, it will need to be pre-XOR'd with the first plaintext input block by the calling function
//input in is a uint8_t bytewise array, is the input to be ciphered.
//input key is up to 32-byte uint8_t array of key value
//input type is the type/key len of AES required (0-128, 1-192, 2-256)
//input nblocks is the number of rounds of 16-byte payload blocks requried (last block will need padding if not flush)
//output out is a uint8_t bytewise array, with only the final round output as the MAC octets
//NOTE: When doing a cbc_mac, you should only run it in the forward (encryption) mode to get the mac bytes
void aes_cbc_mac_generator (uint8_t * key, uint8_t * in, uint8_t * out, int type, int nblocks)
{
int i, j;
uint8_t input_register[16]; //Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//
for (i = 0; i < nblocks; i++)
{
//xor the current input 'in' pt to the current state of the input_register for cbc feedback
//if this is the first iteration, this will load the first round plain text instead
for (j = 0; j < 16; j++)
input_register[j] ^= in[j+((i+0)*16)];
Cipher((state_t*)input_register, ctx.RoundKey);
//debug, load out all intermediate output register values
// memcpy (out+(i*16), input_register, 16*sizeof(uint8_t) );
}
//copy final ciphered input_register to output 'out', user will determine how many bytes of output they want for MAC
memcpy (out, input_register, 16*sizeof(uint8_t) );
}
//byte-wise output of AES ECB Ciphering/Deciphering
//input is uint8_t byte-wise (16-bytes) data to be ciphered or deciphered
//input key is up to 32-byte uint8_t array of key value
//input type is the type/len of AES required (0-128, 1-192, 2-256)
//output is a uint8_t bytewise array of ciphered or deciphered input
//de is a bit-flag signalling to run Cipher (encrypt) on 1, or InvCipher (decrypt) on 0
void aes_ecb_bytewise_payload_crypt (uint8_t * input, uint8_t * key, uint8_t * output, int type, int de)
{
uint8_t input_register[16]; //ECB Input Register
memset (input_register, 0, sizeof(input_register));
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//load input_register with received input (ECB Payload)
memcpy (input_register, input, 16*sizeof(uint8_t) );
//initialize the key variable for the Cipher function
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
//run encryption or decryption depending on de value
if (de) //encrypt
Cipher((state_t*)input_register, ctx.RoundKey);
else //decrypt
InvCipher((state_t*)input_register, ctx.RoundKey);
//copy ciphered/deciphered input_register to output
memcpy (output, input_register, 16*sizeof(uint8_t) );
}
//symmetrical ctr mode payload encryption and decryption
void aes_ctr_bitwise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type)
{
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//init and set the iv and key variables
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
memset (ctx.Iv, 0, 16*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
memcpy (ctx.Iv, iv, AES_BLOCKLEN);
//pack input bit-wise payload to byte array
uint8_t payload_bytes[16];
memset (payload_bytes, 0, sizeof(payload_bytes));
pack_bit_array_into_byte_array_ta (payload, payload_bytes, 16);
//pass to internal CTR handler for payload
AES_CTR_xcrypt_buffer(&ctx, payload_bytes, 16);
//unpack output bytes back to bits
unpack_byte_array_into_bit_array_ta(payload_bytes, payload, 16);
}
//symmetrical ctr mode payload encryption and decryption
void aes_ctr_bytewise_payload_crypt (uint8_t * iv, uint8_t * key, uint8_t * payload, int type)
{
//Set values specific to type (128/192/256)
if (type == 0) //128
{
Nb = 4;
Nk = 4;
Nr = 10;
}
else if (type == 1) //192
{
Nb = 4;
Nk = 6;
Nr = 12;
}
else //if (type == 2) //256
{
Nb = 4;
Nk = 8;
Nr = 14;
}
struct AES_ctx ctx;
//init and set the iv and key variables
memset (ctx.RoundKey, 0, 240*sizeof(uint8_t));
memset (ctx.Iv, 0, 16*sizeof(uint8_t));
KeyExpansion(ctx.RoundKey, key);
memcpy (ctx.Iv, iv, AES_BLOCKLEN);
//pass to internal CTR handler for payload
AES_CTR_xcrypt_buffer(&ctx, payload, 16);
}

832
src/crypt-des.c Normal file
View File

@@ -0,0 +1,832 @@
/*-------------------------------------------------------------------------------
* des.c Crypthings
* DES Alg
*-----------------------------------------------------------------------------*/
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "dsd.h"
//NOTE: The SLUT boxes are S boxes with additional calculations so we don't have
//to manually sort out row and column values and can cut down on operations
uint8_t SLUT1[64] = {
0xE0,0x00,0x40,0xF0,0xD0,0x70,0x10,0x40,
0x20,0xE0,0xF0,0x20,0xB0,0xD0,0x80,0x10,
0x30,0xA0,0xA0,0x60,0x60,0xC0,0xC0,0xB0,
0x50,0x90,0x90,0x50,0x00,0x30,0x70,0x80,
0x40,0xF0,0x10,0xC0,0xE0,0x80,0x80,0x20,
0xD0,0x40,0x60,0x90,0x20,0x10,0xB0,0x70,
0xF0,0x50,0xC0,0xB0,0x90,0x30,0x70,0xE0,
0x30,0xA0,0xA0,0x00,0x50,0x60,0x00,0xD0
};
uint8_t SLUT2[64] = {
0x0F,0x03,0x01,0x0D,0x08,0x04,0x0E,0x07,
0x06,0x0F,0x0B,0x02,0x03,0x08,0x04,0x0E,
0x09,0x0C,0x07,0x00,0x02,0x01,0x0D,0x0A,
0x0C,0x06,0x00,0x09,0x05,0x0B,0x0A,0x05,
0x00,0x0D,0x0E,0x08,0x07,0x0A,0x0B,0x01,
0x0A,0x03,0x04,0x0F,0x0D,0x04,0x01,0x02,
0x05,0x0B,0x08,0x06,0x0C,0x07,0x06,0x0C,
0x09,0x00,0x03,0x05,0x02,0x0E,0x0F,0x09
};
uint8_t SLUT3[64] = {
0xA0,0xD0,0x00,0x70,0x90,0x00,0xE0,0x90,
0x60,0x30,0x30,0x40,0xF0,0x60,0x50,0xA0,
0x10,0x20,0xD0,0x80,0xC0,0x50,0x70,0xE0,
0xB0,0xC0,0x40,0xB0,0x20,0xF0,0x80,0x10,
0xD0,0x10,0x60,0xA0,0x40,0xD0,0x90,0x00,
0x80,0x60,0xF0,0x90,0x30,0x80,0x00,0x70,
0xB0,0x40,0x10,0xF0,0x20,0xE0,0xC0,0x30,
0x50,0xB0,0xA0,0x50,0xE0,0x20,0x70,0xC0
};
uint8_t SLUT4[64] = {
0x07,0x0D,0x0D,0x08,0x0E,0x0B,0x03,0x05,
0x00,0x06,0x06,0x0F,0x09,0x00,0x0A,0x03,
0x01,0x04,0x02,0x07,0x08,0x02,0x05,0x0C,
0x0B,0x01,0x0C,0x0A,0x04,0x0E,0x0F,0x09,
0x0A,0x03,0x06,0x0F,0x09,0x00,0x00,0x06,
0x0C,0x0A,0x0B,0x01,0x07,0x0D,0x0D,0x08,
0x0F,0x09,0x01,0x04,0x03,0x05,0x0E,0x0B,
0x05,0x0C,0x02,0x07,0x08,0x02,0x04,0x0E
};
uint8_t SLUT5[64] = {
0x20,0xE0,0xC0,0xB0,0x40,0x20,0x10,0xC0,
0x70,0x40,0xA0,0x70,0xB0,0xD0,0x60,0x10,
0x80,0x50,0x50,0x00,0x30,0xF0,0xF0,0xA0,
0xD0,0x30,0x00,0x90,0xE0,0x80,0x90,0x60,
0x40,0xB0,0x20,0x80,0x10,0xC0,0xB0,0x70,
0xA0,0x10,0xD0,0xE0,0x70,0x20,0x80,0xD0,
0xF0,0x60,0x90,0xF0,0xC0,0x00,0x50,0x90,
0x60,0xA0,0x30,0x40,0x00,0x50,0xE0,0x30
};
uint8_t SLUT6[64] = {
0x0C,0x0A,0x01,0x0F,0x0A,0x04,0x0F,0x02,
0x09,0x07,0x02,0x0C,0x06,0x09,0x08,0x05,
0x00,0x06,0x0D,0x01,0x03,0x0D,0x04,0x0E,
0x0E,0x00,0x07,0x0B,0x05,0x03,0x0B,0x08,
0x09,0x04,0x0E,0x03,0x0F,0x02,0x05,0x0C,
0x02,0x09,0x08,0x05,0x0C,0x0F,0x03,0x0A,
0x07,0x0B,0x00,0x0E,0x04,0x01,0x0A,0x07,
0x01,0x06,0x0D,0x00,0x0B,0x08,0x06,0x0D
};
uint8_t SLUT7[64] = {
0x40,0xD0,0xB0,0x00,0x20,0xB0,0xE0,0x70,
0xF0,0x40,0x00,0x90,0x80,0x10,0xD0,0xA0,
0x30,0xE0,0xC0,0x30,0x90,0x50,0x70,0xC0,
0x50,0x20,0xA0,0xF0,0x60,0x80,0x10,0x60,
0x10,0x60,0x40,0xB0,0xB0,0xD0,0xD0,0x80,
0xC0,0x10,0x30,0x40,0x70,0xA0,0xE0,0x70,
0xA0,0x90,0xF0,0x50,0x60,0x00,0x80,0xF0,
0x00,0xE0,0x50,0x20,0x90,0x30,0x20,0xC0
};
uint8_t SLUT8[64] = {
0x0D,0x01,0x02,0x0F,0x08,0x0D,0x04,0x08,
0x06,0x0A,0x0F,0x03,0x0B,0x07,0x01,0x04,
0x0A,0x0C,0x09,0x05,0x03,0x06,0x0E,0x0B,
0x05,0x00,0x00,0x0E,0x0C,0x09,0x07,0x02,
0x07,0x02,0x0B,0x01,0x04,0x0E,0x01,0x07,
0x09,0x04,0x0C,0x0A,0x0E,0x08,0x02,0x0D,
0x00,0x0F,0x06,0x0C,0x0A,0x09,0x0D,0x00,
0x0F,0x03,0x03,0x05,0x05,0x06,0x08,0x0B
};
//initial permutation IP (on input)
uint8_t initial_register_permutation[64] = {
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7};
//D-Box Expansion (sometimes noted as E BIT-SELECTION TABLE)
uint8_t message_expansion[48] = {
32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1};
// (sometimes noted as simply 'P')
uint8_t right_half_permutation[32] = {
16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25};
//inverse initial PI, a.k.a. final permutation (on output)
uint8_t final_register_permutation[64] = {
40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25};
//initial key permutation
uint8_t pc1_key_permutation[56] = {
57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4};
//sub c and d key permutation
uint8_t pc2_key_permutation[48] = {
//c bits
14,17,11,24,1,5,
3,28,15,6,21,10,
23,19,12,4,26,8,
16,7,27,20,13,2,
//d bits (with -28 offset to simply/only have one permute function)
13,24,3,9,19,27,
2,12,23,17,5,20,
16,21,11,28,6,25,
18,14,22,8,1,4
};
uint8_t key_shift_sizes[17] = {
1,
1, 1, 2, 2,
2, 2, 2, 2,
1, 2, 2, 2,
2, 2, 2, 1};
uint8_t key_shift_bytes[17] = {
0x80,
0x80, 0x80, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0,
0x80, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0x80};
uint8_t key_shift_x[4] = {8,8,8,4};
uint8_t key_shift_y[4] = {1,2,3,0};
//input is byte-wise array, output is byte-wise array
void permute(uint8_t * input, uint8_t * output, uint8_t * table, uint8_t start, uint8_t end)
{
for (uint8_t i = start; i < end; i++)
{
uint8_t tdiv = (table[i] - 1) / 8;
uint8_t tmod = (table[i] - 1) % 8;
uint8_t idiv = i/8;
uint8_t imod = i%8;
//need a way to simplify or get rid of all these shifts
uint8_t bit = (((input[tdiv] & (0x80 >> tmod)) << tmod) >> imod);
output[idiv] |= bit;
}
}
void rotate_sub_keys(uint8_t * Kc, uint8_t * Kd, uint8_t shift_size, uint8_t shift_byte)
{
uint8_t sb1[4]; memset(sb1, 0, sizeof(sb1));
uint8_t sb2[4]; memset(sb2, 0, sizeof(sb2));
for (uint8_t i = 0; i < 4; i++)
{
sb1[i] = shift_byte & Kc[i];
sb2[i] = shift_byte & Kd[i];
}
for (uint8_t i = 0; i < 4; i++)
{
uint8_t x = key_shift_x[i];
uint8_t y = key_shift_y[i];
Kc[i] <<= shift_size;
Kd[i] <<= shift_size;
Kc[i] |= (sb1[y] >> (x - shift_size));
Kd[i] |= (sb2[y] >> (x - shift_size));
}
}
void des_cipher (uint8_t * main_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][7]; memset(Ks, 0, sizeof(Ks));
uint8_t Kc[4]; memset(Kc, 0, sizeof(Kc));
uint8_t Kd[4]; memset(Kd, 0, sizeof(Kd));
//initial Ks permutation and shuffle to Kc and Kd
permute (main_key, Ks[0], pc1_key_permutation, 0, 56);
for (uint8_t i = 0; i < 3; i++)
{
Kc[i] = Ks[0][i];
}
Kc[3] = Ks[0][3] & 0xF0;
for (uint8_t i = 0; i < 3; i++)
{
Kd[i] = (Ks[0][i+3] & 0x0F) << 4;
Kd[i] |= (Ks[0][i+4] & 0xF0) >> 4;
}
Kd[3] = (Ks[0][6] & 0x0F) << 4;
//reset Ks
memset(Ks, 0, sizeof(Ks));
//create the 16 Ks rounds
for (uint8_t i = 0; i < 16; i++)
{
uint8_t shift_size = key_shift_sizes[i+1];
uint8_t shift_byte = key_shift_bytes[i+1];
rotate_sub_keys(Kc, Kd, shift_size, shift_byte);
permute (Kc, Ks[i], pc2_key_permutation, 0, 24);
permute (Kd, Ks[i], pc2_key_permutation, 24, 48);
}
//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 fiestel 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 fiestel 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);
}
void des56_ofb_keystream_output (uint8_t * main_key, uint8_t * iv, uint8_t * ks_bytes, uint8_t de, int16_t nblocks)
{
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, iv, sizeof(input_register));
//execute the des_cipher in output feedback mode
for (int16_t i = 0; i < nblocks; i++)
{
//de should be 1 here for encryption mode
des_cipher(main_key, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input register
memcpy(ks_bytes+(i*8), output_register, sizeof(output_register)); //copy output_register to ks_bytes + iteration offset of times 8
memset(output_register, 0, sizeof(output_register)); //reset output register
}
}
//the des_cipher function is already ECB mode, this convenience wrapper is just available as a reminder to illustrate that point
void des56_ecb_payload_crypt (uint8_t * main_key, uint8_t * input_register, uint8_t * output_register, uint8_t de)
{
//de 1 for encryption mode, 0 for decryption mode
des_cipher(main_key, input_register, output_register, de);
}
//TDEA, or triple data encryption algorithm, or triple DES, in electronic codebook mode
void tdea_ecb_payload_crypt (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * input, uint8_t * output, uint8_t de)
{
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the input to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, input, sizeof(input_register));
//For TDEA, the cipher alternates between encryption and decryption to the payload
//so, for example, K1 is run as de=1, K2 is run as de=0, and K3 is run as de=1,
//K1 and K3 will always use the same mode and K2 will be the opposite
//NOTE: If running ECB mode in decryption, make sure to send the keys in reverse order
//so that its K3, K2, and K1 for decryption, and K1, K2, K3 for encryption
//K1
des_cipher(K1, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
de = (de ^ 1) & 1; //flip the de bit
des_cipher(K2, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
de = (de ^ 1) & 1; //flip the de bit back
des_cipher(K3, input_register, output_register, de);
//copy payload out
memcpy(output, output_register, sizeof(output_register)); //copy output_register to output
}
//TDEA, or triple data encryption algorithm, or triple DES, in cipher block chain mode (64-bit)
//same as usual inputs (byte wise), also, if needing DES56, just feed the same key into K1, K2, and K3
//if running in decryption mode, this will reverse the key bundle order from (K1, K2, K3) to (K3, K2, K1) on this end
void tdea_cbc_payload_crypt (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * iv, uint8_t * in, uint8_t * out, int16_t nblocks, uint8_t de)
{
int16_t i, j;
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//load first round of input_register accordingly
if (de)
memcpy (input_register, iv, sizeof(input_register)); //load the IV as first input_register if encrypting
else memcpy (input_register, in, sizeof(input_register)); //load first cipher text as input_register is decrypting
//run payload for number of payload nblocks required
for (i = 0; i < nblocks; i++)
{
if (de)
{
//xor the current input 'in' pt to the current state of the input_register for cbc feedback
for (j = 0; j < 8; j++)
input_register[j] ^= in[j+(i*8)];
//K1
des_cipher(K1, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
de = (de ^ 1) & 1; //flip the de bit
des_cipher(K2, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
de = (de ^ 1) & 1; //flip the de bit back
des_cipher(K3, input_register, output_register, de);
memcpy (input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
//copy ciphered output_register to output 'out'
memcpy (out+(i*8), output_register, sizeof(output_register));
memset(output_register, 0, sizeof(output_register)); //reset output register
}
else
{
//K3
des_cipher(K3, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
de = (de ^ 1) & 1; //flip the de bit
des_cipher(K2, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K1
de = (de ^ 1) & 1; //flip the de bit back
des_cipher(K1, input_register, output_register, de);
memcpy (input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
//copy ciphered input_register to output 'out'
memcpy (out+(i*8), output_register, sizeof(output_register));
//xor the current output by IV, or by last received CT, depending on round
if (i == 0)
{
for (j = 0; j < 8; j++)
out[j] ^= iv[j];
}
else
{
for (j = 0; j < 8; j++)
out[j+(i*8)] ^= in[j+((i-1)*8)];
}
//copy in next segment for input_register (if not last)
if (i < nblocks)
memcpy(input_register, in+((i+1)*8), sizeof(input_register));
memset(output_register, 0, sizeof(output_register)); //reset output register
}
}
}
//TDEA, or triple data encryption algorithm, or triple DES, in cipher block chain mode (64-bit)
//same as usual inputs (byte wise), also, if needing DES56, just feed the same key into K1, K2, and K3
//only the last output_register is returned for the variable len MAC bytes, always run in encryption mode
//but if iv is desireable, it will need to be pre-XOR'd with the first plaintext input block by the calling function
void tdea_cbc_mac_generator (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * in, uint8_t * out, int16_t nblocks)
{
int16_t i, j;
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//run payload for number of payload nblocks required
for (i = 0; i < nblocks; i++)
{
//the cipher is always run in the foward, or encryption mode (1,0,1)
//xor the current input 'in' pt to the current state of the input_register for cbc feedback
for (j = 0; j < 8; j++)
input_register[j] ^= in[j+(i*8)];
//K1
des_cipher(K1, input_register, output_register, 1);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
des_cipher(K2, input_register, output_register, 0);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
des_cipher(K3, input_register, output_register, 1);
memcpy (input_register, output_register, sizeof(output_register));
//copy ciphered output_register to output 'out'
//Since we only want the last output_register,
//this will overwrite the out until completion
memcpy (out, output_register, sizeof(output_register));
memset(output_register, 0, sizeof(output_register)); //reset output register
}
}
//TDEA, or triple data encryption algorithm, or triple DES, in cihper feedback mode (64-bit)
//same as usual inputs (byte wise), also, if needing DES56, just feed the same key into K1, K2, and K3
void tdea_cfb_payload_crypt (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * iv, uint8_t * in, uint8_t * out, int16_t nblocks, uint8_t de)
{
int16_t i, j;
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, iv, sizeof(input_register));
//execute the des_cipher in output feedback mode 3 times using each key and transferring output to input each time
for (i = 0; i < nblocks; i++)
{
//the cipher is always run in the foward, or encryption mode (1,0,1)
//K1
des_cipher(K1, input_register, output_register, 1);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
des_cipher(K2, input_register, output_register, 0);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
des_cipher(K3, input_register, output_register, 1);
//xor the current input 'in' to the current state of the input_register for cipher feedback
for (j = 0; j < 8; j++)
output_register[j] ^= in[j+(i*8)];
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
//copy keystream out and reset
memcpy(out+(i*8), output_register, sizeof(output_register)); //copy output_register to ks_bytes + iteration offset of times 8
memset(output_register, 0, sizeof(output_register)); //reset output register
//if running in decryption mode, we feed in the next round of input
if (!de)
memcpy(input_register, in+(i*8), sizeof(input_register));
}
}
//TDEA, or triple data encryption algorithm, or triple DES, in IV counter mode (tested, working)
void tdea_ctr_payload_crypt (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * iv, uint8_t * input, uint8_t * output, int16_t nblocks)
{
int16_t i, j;
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (ctr mode will manipulate the IV, since it needs to keep a rolling counter
memcpy(input_register, iv, sizeof(input_register));
//execute the des_cipher in counter mode 3 times using each key and transferring output to input each time
//then the IV is iterated and fed back into the input_register to start the next nblocks loop
for (i = 0; i < nblocks; i++)
{
//CTR mode cipher should always run in the forward (encryption) mode
//K1
des_cipher(K1, input_register, output_register, 1);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
des_cipher(K2, input_register, output_register, 0);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
des_cipher(K3, input_register, output_register, 1);
//set output at current pointer to the xor of input current pointer and the output_register
for (j = 0; j < 8; j++)
output[j+(i*8)] = input[j+(i*8)] ^ output_register[j];
memset(output_register, 0, sizeof(output_register)); //reset output register
//increment the IV, and handle roll over (uint8_t will rollover to 0 after 0xFF)
for (j = 7; j >= 0; j--)
{
iv[j]++;
if (iv[j] == 0)
continue;
else break;
}
//debug IV iteration
// fprintf (stderr, "\n IV: ");
// for (j = 0; j < 8; j++)
// fprintf (stderr, "%02X", iv[j]);
//feed the new IV into the input register
memcpy (input_register, iv, sizeof(input_register));
}
}
//TDEA, or triple data encryption algorithm, or triple DES, in output feedback mode
void tdea_tofb_keystream_output (uint8_t * K1, uint8_t * K2, uint8_t * K3, uint8_t * iv, uint8_t * ks_bytes, uint8_t de, int16_t nblocks)
{
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, iv, sizeof(input_register));
//execute the des_cipher in output feedback mode 3 times using each key and transferring output to input each time
for (int16_t i = 0; i < nblocks; i++)
{
//K1
des_cipher(K1, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K2
de = (de ^ 1) & 1; //flip the de bit
des_cipher(K2, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
memset(output_register, 0, sizeof(output_register)); //reset output register
//K3
de = (de ^ 1) & 1; //flip the de bit back
des_cipher(K3, input_register, output_register, de);
memcpy(input_register, output_register, sizeof(output_register)); //recycle output_register back into input_register
//copy keystream out and reset
memcpy(ks_bytes+(i*8), output_register, sizeof(output_register)); //copy output_register to ks_bytes + iteration offset of times 8
memset(output_register, 0, sizeof(output_register)); //reset output register
}
}
//a linear feedback shift register with maximal taps on 64-bit values that can be run to any specified len,
//its input is a byte array of up to 8 bytes, and its output is same array packed with new LFSR value in it.
uint64_t lfsr_64_to_len_ca(uint8_t * iv, int16_t len)
{
uint64_t lfsr = 0, bit = 0;
lfsr = ((uint64_t)iv[0] << 56ULL) + ((uint64_t)iv[1] << 48ULL) + ((uint64_t)iv[2] << 40ULL) + ((uint64_t)iv[3] << 32ULL) +
((uint64_t)iv[4] << 24ULL) + ((uint64_t)iv[5] << 16ULL) + ((uint64_t)iv[6] << 8ULL) + ((uint64_t)iv[7] << 0ULL);
memset (iv, 0, 8*sizeof(uint8_t));
for(int16_t cnt = 0; cnt < len; cnt++)
{
//63,61,45,37,27,14
// Polynomial is C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
lfsr = (lfsr << 1) | bit;
}
for (int16_t i = 0; i < 8; i++)
iv[i] = (lfsr >> (56-(i*8))) & 0xFF;
// fprintf (stderr, "\n IV(%02d): ", len);
// for (int16_t i = 0; i < 8; i++)
// fprintf (stderr, "%02X", iv[i]);
return bit;
}
void des56_ca_keystream_output (uint8_t * main_key, uint8_t * iv, uint8_t * ks_bytes, uint8_t de, int16_t ff, int16_t nbits)
{
//cipher input and output
uint8_t input_register[8]; memset(input_register, 0, sizeof(input_register));
uint8_t output_register[8]; memset(output_register, 0, sizeof(output_register));
//copy the IV to the input_register (make copy so we don't manipulate the calling functions copy)
memcpy(input_register, iv, sizeof(input_register));
//fast forward the current input_register state
lfsr_64_to_len_ca(input_register, ff);
//execute the des_cipher in (CA) mode with 1-bit output
for (int16_t i = 0; i < nbits; i++)
{
//de should be 1 here for encryption mode
des_cipher(main_key, input_register, output_register, de);
//keystream accumulation, shift current byte and append
//single bit from current output register's most significant bit
ks_bytes[i/8] <<= 1;
ks_bytes[i/8] |= ((output_register[0] >> 7) & 1);
//advance the input_register with the lfsr 1 time
lfsr_64_to_len_ca(input_register, 1);
//reset output register
memset(output_register, 0, sizeof(output_register));
}
// fprintf (stderr, "\n IR: ");
// for (int16_t i = 0; i < 8; i++)
// fprintf (stderr, "%02X", input_register[i]);
}
//transitional function mainly to load key and iv into an array
void des_multi_keystream_output (unsigned long long int mi, unsigned long long int key_ulli, uint8_t * output, int type, int len)
{
int i = 0;
uint8_t iv[8]; memset (iv, 0, sizeof(iv));
uint8_t key[8]; memset(key, 0, sizeof(key));
//convert unsinged long long int values into array values
for (i = 7; i >= 0; i--)
iv[7-i] = (mi >> (8*i)) & 0xFF;
for (i = 7; i >= 0; i--)
key[7-i] = (key_ulli >> (8*i)) & 0xFF;
//debug
// fprintf (stderr, "\n IV: ");
// for (i = 0; i < 8; i++)
// fprintf (stderr, "%02X", iv[i]);
// fprintf (stderr, "\n Key: ");
// for (i = 0; i < 8; i++)
// fprintf (stderr, "%02X", key[i]);
//types: 1 = DES56 OFB; 2 = DES56 CA (XL)
if (type == 2)
{
if (len == 0)
des56_ca_keystream_output(key, iv, output, 1, 110+696, 1704);
else des56_ca_keystream_output(key, iv, output, 1, 110+000, 1704);
}
else des56_ofb_keystream_output(key, iv, output, 1, len);
}
//transitional function mainly to load iv into an array
void tdea_multi_keystream_output (unsigned long long int mi, uint8_t * key, uint8_t * output, int type, int len)
{
int i = 0;
uint8_t iv[8]; memset (iv, 0, sizeof(iv));
//convert unsinged long long int values into array values
for (i = 7; i >= 0; i--)
iv[7-i] = (mi >> (8*i)) & 0xFF;
//debug
// fprintf (stderr, "\n IV: ");
// for (i = 0; i < 8; i++)
// fprintf (stderr, "%02X", iv[i]);
//type 1 = TDEA TOFB //TODO: Add more types like 2DES?
if (type == 1)
tdea_tofb_keystream_output(key, key+8, key+16, iv, output, 1, len);
else tdea_tofb_keystream_output(key, key+8, key+16, iv, output, 1, len);
}

126
src/crypt-etc.c Normal file
View File

@@ -0,0 +1,126 @@
#include "dsd.h"
void ken_dmr_scrambler_keystream_creation(dsd_state * state, char * input)
{
/*
SLOT 1 Protected LC FLCO=0x00 FID=0x20 <--this link appears to indicate scrambler usage from Kenwood on DMR
DMR PDU Payload [80][20][40][00][00][01][00][00][01] SB: 00000000000 - 000;
SLOT 1 TGT=1 SRC=1 FLCO=0x00 FID=0x00 SVC=0x00 Group Call <--different call, no scrambler from same Kenwood Radio
DMR PDU Payload [00][00][00][00][00][01][00][00][01]
For This, we could possible transition this to not be enforced
since we may have a positive indicator in link control,
but needs further samples and validation
*/
int lfsr = 0, bit = 0;
sscanf (input, "%d", &lfsr);
fprintf (stderr,"DMR Kenwood 15-bit Scrambler Key %05d with Forced Application\n", lfsr);
for (int i = 0; i < 882; i++)
{
state->static_ks_bits[0][i] = lfsr & 0x1;
state->static_ks_bits[1][i] = lfsr & 0x1;
bit = ( (lfsr >> 1) ^ (lfsr >> 0) ) & 1;
lfsr = ( (lfsr >> 1 ) | (bit << 14) );
}
state->ken_sc = 1;
}
void anytone_bp_keystream_creation(dsd_state * state, char * input)
{
uint16_t key = 0;
uint16_t kperm = 0;
sscanf (input, "%hX", &key);
key &= 0xFFFF; //truncate to 16-bits
//calculate key permutation using simple operations
uint8_t nib1, nib2, nib3, nib4;
//nib 1 and 3 are simple inversions
nib1 = ~(key >> 12) & 0xF;
nib3 = ~(key >> 4) & 0xF;
//nib 2 and 4 are +8 and mod 16 (& 0xF)
nib2 = (((key >> 8) & 0xF) + 8) % 16;
nib4 = (((key >> 0) & 0xF) + 8) % 16;
//debug
// fprintf (stderr, "{%01X, %01X, %01X, %01X}", nib1, nib2, nib3, nib4);
kperm = nib1;
kperm <<= 4;
kperm |= nib2;
kperm <<= 4;
kperm |= nib3;
kperm <<= 4;
kperm |= nib4;
//load bits into static keystream
for (int i = 0; i < 16; i++)
{
state->static_ks_bits[0][i] = (kperm >> (15-i)) & 1;
state->static_ks_bits[1][i] = (kperm >> (15-i)) & 1;
}
fprintf (stderr,"DMR Anytone Basic 16-bit Key 0x%04X with Forced Application\n", key);
state->any_bp = 1;
}
void straight_mod_xor_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 > 882)
len = 882;
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,"AMBE Straight XOR %d-bit Keystream: ", len);
for (uint16_t i = 0; i < unpack_len; i++)
fprintf (stderr, "%02X", ks_bytes[i]);
fprintf (stderr, " with Forced Application \n");
state->straight_ks = 1;
state->straight_mod = (int)len;
END_KS:
if (curr == NULL)
fprintf (stderr, "Straight KS String Malformed! No KS Created!\n");
}

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

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

392
src/crypt-pc4.c Normal file
View File

@@ -0,0 +1,392 @@
#include "pc4.h"
/* Global PC4 context instance */
PC4Context ctx;
/* ---------------------------------
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(PC4Context *ctx) {
uint64_t z = (ctx->x += 0x9e3779b97f4a7c15ULL);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
return z ^ (z >> 31);
}
/* ARC4 initialization */
static void arc4_init(PC4Context *ctx, unsigned char key[]) {
int tmp;
for (ctx->i_arc4 = 0; ctx->i_arc4 < 256; ctx->i_arc4++)
ctx->array_arc4[ctx->i_arc4] = (unsigned char)ctx->i_arc4;
ctx->j_arc4 = 0;
for (ctx->i_arc4 = 0; ctx->i_arc4 < 256; ctx->i_arc4++) {
ctx->j_arc4 = (ctx->j_arc4 + ctx->array_arc4[ctx->i_arc4] + key[ctx->i_arc4 % 256]) % 256;
tmp = ctx->array_arc4[ctx->i_arc4];
ctx->array_arc4[ctx->i_arc4] = ctx->array_arc4[ctx->j_arc4];
ctx->array_arc4[ctx->j_arc4] = tmp;
}
ctx->i_arc4 = 0;
ctx->j_arc4 = 0;
}
/* ARC4 output combined with SplitMix64 stream */
static unsigned char arc4_output(PC4Context *ctx) {
uint8_t rndbyte, decal;
int tmp, t;
ctx->i_arc4 = (ctx->i_arc4 + 1) % 256;
ctx->j_arc4 = (ctx->j_arc4 + ctx->array_arc4[ctx->i_arc4]) % 256;
tmp = ctx->array_arc4[ctx->i_arc4];
ctx->array_arc4[ctx->i_arc4] = ctx->array_arc4[ctx->j_arc4];
ctx->array_arc4[ctx->j_arc4] = tmp;
t = (ctx->array_arc4[ctx->i_arc4] + ctx->array_arc4[ctx->j_arc4]) % 256;
if (ctx->xyz == 0) ctx->bb = next_rng(ctx);
decal = (uint8_t)(56 - (8 * ctx->xyz));
rndbyte = (uint8_t)((ctx->bb >> decal) & 0xffu);
ctx->xyz++;
if (ctx->xyz == 8) ctx->xyz = 0;
if (ctx->count == 0) {
rndbyte = (uint8_t)(rndbyte ^ ctx->array_arc4[t]);
ctx->count = 1;
} else {
rndbyte = (uint8_t)(rndbyte + ctx->array_arc4[t]);
ctx->count = 0;
}
return rndbyte;
}
/* Initialize MD2-II state */
static void md2_init(PC4Context *ctx) {
ctx->x1 = 0;
ctx->x2 = 0;
for (ctx->i = 0; ctx->i < n1; ctx->i++) ctx->h2[ctx->i] = 0;
for (ctx->i = 0; ctx->i < n1; ctx->i++) ctx->h1[ctx->i] = 0;
}
/* MD2-II hashing */
static void md2_hashing(PC4Context *ctx, 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 && ctx->x2 < n1; b6--, ctx->x2++) {
b5 = t1[b4++];
ctx->h1[ctx->x2 + n1] = (unsigned char)b5;
ctx->h1[ctx->x2 + (n1 * 2)] = (unsigned char)(b5 ^ ctx->h1[ctx->x2]);
ctx->x1 = ctx->h2[ctx->x2] ^= s4[b5 ^ ctx->x1];
}
if (ctx->x2 == n1) {
b2 = 0;
ctx->x2 = 0;
for (b3 = 0; b3 < (n1 + 2); b3++) {
for (b1 = 0; b1 < (n1 * 3); b1++)
b2 = ctx->h1[b1] ^= s4[b2];
b2 = (b2 + b3) % 256;
}
}
}
}
/* Finalize MD2-II */
static void md2_end(PC4Context *ctx, unsigned char h4[n1]) {
unsigned char h3[n1];
int i, n4 = n1 - ctx->x2;
for (i = 0; i < n4; i++) h3[i] = (unsigned char)n4;
md2_hashing(ctx, h3, (size_t)n4);
md2_hashing(ctx, ctx->h2, sizeof(ctx->h2));
for (i = 0; i < n1; i++) h4[i] = ctx->h1[i];
}
/* Generate a random index */
static int mixy(PC4Context *ctx, int nn2) {
return arc4_output(ctx) % nn2;
}
/* Fisher-Yates shuffle */
static void mixer(PC4Context *ctx, uint8_t *mixu, int nn) {
int ii, jj, tmmp;
for (ii = nn - 1; ii > 0; ii--) {
jj = mixy(ctx, ii + 1);
tmmp = mixu[jj];
mixu[jj] = mixu[ii];
mixu[ii] = (uint8_t)tmmp;
}
}
/* Key schedule and S-box generation */
void create_keys(PC4Context *ctx, unsigned char key1[], size_t size1) {
int i, w, k;
unsigned char h4[n1];
md2_init(ctx);
md2_hashing(ctx, key1, size1);
md2_end(ctx, h4);
for (i = 0; i < 16; i++) ctx->keys[i] = h4[i];
arc4_init(ctx, h4);
ctx->x = 0;
for (i = 0; i < 8; i++) ctx->x = (ctx->x << 8) + (uint64_t)(h4[256 + i] & 0xffu);
ctx->xyz = 0;
ctx->count = 0;
for (i = 0; i < 20000; i++) (void)arc4_output(ctx);
uint8_t numbers[256];
for (w = 0; w < 16; w++) {
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 256; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 256);
for (i = 0; i < 256; i++) ctx->perm[w][i] = numbers[i];
}
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 256; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 256);
for (i = 0; i < 256; i++) ctx->new1[i] = numbers[i];
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 49; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 49);
for (i = 0; i < 49; i++) ctx->array[i] = numbers[i];
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < nbround; i++) ctx->decal[i] = (uint8_t)((arc4_output(ctx) % 23) + 1);
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (w = 0; w < 3; w++)
for (i = 0; i < nbround; i++)
ctx->rngxor[i][w] = arc4_output(ctx);
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 49; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 49);
for (i = 0; i < 49; i++) ctx->array2[i] = numbers[i];
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 256; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 256);
for (i = 0; i < 256; i++) {
ctx->tab[i] = numbers[i];
ctx->inv[ctx->tab[i]] = (unsigned char)i;
}
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (w = 0; w < 3; w++) {
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (i = 0; i < 3; i++) numbers[i] = (uint8_t)i;
mixer(ctx, numbers, 3);
for (i = 0; i < 3; i++) ctx->permut[w][i] = numbers[i];
}
k = arc4_output(ctx) + 256;
for (i = 0; i < k; i++) (void)arc4_output(ctx);
for (w = 0; w < 3; w++)
for (i = 0; i < nbround; i++)
ctx->rngxor2[i][w] = arc4_output(ctx);
}
/* Compute round transformation */
static void compute(PC4Context *ctx, uint8_t *tab1, uint8_t round) {
ctx->tot[0] = (uint8_t)((ctx->perm[round][tab1[ctx->permut[0][0]]] +
ctx->perm[round][tab1[ctx->permut[0][1]]]) ^
ctx->perm[round][tab1[ctx->permut[0][2]]]);
ctx->tot[0] = (uint8_t)(ctx->tot[0] + ctx->new1[ctx->tot[0]]);
ctx->tot[1] = (uint8_t)((ctx->perm[round][tab1[ctx->permut[1][0]]] +
ctx->perm[round][tab1[ctx->permut[1][1]]]) ^
ctx->perm[round][tab1[ctx->permut[1][2]]]);
ctx->tot[1] = (uint8_t)(ctx->tot[1] + ctx->new1[ctx->tot[1]]);
ctx->tot[2] = (uint8_t)((ctx->perm[round][tab1[ctx->permut[2][0]]] +
ctx->perm[round][tab1[ctx->permut[2][1]]]) ^
ctx->perm[round][tab1[ctx->permut[2][2]]]);
ctx->tot[2] = (uint8_t)(ctx->tot[2] + ctx->new1[ctx->tot[2]]);
}
/* Convert bits to bytes */
void binhex(PC4Context *ctx, 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));
}
ctx->convert[ctx->ptconvert] = a;
ctx->ptconvert++;
}
}
/* Convert byte to bits */
void hexbin(PC4Context *ctx, short *q, uint8_t w, uint8_t hex) { // warning: unused parameter ‘ctx’ [-Wunused-parameter]
short *bits = (short *)q;
for (uint8_t i = 0; i < 8; ++i) {
bits[(short)(7 + w) - i] = (short)((hex >> i) & 1u);
}
UNUSEDPC4(ctx); //fix above warning
}
/* Encrypt one block */
void pc4encrypt(PC4Context *ctx) {
int i;
ctx->totb = 0;
for (i = 0; i < 3; i++) {
ctx->l[0][i] = ctx->convert[i];
ctx->r[0][i] = ctx->convert[i + 3];
}
for (i = 1; i <= ctx->rounds; i++) {
ctx->totb ^= ctx->r[(i - 1) % 2][0];
ctx->totb ^= ctx->r[(i - 1) % 2][1];
ctx->totb ^= ctx->r[(i - 1) % 2][2];
ctx->r[(i - 1) % 2][0] += (uint8_t)(~ctx->rngxor2[ctx->rounds - i][0]);
ctx->r[(i - 1) % 2][1] ^= (uint8_t)(~ctx->rngxor2[ctx->rounds - i][1]);
ctx->r[(i - 1) % 2][2] += (uint8_t)(~ctx->rngxor2[ctx->rounds - i][2]);
ctx->result = 0;
ctx->result += ((uint32_t)ctx->r[(i - 1) % 2][0] << 16);
ctx->result += ((uint32_t)ctx->r[(i - 1) % 2][1] << 8);
ctx->result += ctx->r[(i - 1) % 2][2];
ctx->result = rol(ctx->result, ctx->decal[i - 1], 24);
ctx->r[(i - 1) % 2][0] = (uint8_t)(ctx->result >> 16);
ctx->r[(i - 1) % 2][1] = (uint8_t)((ctx->result >> 8) & 0xffu);
ctx->r[(i - 1) % 2][2] = (uint8_t)(ctx->result & 0xffu);
ctx->r[(i - 1) % 2][0] = ctx->tab[ctx->r[(i - 1) % 2][0]];
ctx->r[(i - 1) % 2][0] ^= ctx->rngxor[i - 1][0];
ctx->r[(i - 1) % 2][1] = ctx->inv[ctx->r[(i - 1) % 2][1]];
ctx->r[(i - 1) % 2][1] -= ctx->rngxor[i - 1][1];
ctx->r[(i - 1) % 2][2] = ctx->tab[ctx->r[(i - 1) % 2][2]];
ctx->r[(i - 1) % 2][2] ^= ctx->rngxor[i - 1][2];
compute(ctx, ctx->r[(i - 1) % 2], (uint8_t)((i - 1) % 16));
ctx->l[i % 2][0] = ctx->r[(i - 1) % 2][0];
ctx->r[i % 2][0] = ctx->l[(i - 1) % 2][0] - ctx->tot[0];
ctx->l[i % 2][1] = ctx->r[(i - 1) % 2][1];
ctx->r[i % 2][1] = ctx->l[(i - 1) % 2][1] ^ ctx->tot[1];
ctx->l[i % 2][2] = ctx->r[(i - 1) % 2][2];
ctx->r[i % 2][2] = ctx->l[(i - 1) % 2][2] - ctx->tot[2];
}
for (i = 0; i < 3; i++) {
ctx->convert[i + 3] = ctx->l[(ctx->rounds - 1) % 2][i];
ctx->convert[i] = ctx->r[(ctx->rounds - 1) % 2][i];
}
ctx->totb %= 2;
}
/* Decrypt one block */
void pc4decrypt(PC4Context *ctx) {
int i;
ctx->totb = 0;
for (i = 0; i < 3; i++) {
ctx->l[0][i] = ctx->convert[i];
ctx->r[0][i] = ctx->convert[i + 3];
}
ctx->y = (uint8_t)((ctx->rounds - 1) % 16);
if (ctx->y == 0) ctx->y = 16;
for (i = 1; i <= ctx->rounds; i++) {
ctx->y--;
compute(ctx, ctx->r[(i - 1) % 2], ctx->y);
if (ctx->y == 0) ctx->y = 16;
ctx->result = 0;
ctx->l[(i - 1) % 2][0] ^= ctx->rngxor[ctx->rounds - i][0];
ctx->l[(i - 1) % 2][0] = ctx->inv[ctx->l[(i - 1) % 2][0]];
ctx->l[(i - 1) % 2][1] += ctx->rngxor[ctx->rounds - i][1];
ctx->l[(i - 1) % 2][1] = ctx->tab[ctx->l[(i - 1) % 2][1]];
ctx->l[(i - 1) % 2][2] ^= ctx->rngxor[ctx->rounds - i][2];
ctx->l[(i - 1) % 2][2] = ctx->inv[ctx->l[(i - 1) % 2][2]];
ctx->result += ((uint32_t)ctx->l[(i - 1) % 2][0] << 16);
ctx->result += ((uint32_t)ctx->l[(i - 1) % 2][1] << 8);
ctx->result += ctx->l[(i - 1) % 2][2];
ctx->result = ror(ctx->result, ctx->decal[ctx->rounds - i], 24);
ctx->l[(i - 1) % 2][0] = (uint8_t)(ctx->result >> 16);
ctx->l[(i - 1) % 2][1] = (uint8_t)((ctx->result >> 8) & 0xffu);
ctx->l[(i - 1) % 2][2] = (uint8_t)(ctx->result & 0xffu);
ctx->l[(i - 1) % 2][0] -= (uint8_t)(~ctx->rngxor2[i - 1][0]);
ctx->l[(i - 1) % 2][1] ^= (uint8_t)(~ctx->rngxor2[i - 1][1]);
ctx->l[(i - 1) % 2][2] -= (uint8_t)(~ctx->rngxor2[i - 1][2]);
ctx->totb ^= ctx->l[(i - 1) % 2][0];
ctx->totb ^= ctx->l[(i - 1) % 2][1];
ctx->totb ^= ctx->l[(i - 1) % 2][2];
ctx->l[i % 2][0] = ctx->r[(i - 1) % 2][0];
ctx->r[i % 2][0] = ctx->l[(i - 1) % 2][0] + ctx->tot[0];
ctx->l[i % 2][1] = ctx->r[(i - 1) % 2][1];
ctx->r[i % 2][1] = ctx->l[(i - 1) % 2][1] ^ ctx->tot[1];
ctx->l[i % 2][2] = ctx->r[(i - 1) % 2][2];
ctx->r[i % 2][2] = ctx->l[(i - 1) % 2][2] + ctx->tot[2];
}
for (i = 0; i < 3; i++) {
ctx->convert[i + 3] = ctx->l[(ctx->rounds - 1) % 2][i];
ctx->convert[i] = ctx->r[(ctx->rounds - 1) % 2][i];
}
ctx->totb %= 2;
}

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

146
src/crypt-rc4.c Normal file
View File

@@ -0,0 +1,146 @@
/*-------------------------------------------------------------------------------
* rc4.c Crypthings
* RC4 Alg
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//this version is for voice, going to transition to a block output version
void rc4_voice_decrypt(int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[])
{
int i, j, count;
uint8_t t, b;
//init Sbox
uint8_t S[256];
for(int i = 0; i < 256; i++) S[i] = i;
//Key Scheduling
j = 0;
for(i = 0; i < 256; i++)
{
j = (j + S[i] + key[i % keylength]) % 256;
t = S[i];
S[i] = S[j];
S[j] = t;
}
//Drop Bytes and Cipher Byte XOR
i = j = 0;
for(count = 0; count < (messagelength + drop); count++)
{
i = (i + 1) % 256;
j = (j + S[i]) % 256;
t = S[i];
S[i] = S[j];
S[j] = t;
b = S[(S[i] + S[j]) % 256];
//return mbe payload byte here
if (count >= drop)
plain[count - drop] = b^cipher[count - drop];
}
}
//this is for PDU usage
void rc4_block_output (int drop, int keylen, int meslen, uint8_t * key, uint8_t * output_blocks)
{
int i, j, x, count;
unsigned int keylength = (unsigned int)keylen;
unsigned int messagelength = (unsigned int)meslen;
unsigned int S[256];
for(i=0; i<256; i++)
S[i] = i;
j = 0;
for(i = 0; i<256; i++)
{
j = (j + S[i] + key[i % keylength]) % 256;
unsigned int temp = S[i];
S[i] = S[j];
S[j] = temp;
}
//Generate Keystream
i = 0;
j = 0;
x = 0;
unsigned int byte;
// fprintf (stderr, " Keystream Octets = ");
for(count = 0; count < (messagelength + drop); count++)
{
i = (i + 1) % 256;
j = (j + S[i]) % 256;
unsigned int temp = S[i];
S[i] = S[j];
S[j] = temp;
byte = S[(S[i] + S[j]) % 256];
//Collect Output blocks
if (count >= drop)
output_blocks[x++] = byte;
}
}
//This is now verified to work after changing the drop byte value from 256 to 0.
//also, had to change the application to not skip the additional 7 bits like DMRA or P25 does.
void hytera_enhanced_rc4_setup(dsd_opts * opts, dsd_state * state, unsigned long long int key_value, unsigned long long int mi_value)
{
UNUSED(opts);
uint8_t key[5]; memset (key, 0, sizeof(key));
uint8_t kiv[5]; memset (kiv, 0, sizeof(kiv));
uint8_t mi[5]; memset (mi, 0, sizeof(mi));
uint8_t ks[135]; memset (ks, 0, sizeof(ks));
//load key_value into key array
key[0] = ((key_value & 0xFF00000000) >> 32UL);
key[1] = ((key_value & 0xFF000000) >> 24);
key[2] = ((key_value & 0xFF0000) >> 16);
key[3] = ((key_value & 0xFF00) >> 8);
key[4] = ((key_value & 0xFF) >> 0);
//load mi_value into mi array
mi[0] = ((mi_value & 0xFF00000000) >> 32UL);
mi[1] = ((mi_value & 0xFF000000) >> 24);
mi[2] = ((mi_value & 0xFF0000) >> 16);
mi[3] = ((mi_value & 0xFF00) >> 8);
mi[4] = ((mi_value & 0xFF) >> 0);
//pointer to the ks_octet storage
uint8_t * ks_octets;
if (state->currentslot == 0)
ks_octets = state->ks_octetL;
else ks_octets = state->ks_octetR;
//NOTE: Drop Byte value is 0
rc4_block_output(0, 5, 135, key, ks);
for (int i = 0; i < 5; i++)
kiv[i] = key[i] ^ mi[i];
for (int i = 0; i < 135; i++)
ks_octets[i] = kiv[i%5] ^ ks[i];
//debug
// fprintf (stderr, " KS: ");
// for (int i = 0; i < 135; i++)
// {
// if ((i != 0) && ((i%7) == 0))
// fprintf (stderr, " ");
// fprintf (stderr, "%02X", ks[i]); //ks_octets
// }
//NULL pointer to ks_octets
ks_octets = NULL;
//end line break
// fprintf (stderr, "\n");
}

361
src/crypt-tyt.c Normal file
View File

@@ -0,0 +1,361 @@
#include "dsd.h"
#include "dmr_const.h"
#include "pc4.h"
//interleaved code words for AMBE+2 (as it arrives over the air)
void ambe2_codeword_print_i (dsd_opts * opts, char ambe_fr[4][24])
{
uint8_t interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//reinterleave 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] = (uint8_t)ambe_fr[*w][*x];
interleaved[(i*2)+1] = (uint8_t)ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
uint8_t bytes[9]; memset(bytes, 0, sizeof(bytes));
//pack
pack_bit_array_into_byte_array(interleaved, bytes, 9);
if (opts->payload == 1)
{
fprintf (stderr, " AMBE HEX(72) INT: ");
for (int8_t i = 0; i < 9; i++)
fprintf (stderr, "%02X", bytes[i]);
fprintf (stderr, "\n");
}
}
//de-interleaved code words for AMBE+2
void ambe2_codeword_print_b (dsd_opts * opts, char ambe_fr[4][24])
{
uint8_t fr_reverse[4][24]; memset(fr_reverse, 0, sizeof(fr_reverse));
for (int i = 0; i < 24; i++)
fr_reverse[0][i] = ambe_fr[0][23-i];
for (int i = 0; i < 23; i++)
fr_reverse[1][i] = ambe_fr[1][22-i];
for (int i = 0; i < 11; i++)
fr_reverse[2][i] = ambe_fr[2][10-i];
for (int i = 0; i < 14; i++)
fr_reverse[3][i] = ambe_fr[3][13-i];
uint32_t v0 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[0], 24); //24
uint32_t v1 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[1], 23); //23
uint32_t v2 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[2], 11); //11
uint32_t v3 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[3], 14); //14
uint32_t c0 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[0], 12);
uint32_t c1 = (uint32_t)convert_bits_into_output((uint8_t *)fr_reverse[1], 12);
//72 bit version
unsigned long long int hex1 = ((unsigned long long int)v0 << 40ULL) + ((unsigned long long int)v1 << 17ULL) + ((unsigned long long int)v2 << 6ULL) + (v3 >> 8);
unsigned long long int hex2 = v3 & 0xFF;
//49 bit version prior to golay correction and c1 demodulation pN
unsigned long long int hex49 = ((unsigned long long int)c0 << 37ULL) + ((unsigned long long int)c1 << 25ULL) + ((unsigned long long int)v2 << 14ULL) + v3;
UNUSED(opts);
if (opts->payload == 1)
{
fprintf (stderr, " AMBE HEX(72): %016llX%02llX \n", hex1, hex2);
fprintf (stderr, " AMBE HEX(49): %014llX\n", hex49 << 7);
}
}
//de-interleaved code words for AMBE+2
void ambe2_codeword_print_f (dsd_opts * opts, char ambe_fr[4][24])
{
uint32_t v0 = (uint32_t)convert_bits_into_output((uint8_t *)ambe_fr[0], 24); //24
uint32_t v1 = (uint32_t)convert_bits_into_output((uint8_t *)ambe_fr[1], 23); //23
uint32_t v2 = (uint32_t)convert_bits_into_output((uint8_t *)ambe_fr[2], 11); //11
uint32_t v3 = (uint32_t)convert_bits_into_output((uint8_t *)ambe_fr[3], 14); //14
// if (opts->payload == 1)
// fprintf (stderr, " AMBE V0: %06X; V1: %06X; V2: %03X; V3: %04X; \n", v0, v1, v2, v3);
unsigned long long int hex1 = ((unsigned long long int)v0 << 40ULL) + ((unsigned long long int)v1 << 17ULL) + ((unsigned long long int)v2 << 6ULL) + (v3 >> 8);
unsigned long long int hex2 = v3 & 0xFF;
if (opts->payload == 1)
fprintf (stderr, " AMBE HEX(72): %016llX%02llX \n", hex1, hex2);
}
//NOTE: This mode DOES NOT work over a repeater, simplex only
//repeaters may or will attempt to correct the frame errors
void tyt16_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24], int fnum)
{
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[10]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t ks[80]; memset(ks, 0, sizeof(ks));
ks_bytes[0] = (state->H >> 8) & 0xFF;
ks_bytes[1] = (state->H >> 0) & 0xFF;
//copy same bytes into rest of byte array
for (int16_t i = 2; i < 10; i++)
ks_bytes[i] = ks_bytes[i%2];
//convert byte array into a bit array
unpack_byte_array_into_bit_array(ks_bytes, ks, 10);
//set ks idx position (-1)
int idx = 0;
if (fnum == 0)
idx = 79;
else idx = 71;
//apply keystream to interleave
for (int8_t i = 0; i < 72; i++)
interleaved[i] ^= ks[idx--];
//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 tyt_ap_pc4_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");
/* 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;
}
}
void tyt_ep_aes_keystream_creation(dsd_state * state, char * input)
{
char buf[1024];
strncpy(buf, input, 1023);
buf[1023] = '\0';
char *pEnd;
unsigned long long int K1 = strtoull (buf, &pEnd, 16);
unsigned long long int K2 = strtoull (pEnd, &pEnd, 16);
//
uint8_t static_key[32];
memset(static_key, 0, sizeof(static_key));
//static key value
static_key[0]=0x6e; static_key[1]=0x02; static_key[2]=0x8d; static_key[3]=0x8a;
static_key[4]=0xca; static_key[5]=0xeb; static_key[6]=0x9b; static_key[7]=0xbe;
static_key[8]=0x42; static_key[9]=0x72; static_key[10]=0xfb; static_key[11]=0x82;
static_key[12]=0x64; static_key[13]=0x56; static_key[14]=0x31; static_key[15]=0xfa;
//the key value provided by user
uint8_t user_key[16];
memset(user_key, 0, sizeof(user_key));
//Load user key into array to manipulate
for (int i = 0; i < 8; i++)
{
user_key[i+0] = (K1 >> (56-(i*8))) & 0xFF;
user_key[i+8] = (K2 >> (56-(i*8))) & 0xFF;
}
uint8_t input_register[16];
memset(input_register, 0, sizeof(input_register));
//manipulate user provided key by loading bytes in reverse order into the input_register
for (int i = 0; i < 16; i++)
input_register[15-i] = user_key[i];
uint8_t ks_bytes[16];
memset(ks_bytes, 0, sizeof(ks_bytes));
//create keystream
aes_ofb_keystream_output(input_register, static_key, ks_bytes, 0, 1);
uint8_t ks_bits[128];
memset(ks_bits, 0, sizeof(ks_bits));
unpack_byte_array_into_bit_array(ks_bytes, ks_bits, 16);
//load static keystream into ctx.bits since that isn't ever zeroed out
for (int i = 0; i < 49; i++)
ctx.bits[i] = ks_bits[i];
fprintf (stderr,"DMR TYT EP (AES-128) Key %016llX%016llX with Forced Application\n", K1, K2);
state->tyt_ep = 1;
}
//connect systems 72-bit (9-byte) Extended Encryption //TODO: Move this later
void csi72_ambe2_codeword_keystream(dsd_state * state, char ambe_fr[4][24])
{
char interleaved[72];
memset (interleaved, 0, sizeof(interleaved));
//interleave the frame
const int *w, *x, *y, *z;
w = rW; x = rX; y = rY; z = rZ;
for (int8_t i = 0; i < 36; i++)
{
interleaved[(i*2)+0] = ambe_fr[*w][*x];
interleaved[(i*2)+1] = ambe_fr[*y][*z];
w++;
x++;
y++;
z++;
}
uint8_t ks_bytes[9]; memset(ks_bytes, 0, sizeof(ks_bytes));
uint8_t ks[72]; memset(ks, 0, sizeof(ks));
//keys are loaded in reverse byte order, stored in state->static_ks_bits[0]
for (int i = 0; i < 9; i++)
ks_bytes[i] = state->static_ks_bits[0][8-i];
//convert byte array into a bit array
unpack_byte_array_into_bit_array(ks_bytes, ks, 9);
//apply keystream to interleave
for (int8_t i = 0; i < 72; i++)
interleaved[i] ^= ks[71-i];
//deinterleave back into ambe_fr frame
w = rW; x = rX; y = rY; z = rZ;
int k = 0;
for (int8_t i = 0; i < 36; i++)
{
ambe_fr[*w][*x] = interleaved[k++];
ambe_fr[*y][*z] = interleaved[k++];
w++;
x++;
y++;
z++;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,8 @@
#include "dsd.h" #include "dsd.h"
#include "dmr_const.h" #include "dmr_const.h"
// #define PRINT_AMBE72 //enable to view 72-bit AMBE codewords
//A subroutine for processing each TDMA frame individually to allow for //A subroutine for processing each TDMA frame individually to allow for
//processing voice and/or data on both BS slots (channels) simultaneously //processing voice and/or data on both BS slots (channels) simultaneously
void dmrBS (dsd_opts * opts, dsd_state * state) void dmrBS (dsd_opts * opts, dsd_state * state)
@@ -303,30 +305,65 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
//check for a rc burst with bptc or 34 rate data in it (testing only) //check for a rc burst with bptc or 34 rate data in it (testing only)
// #define RC_TESTING //disable if not in use // #define RC_TESTING //disable if not in use
#ifdef RC_TESTING #ifdef RC_TESTING
if ( (strcmp (sync, DMR_BS_DATA_SYNC) != 0) && (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) &&
( (internalslot == 0 && vc1 == 6) || (internalslot == 1 && vc2 == 6) ) ) //skip the vc counter, just look at the QR and P/Pi if not voice or data sync pattern
if ( (strcmp (sync, DMR_BS_DATA_SYNC) != 0) && (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) )
{ {
//if the QR FEC is good, and the P/PI bit is on for RC //Golay_20_8_decode FEC for the burst type (SlotType)
if (QR_16_7_6_decode(emb_pdu) && emb_pdu[4]) unsigned char SlotType[20];
memset (SlotType, 0, sizeof(SlotType));
uint8_t k = 61;
for (uint8_t i = 0; i < 5; i++)
{
SlotType[(i*2)+0] = (state->dmr_stereo_payload[k+0] >> 1) & 1;
SlotType[(i*2)+1] = (state->dmr_stereo_payload[k++] >> 0) & 1;
}
k = 90;
for (uint8_t i = 0; i < 5; i++)
{
SlotType[(i*2)+10] = (state->dmr_stereo_payload[k+0] >> 1) & 1;
SlotType[(i*2)+11] = (state->dmr_stereo_payload[k++] >> 0) & 1;
}
//if the QR FEC is good, tact/cach FEC is good, and slot type burst FEC is good, and the P/PI bit is on for RC
//NOTE: This can still trigger on bad signal when it should be a data sync pattern but signal drops out or
//occassionally on p_clear with trunking tuner logic active and partial stale dibits in the buffer
if (QR_16_7_6_decode(emb_pdu) && emb_pdu[4] && tact_okay == 1) //during voice test
{
if (Golay_20_8_decode(SlotType))
{ {
fprintf (stderr,"%s ", timestr); fprintf (stderr,"%s ", timestr);
if (opts->inverted_dmr == 0) if (opts->inverted_dmr == 0)
fprintf (stderr,"Sync: +RC "); fprintf (stderr,"Sync: +RC ");
else fprintf (stderr,"Sync: -RC "); else fprintf (stderr,"Sync: -RC ");
dmr_data_sync (opts, state); dmr_data_sync (opts, state);
}
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB); for (i = 0; i < 48; i++)
state->dmr_embedded_signalling[internalslot][5][i] = syncdata[i];
dmr_sbrc (opts, state, emb_pdu[4]); dmr_sbrc (opts, state, emb_pdu[4]);
if (internalslot == 0) emb_ok = 1;
vc1 = 7;
else vc2 = 7;
//give an audio cue when this happens (low, high)
beeper (opts, state, internalslot, 40, 86, 3);
beeper (opts, state, internalslot, 80, 86, 3);
//put into Event History
state->event_history_s[0].Event_History_Items[internalslot].color_pair = 4;
watchdog_event_datacall (opts, state, 0, 0, "DMR Reverse Channel P/PI Indicator On (FEC Okay);", internalslot);
push_event_history (&state->event_history_s[internalslot]);
init_event_history (&state->event_history_s[internalslot], 0, 1);
if (Golay_20_8_decode(SlotType))
{
skipcount++;
goto SKIP; goto SKIP;
}
} }
@@ -425,6 +462,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if (internalslot == 0 && vc1 == 6) if (internalslot == 0 && vc1 == 6)
{ {
//this needs to run prior to embedded link control
if (state->payload_algid == 0x02)
hytera_enhanced_alg_refresh(state);
//process embedded link control //process embedded link control
fprintf (stderr, "\n"); fprintf (stderr, "\n");
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB); dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
@@ -435,6 +475,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if (internalslot == 1 && vc2 == 6) if (internalslot == 1 && vc2 == 6)
{ {
//this needs to run prior to embedded link control
if (state->payload_algidR == 0x02)
hytera_enhanced_alg_refresh(state);
//process embedded link control //process embedded link control
fprintf (stderr, "\n"); fprintf (stderr, "\n");
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB); dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
@@ -452,6 +495,26 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
memcpy (m2, ambe_fr2, sizeof(m2)); memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3)); memcpy (m3, ambe_fr3, sizeof(m3));
if (state->tyt_bp == 1)
{
tyt16_ambe2_codeword_keystream(state, ambe_fr, 0);
tyt16_ambe2_codeword_keystream(state, ambe_fr2, 1);
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
ambe2_codeword_print_i(opts, ambe_fr3);
#endif
processMbeFrame (opts, state, NULL, ambe_fr, NULL); processMbeFrame (opts, state, NULL, ambe_fr, NULL);
if(internalslot == 0) if(internalslot == 0)
{ {
@@ -530,6 +593,10 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state); if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state); if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state);
if (internalslot == 0 && vc1 == 6) state->static_ks_counter[0] = 0;
if (internalslot == 1 && vc2 == 6) state->static_ks_counter[1] = 0;
if (opts->dmr_le != 2) //if not Hytera Enhanced
dmr_late_entry_mi_fragment (opts, state, vc%7, m1, m2, m3); dmr_late_entry_mi_fragment (opts, state, vc%7, m1, m2, m3);
//increment the vc counters //increment the vc counters
@@ -583,6 +650,14 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
ncursesPrinter(opts, state); ncursesPrinter(opts, state);
} }
//slot 1
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
//slot 2 for TDMA systems
watchdog_event_history(opts, state, 1);
watchdog_event_current(opts, state, 1);
// //
if (timestr != NULL) if (timestr != NULL)
{ {
@@ -625,11 +700,23 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
state->currentslot = 0; state->currentslot = 0;
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
else if (state->payload_algid == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 0;
dmr_alg_refresh (opts, state);
}
if (state->payload_algidR >= 0x21) if (state->payload_algidR >= 0x21)
{ {
state->currentslot = 1; state->currentslot = 1;
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
else if (state->payload_algidR == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 1;
dmr_alg_refresh (opts, state);
}
//failsafe to reset all data header and blocks when bad tact or emb //failsafe to reset all data header and blocks when bad tact or emb
dmr_reset_blocks (opts, state); dmr_reset_blocks (opts, state);
@@ -643,6 +730,10 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
timestr = NULL; timestr = NULL;
} }
//reset static ks counter
state->static_ks_counter[0] = 0;
state->static_ks_counter[1] = 0;
} }
//Process buffered half frame and 2nd half and then jump to full BS decoding //Process buffered half frame and 2nd half and then jump to full BS decoding
@@ -713,6 +804,9 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
internalslot = state->currentslot = tact_bits[1]; internalslot = state->currentslot = tact_bits[1];
//reset static ks counter
state->static_ks_counter[internalslot] = 0;
//Setup for first AMBE Frame //Setup for first AMBE Frame
//Interleave Schedule //Interleave Schedule
@@ -860,6 +954,26 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
memcpy (m2, ambe_fr2, sizeof(m2)); memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3)); memcpy (m3, ambe_fr3, sizeof(m3));
if (state->tyt_bp == 1)
{
tyt16_ambe2_codeword_keystream(state, ambe_fr, 0);
tyt16_ambe2_codeword_keystream(state, ambe_fr2, 1);
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
ambe2_codeword_print_i(opts, ambe_fr3);
#endif
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
// processMbeFrame (opts, state, NULL, ambe_fr, NULL); // processMbeFrame (opts, state, NULL, ambe_fr, NULL);
// processMbeFrame (opts, state, NULL, ambe_fr2, NULL); // processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
@@ -912,6 +1026,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
} }
//collect the mi fragment //collect the mi fragment
if (opts->dmr_le != 2) //if not Hytera Enhanced
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3); dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
cach_err = dmr_cach (opts, state, cachdata); cach_err = dmr_cach (opts, state, cachdata);
@@ -943,11 +1058,23 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
state->currentslot = 0; state->currentslot = 0;
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
else if (state->payload_algid == 0x02)
{
state->currentslot = 0;
hytera_enhanced_alg_refresh(state);
dmr_alg_refresh (opts, state);
}
if (state->payload_algidR >= 0x21) if (state->payload_algidR >= 0x21)
{ {
state->currentslot = 1; state->currentslot = 1;
dmr_alg_refresh (opts, state); dmr_alg_refresh (opts, state);
} }
else if (state->payload_algid == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 1;
dmr_alg_refresh (opts, state);
}
//failsafe to reset all data header and blocks when bad tact or emb //failsafe to reset all data header and blocks when bad tact or emb
dmr_reset_blocks (opts, state); dmr_reset_blocks (opts, state);

View File

@@ -3,7 +3,7 @@
* DMR Control Signal Data PDU (CSBK, MBC) Handler and Related Functions * DMR Control Signal Data PDU (CSBK, MBC) Handler and Related Functions
* *
* Portions of Connect+ code reworked from Boatbod OP25 * Portions of Connect+ code reworked from Boatbod OP25
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c * Source: https://github.com/boatbod/op25/blob/master/op25/gr-op25_repeater/lib/dmr_slot.cc
* *
* Portions of Capacity+ code reworked from Eric Cottrell * Portions of Capacity+ code reworked from Eric Cottrell
* Source: https://github.com/LinuxSheeple-E/dsd/blob/Feature/DMRECC/dmr_csbk.c * Source: https://github.com/LinuxSheeple-E/dsd/blob/Feature/DMRECC/dmr_csbk.c
@@ -430,6 +430,10 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
if (ccfreq != 0) state->p25_cc_freq = ccfreq; if (ccfreq != 0) state->p25_cc_freq = ccfreq;
} }
//when on a CC, rotate the symbol out file every hour, if enabled
if (opts->p25_is_tuned == 0) //if not currently tuned on Tier 3 system
rotate_symbol_out_file(opts, state); //may need a second check to make sure other slot on T3 standard isn't busy as well
} }
//P_CLEAR //P_CLEAR
@@ -502,6 +506,10 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1) if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
{ {
//run a watchdog here so we can update this with the most recent info
watchdog_event_current(opts, state, 0);
watchdog_event_current(opts, state, 1);
//display/le/buzzer bug fix when p_clear activated (unsure why this was disabled) //display/le/buzzer bug fix when p_clear activated (unsure why this was disabled)
//clear only the current slot initially, then clear both if tuning to a different freq //clear only the current slot initially, then clear both if tuning to a different freq
if (state->currentslot == 0 && csbk_fid != 253) //don't reset on Cap+ since we aren't testing based on the current TS if (state->currentslot == 0 && csbk_fid != 253) //don't reset on Cap+ since we aren't testing based on the current TS
@@ -529,11 +537,15 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
//clear both if tuning away to another frequency //clear both if tuning away to another frequency
if (GetCurrentFreq(opts->rigctl_sockfd) != state->p25_cc_freq) if (GetCurrentFreq(opts->rigctl_sockfd) != state->p25_cc_freq)
{ {
state->lastsrc = 0;
state->lasttg = 0;
state->payload_mi = 0; state->payload_mi = 0;
state->payload_algid = 0; state->payload_algid = 0;
state->payload_keyid = 0; state->payload_keyid = 0;
state->dmr_so = 0; state->dmr_so = 0;
state->lastsrcR = 0;
state->lasttgR = 0;
state->payload_miR = 0; state->payload_miR = 0;
state->payload_algidR = 0; state->payload_algidR = 0;
state->payload_keyidR = 0; state->payload_keyidR = 0;
@@ -564,11 +576,15 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
//clear both if tuning away to another frequency //clear both if tuning away to another frequency
if (opts->rtlsdr_center_freq != tempf) if (opts->rtlsdr_center_freq != tempf)
{ {
state->lastsrc = 0;
state->lasttg = 0;
state->payload_mi = 0; state->payload_mi = 0;
state->payload_algid = 0; state->payload_algid = 0;
state->payload_keyid = 0; state->payload_keyid = 0;
state->dmr_so = 0; state->dmr_so = 0;
state->lastsrcR = 0;
state->lasttgR = 0;
state->payload_miR = 0; state->payload_miR = 0;
state->payload_algidR = 0; state->payload_algidR = 0;
state->payload_keyidR = 0; state->payload_keyidR = 0;
@@ -613,7 +629,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
if (p_kind == 0) fprintf (stderr, " Disable Target PTT (DIS_PTT)"); if (p_kind == 0) fprintf (stderr, " Disable Target PTT (DIS_PTT)");
if (p_kind == 1) fprintf (stderr, " Enable Target PTT (EN_PTT)"); if (p_kind == 1) fprintf (stderr, " Enable Target PTT (EN_PTT)");
if (p_kind == 2) fprintf (stderr, " Call (ILLEGALLY_PARKED)"); if (p_kind == 2) fprintf (stderr, " Call Hangtime (ILLEGALLY_PARKED)");
if (p_kind == 3) fprintf (stderr, " Enable Target MS PTT (EN_PTT_ONE_MS)"); if (p_kind == 3) fprintf (stderr, " Enable Target MS PTT (EN_PTT_ONE_MS)");
fprintf (stderr, "\n"); fprintf (stderr, "\n");
@@ -629,11 +645,13 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
{ {
if (state->currentslot == 0) state->dmrburstL = 1; if (state->currentslot == 0) state->dmrburstL = 1;
else state->dmrburstR = 1; else state->dmrburstR = 1;
state->gi[state->currentslot] = 0;
} }
if (!gi && opts->trunk_tune_private_calls == 1) if (!gi && opts->trunk_tune_private_calls == 1)
{ {
if (state->currentslot == 0) state->dmrburstL = 1; if (state->currentslot == 0) state->dmrburstL = 1;
else state->dmrburstR = 1; else state->dmrburstR = 1;
state->gi[state->currentslot] = 1;
} }
} }
@@ -700,8 +718,8 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
uint8_t ann_res = (uint8_t)ConvertBitIntoBytes(&bpbits1[0], 4); uint8_t ann_res = (uint8_t)ConvertBitIntoBytes(&bpbits1[0], 4);
uint8_t cc_ch1 = (uint8_t)ConvertBitIntoBytes(&bpbits1[4], 4); uint8_t cc_ch1 = (uint8_t)ConvertBitIntoBytes(&bpbits1[4], 4);
uint8_t cc_ch2 = (uint8_t)ConvertBitIntoBytes(&bpbits1[8], 4); uint8_t cc_ch2 = (uint8_t)ConvertBitIntoBytes(&bpbits1[8], 4);
uint8_t ch1_flag = bpbits1[13]; uint8_t ch1_flag = bpbits1[12];
uint8_t ch2_flag = bpbits1[14]; uint8_t ch2_flag = bpbits1[13];
uint16_t bcast_ch1 = (uint16_t)ConvertBitIntoBytes(&bpbits2[0], 12); uint16_t bcast_ch1 = (uint16_t)ConvertBitIntoBytes(&bpbits2[0], 12);
uint16_t bcast_ch2 = (uint16_t)ConvertBitIntoBytes(&bpbits2[12], 12); uint16_t bcast_ch2 = (uint16_t)ConvertBitIntoBytes(&bpbits2[12], 12);
@@ -724,8 +742,8 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
uint16_t t_emerg_timer = (uint16_t)ConvertBitIntoBytes(&bpbits1[0], 9); uint16_t t_emerg_timer = (uint16_t)ConvertBitIntoBytes(&bpbits1[0], 9);
uint8_t t_packet_timer = (uint8_t)ConvertBitIntoBytes(&bpbits1[9], 5); uint8_t t_packet_timer = (uint8_t)ConvertBitIntoBytes(&bpbits1[9], 5);
uint16_t t_msms_timer = (uint16_t)ConvertBitIntoBytes(&bpbits1[0], 9); uint16_t t_msms_timer = (uint16_t)ConvertBitIntoBytes(&bpbits2[0], 12);
uint16_t t_msline_timer = (uint16_t)ConvertBitIntoBytes(&bpbits1[0], 9); uint16_t t_msline_timer = (uint16_t)ConvertBitIntoBytes(&bpbits2[12], 12);
//just doing the raw values here, and not the decoded values, see clause A.1, Tables A.2, A.3, A.4, A.5 //just doing the raw values here, and not the decoded values, see clause A.1, Tables A.2, A.3, A.4, A.5
fprintf (stderr, "\n"); fprintf (stderr, "\n");
@@ -910,10 +928,10 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
uint8_t nin = (uint8_t)ConvertBitIntoBytes(&bpbits2[16], 8); //see clause 7.2.41 uint8_t nin = (uint8_t)ConvertBitIntoBytes(&bpbits2[16], 8); //see clause 7.2.41
//the second bit of the CSI, the rest are reserved (wasteful) //the second bit of the CSI, the rest are reserved (wasteful)
uint8_t hibernate_flag = bpbits1[1]; uint8_t hibernate_flag = bpbits2[1];
//the first bit of the nin, the rest are reserved (yet again) //the first bit of the nin, the rest are reserved (yet again)
uint8_t reg_tg_sub = bpbits2[0]; //if the MS has to send TG during Registration Process (Zzzzzz) uint8_t reg_tg_sub = bpbits2[16]; //if the MS has to send TG during Registration Process (Zzzzzz)
fprintf (stderr, "\n"); fprintf (stderr, "\n");
fprintf (stderr, " Hibernate Flag: %d; Reg Flag: %d; RES1: %d; RES2: %X; RES3: %X; BPARMS1: %X", hibernate_flag, reg_tg_sub, bpbits1[0], csi & 0x3F, nin & 0x7F, bparms1); fprintf (stderr, " Hibernate Flag: %d; Reg Flag: %d; RES1: %d; RES2: %X; RES3: %X; BPARMS1: %X", hibernate_flag, reg_tg_sub, bpbits1[0], csi & 0x3F, nin & 0x7F, bparms1);
@@ -975,9 +993,17 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
UNUSED(svc_flag); UNUSED(svc_flag);
UNUSED(als_flag); UNUSED(als_flag);
char ahoy_str[200]; memset(ahoy_str, 0, sizeof(ahoy_str));
sprintf (ahoy_str, "AHOY TGT: %d; SRC: %d; ", ahoy_target, ahoy_source);
if (ahoy_gi == 0) fprintf (stderr, "Private "); if (ahoy_gi == 0) fprintf (stderr, "Private ");
else fprintf (stderr, "Group "); else fprintf (stderr, "Group ");
if (ahoy_gi == 0)
strcat (ahoy_str, "Private; ");
else strcat (ahoy_str, "Group; ");
state->gi[state->currentslot] = ahoy_gi ^ 1;
//need to put SVC OPT decoding in here, maybe just copy and paste from FLC? //need to put SVC OPT decoding in here, maybe just copy and paste from FLC?
fprintf (stderr, "FID: %02X SVC: %02X ", csbk_fid, svc_opt); fprintf (stderr, "FID: %02X SVC: %02X ", csbk_fid, svc_opt);
@@ -997,9 +1023,49 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
fprintf (stderr, "Target: %d; Source: %d; ", ahoy_target, ahoy_source); fprintf (stderr, "Target: %d; Source: %d; ", ahoy_target, ahoy_source);
if (svc_kind == 0 || svc_kind == 1) strcat (ahoy_str, "Voice Call; ");
else if (svc_kind == 2 || svc_kind == 3) strcat (ahoy_str, "Packet Data Call; ");
else if (svc_kind == 4 || svc_kind == 5) strcat (ahoy_str, "UDT Short Data Call; ");
else if (svc_kind == 6) strcat (ahoy_str, "UDT Short Data Polling Service; ");
else if (svc_kind == 7) strcat (ahoy_str, "Status Transport Service; ");
else if (svc_kind == 8) strcat (ahoy_str, "Call Diversion Service; ");
else if (svc_kind == 9) strcat (ahoy_str, "Call Answer Service; ");
else if (svc_kind == 10) strcat (ahoy_str, "Full Duplex Voice Call; ");
else if (svc_kind == 11) strcat (ahoy_str, "Full Duplex Packet Data Call; ");
else if (svc_kind == 12) strcat (ahoy_str, "Reserved; ");
else if (svc_kind == 13) strcat (ahoy_str, "Supplimentary Service (Stun/Revive/Kill/Auth); ");
else if (svc_kind == 14) strcat (ahoy_str, "Registration/Authentication; ");
else if (svc_kind == 15) strcat (ahoy_str, "Cancel Call Service; ");
//check the source and/or target for special gateway identifiers //check the source and/or target for special gateway identifiers
dmr_gateway_identifier (ahoy_source, ahoy_target); dmr_gateway_identifier (ahoy_source, ahoy_target);
//log ahoy as a data call event //re-enable this if you want, but it can clog up the event history
// if (state->currentslot == 0)
// {
// state->lastsrc = ahoy_source;
// state->lasttg = ahoy_target;
// }
// else
// {
// state->lastsrcR = ahoy_source;
// state->lasttgR = ahoy_target;
// }
// watchdog_event_datacall (opts, state, ahoy_source, ahoy_target, ahoy_str, state->currentslot);
// if (state->currentslot == 0)
// {
// state->lastsrc = 0;
// state->lasttg = 0;
// }
// else
// {
// state->lastsrcR = 0;
// state->lasttgR = 0;
// }
// watchdog_event_history(opts, state, 0);
// watchdog_event_current(opts, state, 0);
//end ahoy logging
} }
if (csbk_o == 42) if (csbk_o == 42)
@@ -1517,6 +1583,10 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
//Test allowing a tg hold to pre-empt a call in progress and tune to the hold TG //Test allowing a tg hold to pre-empt a call in progress and tune to the hold TG
if (state->tg_hold != 0) state->last_vc_sync_time = 0; if (state->tg_hold != 0) state->last_vc_sync_time = 0;
//if no activity in this window
if ( (time(NULL) - state->last_vc_sync_time) > 2 ) //may use last_cc_sync_time instead
rotate_symbol_out_file(opts, state);
//TODO: Consider a method to allow moving the frequency to the rest channel //TODO: Consider a method to allow moving the frequency to the rest channel
//when a TG hold is specified but nether slot carries the TG on Hold; //when a TG hold is specified but nether slot carries the TG on Hold;
//CODED: using slow link control for that //CODED: using slow link control for that
@@ -2101,6 +2171,10 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
//Test allowing a tg hold to pre-empt a call in progress and tune to the hold TG //Test allowing a tg hold to pre-empt a call in progress and tune to the hold TG
if (state->tg_hold != 0) state->last_vc_sync_time = 0; if (state->tg_hold != 0) state->last_vc_sync_time = 0;
//if no activity in this window
if ( (time(NULL) - state->last_vc_sync_time) > 2 ) //may use last_cc_sync_time instead
rotate_symbol_out_file(opts, state);
//TODO: Consider a method to allow moving the frequency to the free repeater channel //TODO: Consider a method to allow moving the frequency to the free repeater channel
//when a TG hold is specified but nether slot carries the TG on Hold; //when a TG hold is specified but nether slot carries the TG on Hold;
//CODED: using Free repeater in SLC to change over if required //CODED: using Free repeater in SLC to change over if required
@@ -2371,6 +2445,9 @@ void dmr_decode_syscode(dsd_opts * opts, dsd_state * state, uint8_t * cs_pdu_bit
//raw syscode //raw syscode
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 14); uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 14);
if (type == 0)
state->dmr_t3_syscode = syscode;
uint8_t model = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 2); uint8_t model = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 2);
uint16_t net = 0; uint16_t net = 0;
uint16_t site = 0; uint16_t site = 0;

View File

@@ -11,9 +11,6 @@
//TODO: Test USBD LIP Decoder with Real World Samples (if/when available) //TODO: Test USBD LIP Decoder with Real World Samples (if/when available)
//TODO: Test UDT NMEA and LIP Decoders with Real World Samples (if/when available) //TODO: Test UDT NMEA and LIP Decoders with Real World Samples (if/when available)
//WIP: Move all extra decoders for location, etc, to a new file and add prototypes to dsd.h
//TODO: Test CRC9/CRC32 on Rate 1 Data with Real World Samples (if/when available)
//TODO: Address areas that require reading of ISO7, ISO8, and UTF-16 string formats
#include "dsd.h" #include "dsd.h"
@@ -143,7 +140,9 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks
{ {
is_udt = 1; is_udt = 1;
sprintf(state->fsubtype, " UDTC "); if (state->data_conf_data[slot] == 1)
sprintf(state->fsubtype, " UDTC "); //confirmed data
else sprintf(state->fsubtype, " UDTU "); //unconfirmed data
} }
break; break;
case 0x08: //3/4 Rate Data case 0x08: //3/4 Rate Data
@@ -167,11 +166,11 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
crclen = 9; //confirmed data only crclen = 9; //confirmed data only
crcmask = 0x10F; crcmask = 0x10F;
is_full = 1; is_full = 1;
pdu_len = 25; //196 bits - 24.5 bytes pdu_len = 24; //192 bits 24 bytes + 4 pad bits
sprintf(state->fsubtype, " R_1U "); sprintf(state->fsubtype, " R_1U ");
if (state->data_conf_data[slot] == 1) if (state->data_conf_data[slot] == 1)
{ {
pdu_len = 23; pdu_len = 22; //start at plus two when assembling
pdu_start = 2; //start at plus two when assembling pdu_start = 2; //start at plus two when assembling
sprintf(state->fsubtype, " R_1C "); sprintf(state->fsubtype, " R_1C ");
} }
@@ -504,16 +503,20 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
if (is_full) //Rate 1 Data if (is_full) //Rate 1 Data
{ {
//assembly (w/ confirmed data) on a working Tier III Tait System
CRCExtracted = 0; CRCExtracted = 0;
CRCComputed = 0; CRCComputed = 0;
IrrecoverableErrors = 0; //implicit, since there is no encoding IrrecoverableErrors = 0; //implicit, since there is no encoding
for (i = 0; i < 12; i++) //pack rate 1 data for a total of up to 24 bytes, (22 if Confirmed Data)
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8); //skipping the 4 padding bits located at 96,97,98,99 using the ptr values
//as offsets for confirmed data, len, and continue points for packing
//Skip Padding Bits (96,97,98,99) int bit_ptr = pdu_start * 8;
for (i = 0; i < 12; i++) int byte_ptr = 0;
DMR_PDU[i+12] = (uint8_t)ConvertBitIntoBytes(&info[(i*8)+100], 8); pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12-pdu_start);
bit_ptr = 100; byte_ptr = 12-pdu_start;
pack_bit_array_into_byte_array (info+bit_ptr, DMR_PDU+byte_ptr, 12);
//set CRC to correct on unconfirmed 1 rate data blocks (for reporting due to no CRC available on these) //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; if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
@@ -527,11 +530,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
CRCExtracted = CRCExtracted ^ crcmask; CRCExtracted = CRCExtracted ^ crcmask;
//reorganize the info bit array into confdatabits, just for CRC9 check //reorganize the info bit array into confdatabits, just for CRC9 check
//unconfirmed, haven't seen any samples with rate 1 data int k = 0;
for(i = 0; i < 176; i++) confdatabits[i] = info[i + 16]; for (i = 16; i < 96; i++) confdatabits[k++] = info[i]; //first half
for(i = 0; i < 7; i++) confdatabits[i + 176] = info[i]; for (i = 100; i < 196; i++) confdatabits[k++] = info[i]; //second half
for (i = 0; i < 7; i++) confdatabits[k++] = info[i]; //DBSN
CRCComputed = ComputeCrc9Bit(confdatabits, k);
CRCComputed = ComputeCrc9Bit(confdatabits, 183);
if (CRCExtracted == CRCComputed) if (CRCExtracted == CRCComputed)
{ {
CRCCorrect = 1; CRCCorrect = 1;
@@ -539,6 +543,9 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
} }
else state->data_block_crc_valid[slot][blockcounter] = 0; else state->data_block_crc_valid[slot][blockcounter] = 0;
//debug confirmed data values (working now)
// fprintf (stderr, " K: %d; DSBN: %d; CRC: %03X / %03X;", k, dbsn, CRCComputed, CRCExtracted);
} }
//copy info to dmr_pdu_bits //copy info to dmr_pdu_bits
@@ -551,9 +558,9 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors); if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
//full link control //full link control
if (databurst == 0x01) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 1); //VLC if (databurst == 0x01) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, &IrrecoverableErrors, 1); //VLC
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, &IrrecoverableErrors, 2); //TLC
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 3); //EMB if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, &IrrecoverableErrors, 3); //EMB
//dmr data header and multi block types (header, 1/2, 3/4, 1, UDT) - type 1 //dmr data header and multi block types (header, 1/2, 3/4, 1, UDT) - type 1
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors); if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors);

View File

@@ -12,7 +12,7 @@
#include "dsd.h" #include "dsd.h"
//combined flco handler (vlc, tlc, emb), minus the superfluous structs and strings //combined flco handler (vlc, tlc, emb), minus the superfluous structs and strings
void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors, uint8_t type) void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t * IrrecoverableErrors, uint8_t type)
{ {
UNUSED(CRCCorrect); UNUSED(CRCCorrect);
@@ -56,13 +56,43 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24); //Target or Talk Group target = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24); //Target or Talk Group
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[48], 24); source = (uint32_t)ConvertBitIntoBytes(&lc_bits[48], 24);
//Kenwood w/ Scrambler Application on DMR (disable this if clash with other link control, its obscure)
uint8_t is_kenwood_sc = 0;
if (*IrrecoverableErrors == 0 && CRCCorrect == 1 && pf == 1 && fid == 0x20 && (so & 0x40) == 0x40)
{
pf = 0; //turn off PF flag
fid = 0; //unclear if this signals FID (or is cipher type for scrambler)
//NOTE: bit counter reset is handled after vc6 voice now
is_kenwood_sc = 1;
//if forcing a keystream, flip the encryption bit for this to unmute
if (state->ken_sc == 1)
so ^= 0x40;
}
//Kirisun
if (*IrrecoverableErrors == 0 && CRCCorrect == 1 && fid == 0x0A) //&& (so & 0x40) == 0x40
{
//disable late entry for DMRA, Flag 3 if encrypted, 0 if not
if (so & 0x40)
opts->dmr_le = 3;
else opts->dmr_le = 0;
}
//read ahead a little to get this for the xpt flag //read ahead a little to get this for the xpt flag
if (IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68) if (*IrrecoverableErrors == 0 && flco == 0x09 && fid == 0x68)
{ {
sprintf (state->dmr_branding, "%s", " Hytera"); sprintf (state->dmr_branding, "%s", " Hytera");
sprintf (state->dmr_branding_sub, "XPT "); sprintf (state->dmr_branding_sub, "XPT ");
} }
//bug fix for Hytera Enhanced link control (go by the checksum in the message)
//a good checksum will set IrrecoverableErrors back to zero
if (fid == 0x68 && flco == 0x02 && *IrrecoverableErrors == 0)
*IrrecoverableErrors = 1;
//look at the dmr_branding_sub for the XPT string //look at the dmr_branding_sub for the XPT string
//branding sub is set at CSBK(68-3A and 3B), SLCO 8, and here on 0x09 //branding sub is set at CSBK(68-3A and 3B), SLCO 8, and here on 0x09
if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1; if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1;
@@ -81,7 +111,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
goto END_FLCO; goto END_FLCO;
} }
if (IrrecoverableErrors == 0) if (*IrrecoverableErrors == 0)
{ {
if (slot == 0) state->dmr_flco = flco; if (slot == 0) state->dmr_flco = flco;
@@ -98,14 +128,15 @@ 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) //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)
{ {
dmr_embedded_alias_header(opts, state, lc_bits); is_alias = 1;
dmr_talker_alias_lc_header(opts, state, slot, lc_bits);
} }
//Embedded Talker Alias Header (continuation) and Blocks //Embedded Talker Alias Header (continuation) and Blocks
if ( (fid == 0 || fid == 0x68) && type == 3 && flco > 0x03 && flco < 0x08) if ( (fid == 0 || fid == 0x68) && type == 3 && flco > 0x04 && flco < 0x08)
{ {
is_alias = 1; is_alias = 1;
dmr_embedded_alias_blocks(opts, state, lc_bits); dmr_talker_alias_lc_blocks(opts, state, slot, flco-5, lc_bits);
} }
//Embedded GPS //Embedded GPS
@@ -122,15 +153,19 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
capsite = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 4); //don't believe so capsite = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 4); //don't believe so
restchannel = (int)ConvertBitIntoBytes(&lc_bits[52], 4); // restchannel = (int)ConvertBitIntoBytes(&lc_bits[52], 4); //
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16); source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16);
if (flco == 0x07)
state->gi[slot] = 1;
else state->gi[slot] = 0;
} }
//Unknown CapMax/Moto Things //Unknown CapMax/Moto Things
if (fid == 0x10 && (flco == 0x08 || flco == 0x28)) if (fid == 0x10 && (flco == 0x08 || flco == 0x28 || flco == 0x29))
{ {
//NOTE: fid 0x10 and flco 0x08 (emb) produces a lot of 'zero' bytes //NOTE: fid 0x10 and flco 0x08 (emb) produces a lot of 'zero' bytes
//this has been observed to happen often on CapMax systems, so I believe it could be some CapMax 'thing' //this has been observed to happen often on CapMax systems, so I believe it could be some CapMax 'thing'
//Unknown Link Control - FLCO=0x08 FID=0x10 SVC=0xC1 or FLCO=0x08 FID=0x10 SVC=0xC0 <- probably no SVC bits in the lc //Unknown Link Control - FLCO=0x08 FID=0x10 SVC=0xC1 or FLCO=0x08 FID=0x10 SVC=0xC0 <- probably no SVC bits in the lc
//flco 0x28 has also been observed lately but the tg and src values don't match //flco 0x28 has also been observed lately but the tg and src values don't match
//flco 0x29 just observed, similar pattern to 0x08 listed above
//another flco 0x10 does seem to match, so is probably capmax group call flco //another flco 0x10 does seem to match, so is probably capmax group call flco
if (type == 1) fprintf (stderr, "%s \n", KCYN); if (type == 1) fprintf (stderr, "%s \n", KCYN);
if (type == 2) fprintf (stderr, "%s \n", KCYN); if (type == 2) fprintf (stderr, "%s \n", KCYN);
@@ -151,10 +186,18 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
//reset data header format storage //reset data header format storage
state->data_header_format[slot] = 7; state->data_header_format[slot] = 7;
//reset data header sap storage
state->data_header_sap[slot] = 0;
//flag off data header validity //flag off data header validity
state->data_header_valid[slot] = 0; state->data_header_valid[slot] = 0;
//flag off conf data flag //flag off conf data flag
state->data_conf_data[slot] = 0; state->data_conf_data[slot] = 0;
//reset padding
state->data_block_poc[slot] = 0;
//reset byte counter
state->data_byte_ctr[slot] = 0;
//reset ks start value
state->data_ks_start[slot] = 0;
goto END_FLCO; goto END_FLCO;
} }
@@ -276,8 +319,8 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
} }
//unknown other manufacturer or OTA ENC, etc. //unknown other manufacturer or OTA ENC, etc.
//removed tait from the list, added hytera 0x08 //removed tait from the list, added hytera 0x08, added Kirisun and KW Scrambler LC
if (fid != 0 && fid != 0x68 && fid != 0x10 && fid != 0x08) 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 == 1) fprintf (stderr, "%s \n", KYEL);
if (type == 2) fprintf (stderr, "%s \n", KYEL); if (type == 2) fprintf (stderr, "%s \n", KYEL);
@@ -289,9 +332,75 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
} }
} }
else //Look for Hytera PI LC (this may fail the FEC, but have a secondary checksum value)
{
if (fid == 0x68 && flco == 0x02)
{
uint8_t checksum = 0;
uint8_t alg = (uint8_t)ConvertBitIntoBytes(&lc_bits[0], 8);
uint8_t key = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8);
unsigned long long int mi = (unsigned long long int)ConvertBitIntoBytes(&lc_bits[24], 40);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot %d Alg: %02X; KEY ID: %02X; MI(40): %010llX;", slot+1, alg, key, mi);
fprintf (stderr, " Hytera Enhanced; ");
if (slot == 0 && state->R != 0)
fprintf (stderr, "Key: %010llX; ", state->R);
if (slot == 1 && state->RR != 0)
fprintf (stderr, "Key: %010llX; ", state->RR);
for (int i = 0; i < 8; i++)
{
checksum += (uint8_t)ConvertBitIntoBytes(&lc_bits[i*8], 8);
checksum &= 0xFF;
}
checksum = ~checksum & 0xFF;
checksum++;
//debug
// fprintf (stderr, " CHKSUM: %02X / %02X", checksum, (uint8_t)ConvertBitIntoBytes(&lc_bits[64], 8));
if (checksum == (uint8_t)ConvertBitIntoBytes(&lc_bits[64], 8))
{
if (slot == 0)
{
state->dmr_so |= 0x40; //OR the enc bit onto the SO
state->payload_algid = alg;
state->payload_keyid = key;
state->payload_mi = mi;
}
else
{
state->dmr_soR |= 0x40; //OR the enc bit onto the SO
state->payload_algidR = alg;
state->payload_keyidR = key;
state->payload_miR = mi;
}
//disable late entry for DMRA (hopefully, there aren't any systems running both DMRA and Hytera Enhanced mixed together)
opts->dmr_le = 2;
*IrrecoverableErrors = 0; //only set if checksum passes
}
if (checksum == (uint8_t)ConvertBitIntoBytes(&lc_bits[64], 8))
// fprintf (stderr, " (Checksum Okay);");
{;}
else
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (Checksum Err);");
fprintf (stderr, "\n");
}
fprintf (stderr, "%s ", KNRM);
goto END_FLCO;
}
}
//will want to continue to observe for different flco and fid combinations to find out their meaning //will want to continue to observe for different flco and fid combinations to find out their meaning
if(IrrecoverableErrors == 0 && is_alias == 0 && is_gps == 0) if(*IrrecoverableErrors == 0 && is_alias == 0 && is_gps == 0)
{ {
//set overarching manufacturer in use when non-standard feature id set is up //set overarching manufacturer in use when non-standard feature id set is up
if (fid != 0) state->dmr_mfid = fid; if (fid != 0) state->dmr_mfid = fid;
@@ -329,27 +438,41 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
{ {
state->dmr_fid = 0; state->dmr_fid = 0;
state->dmr_so = 0; state->dmr_so = 0;
// state->lasttg = 0; state->lasttg = 0;
// state->lastsrc = 0; state->lastsrc = 0;
state->payload_algid = 0; state->payload_algid = 0;
state->payload_mi = 0; state->payload_mi = 0;
state->payload_keyid = 0; state->payload_keyid = 0;
//reset gain //reset gain
if (opts->floating_point == 1) if (opts->floating_point == 1)
state->aout_gain = opts->audio_gain; state->aout_gain = opts->audio_gain;
state->dmr_alias_block_len[0] = 0;
state->dmr_alias_char_size[0] = 0;
state->dmr_alias_format[0] = 0;
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]));
} }
if (state->currentslot == 1) if (state->currentslot == 1)
{ {
state->dmr_fidR = 0; state->dmr_fidR = 0;
state->dmr_soR = 0; state->dmr_soR = 0;
// state->lasttgR = 0; state->lasttgR = 0;
// state->lastsrcR = 0; state->lastsrcR = 0;
state->payload_algidR = 0; state->payload_algidR = 0;
state->payload_miR = 0; state->payload_miR = 0;
state->payload_keyidR = 0; state->payload_keyidR = 0;
//reset gain //reset gain
if (opts->floating_point == 1) if (opts->floating_point == 1)
state->aout_gainR = opts->audio_gain; state->aout_gainR = opts->audio_gain;
state->dmr_alias_block_len[1] = 0;
state->dmr_alias_char_size[1] = 0;
state->dmr_alias_format[1] = 0;
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]));
} }
} }
@@ -395,23 +518,27 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
// strcat (state->call_string[slot], " Grp"); // strcat (state->call_string[slot], " Grp");
sprintf (state->call_string[slot], " Group "); sprintf (state->call_string[slot], " Group ");
fprintf (stderr, "Group "); fprintf (stderr, "Group ");
state->gi[slot] = 0;
} }
else else
{ {
// strcat (state->call_string[slot], " Pri"); // strcat (state->call_string[slot], " Pri");
sprintf (state->call_string[slot], " Private "); sprintf (state->call_string[slot], " Private ");
fprintf (stderr, "Private "); fprintf (stderr, "Private ");
state->gi[slot] = 1;
} }
} }
else if (flco == 0x3) //UU_V_Ch_Usr else if (flco == 0x3) //UU_V_Ch_Usr
{ {
sprintf (state->call_string[slot], " Private "); sprintf (state->call_string[slot], " Private ");
fprintf (stderr, "Private "); fprintf (stderr, "Private ");
state->gi[slot] = 1;
} }
else //Grp_V_Ch_Usr -- still valid on hytera VLC else //Grp_V_Ch_Usr -- still valid on hytera VLC
{ {
sprintf (state->call_string[slot], " Group "); sprintf (state->call_string[slot], " Group ");
fprintf (stderr, "Group "); fprintf (stderr, "Group ");
state->gi[slot] = 0;
} }
if(so & 0x80) if(so & 0x80)
@@ -460,6 +587,13 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
state->group_tally++; state->group_tally++;
} }
//run a watchdog here so we can update this with the crypto variables and ENC LO
if (target != 0 && lo == 0)
{
sprintf (state->event_history_s[slot].Event_History_Items[0].internal_str, "Target: %d; has been locked out; Encryption Lock Out Enabled.", target);
watchdog_event_current(opts, state, slot);
}
//Craft a fake CSBK pdu send it to run as a p_clear to return to CC if available //Craft a fake CSBK pdu send it to run as a p_clear to return to CC if available
uint8_t dummy[12]; uint8_t* dbits; memset (dummy, 0, sizeof(dummy)); dummy[0] = 46; dummy[1] = 255; uint8_t dummy[12]; uint8_t* dbits; memset (dummy, 0, sizeof(dummy)); dummy[0] = 46; dummy[1] = 255;
if ( (strcmp(gm, "B") == 0) && (strcmp(gn, "ENC LO") == 0) ) //&& (opts->trunk_tune_data_calls == 0) if ( (strcmp(gm, "B") == 0) && (strcmp(gn, "ENC LO") == 0) ) //&& (opts->trunk_tune_data_calls == 0)
@@ -529,10 +663,22 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
} }
//should rework this back into the upper portion //should rework this back into the upper portion
if (fid == 0x0A) fprintf (stderr, "Kirisun ");
if (fid == 0x68) fprintf (stderr, "Hytera "); if (fid == 0x68) fprintf (stderr, "Hytera ");
if (is_xpt) fprintf (stderr, "XPT "); if (is_xpt) fprintf (stderr, "XPT ");
if (fid == 0x68 && flco == 0x00) fprintf (stderr, "Group "); if (fid == 0x68 && flco == 0x00)
if (fid == 0x68 && flco == 0x03) fprintf (stderr, "Private "); {
fprintf (stderr, "Group ");
state->gi[slot] = 0;
}
if (fid == 0x68 && flco == 0x03)
{
fprintf (stderr, "Private ");
state->gi[slot] = 1;
}
if (is_kenwood_sc)
fprintf(stderr, "Kenwood Scrambler ");
fprintf(stderr, "Call "); fprintf(stderr, "Call ");
@@ -613,6 +759,40 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
} }
if (slot == 0 && state->payload_algid == 0x02 && state->R != 0)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "Key: %010llX ", state->R);
fprintf (stderr, "%s ", KNRM);
}
if (slot == 1 && state->payload_algidR == 0x02 && state->RR != 0)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "Key: %010llX ", state->RR);
fprintf (stderr, "%s ", KNRM);
}
if (slot == 0 && (state->payload_algid == 0x25 || state->payload_algid == 0x24) && state->aes_key_loaded[0] == 1)
{
fprintf (stderr, "\n ");
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "Key: %016llX %016llX ", state->A1[0], state->A2[0]);
if (state->payload_algid == 0x25)
fprintf (stderr, "%016llX %016llX", state->A3[0], state->A4[0]);
fprintf (stderr, "%s ", KNRM);
}
if (slot == 1 && (state->payload_algidR == 0x25 || state->payload_algidR == 0x24) && state->aes_key_loaded[1] == 1)
{
fprintf (stderr, "\n ");
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "Key: %016llX %016llX ", state->A1[1], state->A2[1]);
if (state->payload_algidR == 0x25)
fprintf (stderr, "%016llX %016llX", state->A3[1], state->A4[1]);
fprintf (stderr, "%s ", KNRM);
}
} }
END_FLCO: END_FLCO:
@@ -626,7 +806,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
if(IrrecoverableErrors != 0) if(*IrrecoverableErrors != 0)
{ {
if (type != 3) fprintf (stderr, "\n"); if (type != 3) fprintf (stderr, "\n");
fprintf (stderr, "%s", KRED); fprintf (stderr, "%s", KRED);
@@ -825,7 +1005,7 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
uint8_t model = (uint8_t)ConvertBitIntoBytes(&slco_bits[4], 2); uint8_t model = (uint8_t)ConvertBitIntoBytes(&slco_bits[4], 2);
uint16_t netsite = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 12); uint16_t netsite = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 12);
uint8_t reg = slco_bits[18]; //registration required/not required or normalchanneltype/composite cch uint8_t reg = slco_bits[18]; //registration required/not required or normalchanneltype/composite cch
uint8_t csc = (uint16_t)ConvertBitIntoBytes(&slco_bits[19], 9); //common slot counter, 0-511 uint16_t csc = (uint16_t)ConvertBitIntoBytes(&slco_bits[19], 9); //common slot counter, 0-511
UNUSED(netsite); UNUSED(netsite);
uint16_t net = 0; uint16_t net = 0;
@@ -1005,6 +1185,10 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
if (ccfreq != 0) state->p25_cc_freq = ccfreq; if (ccfreq != 0) state->p25_cc_freq = ccfreq;
} }
//if on Con+ control channel and no activity in this window
if ( (time(NULL) - state->last_vc_sync_time) > 2 ) //may use last_cc_sync_time instead
rotate_symbol_out_file(opts, state);
} }
else if (slco == 0xF) else if (slco == 0xF)
@@ -1151,95 +1335,3 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
//externalize embedded alias to keep the flco function relatively clean
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
{
UNUSED(opts);
uint8_t slot = state->currentslot;
uint8_t format = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 2);
uint8_t len;
//this len seems to pertain to number of blocks? not bit len.
//len = (uint8_t)ConvertBitIntoBytes(&lc_bits[18], 5);
if (format == 0) len = 7;
else if (format == 1 || format == 2) len = 8;
else len = 16;
state->dmr_alias_format[slot] = format;
state->dmr_alias_len[slot] = len;
//fprintf (stderr, "F: %d L: %d - ", format, len);
}
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
{
UNUSED(opts);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Embedded Alias: ");
uint8_t slot = state->currentslot;
uint8_t block = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //FLCO equals block number
uint8_t format = state->dmr_alias_format[slot]; //0=7-bit; 1=ISO8; 2=UTF-8; 3=UTF16BE
uint8_t len = state->dmr_alias_len[slot];
uint8_t start; //starting position depends on context of block and format
UNUSED(format);
//Cap Max Variation
uint8_t fid = (uint8_t)ConvertBitIntoBytes(&lc_bits[8], 8);
if (fid == 0x10) block = block - 0x10; //CapMax adds 0x10 to its FLCO (block num) in its Embedded Aliasing
//there is some issue with the next three lines of code that prevent proper assignments, not sure what.
//if (block > 4) start = 16;
//else if (block == 4 && format > 0) start = 23; //8-bit and 16-bit chars
//else start = 24;
//forcing start to 16 make it work on 8-bit alias, len seems okay when set off of format
start = 16;
len = 8;
// fprintf (stderr, "block: %d start: %d len: %d ", block, start, len);
//all may not be used depending on format, len, start.
uint16_t A0, A1, A2, A3, A4, A5, A6;
//sanity check
if (block > 7) block = 4; //prevent oob array (although we should never get here)
if (len > 6) //if not greater than zero, then the header hasn't arrived yet
{
A0 = 0; A1 = 0; A2 = 0; A3 = 0; A4 = 0; A5 = 0; A6 = 0; //NULL ASCII Characters
A0 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*0], len);
A1 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*1], len);
A2 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*2], len);
A3 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*3], len);
A4 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*4], len);
A5 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*5], len);
A6 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*6], len);
//just going to assign the usual ascii set here to prevent 'naughty' characters, sans diacriticals
if (A0 > 0x19 && A0 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][0], "%c", A0);
if (A1 > 0x19 && A1 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][1], "%c", A1);
if (A2 > 0x19 && A2 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][2], "%c", A2);
if (A3 > 0x19 && A3 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][3], "%c", A3);
if (A4 > 0x19 && A4 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][4], "%c", A4);
if (A5 > 0x19 && A5 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][5], "%c", A5);
if (A6 > 0x19 && A6 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][6], "%c", A6);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 7; j++)
{
fprintf (stderr, "%s", state->dmr_alias_block_segment[slot][i][j]);
}
}
}
else fprintf (stderr, "Missing Header Block Format and Len Data");
fprintf (stderr, "%s", KNRM);
}

View File

@@ -14,23 +14,6 @@ void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc,
uint8_t slot = state->currentslot; 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 //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][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4); state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
@@ -90,7 +73,7 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
} }
} }
int mi_final = 0; unsigned long long int mi_final = 0;
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF; mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
mi_crc_ext = (uint8_t)ConvertBitIntoBytes(&mi_bits[32], 4); mi_crc_ext = (uint8_t)ConvertBitIntoBytes(&mi_bits[32], 4);
@@ -108,42 +91,99 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
if (state->payload_mi != mi_final) if (state->payload_mi != mi_final)
{ {
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X ", state->payload_mi, mi_final); fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08llX : %08llX ", state->payload_mi, mi_final);
if (mi_crc_ok == 1) state->payload_mi = mi_final; if (mi_crc_ok == 1) state->payload_mi = mi_final;
if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)"); if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)");
else fprintf (stderr, "(CRC ERR)"); else fprintf (stderr, "(CRC ERR)");
fprintf (stderr, "\n"); fprintf (stderr, "\n");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
//run afterwards, or le verification won't match up properly
if (state->payload_algid != 0x21) //run expansions afterwards, or le verification won't match up properly
//DES1
if (state->payload_algid == 0x22)
{ {
LFSR64 (state); LFSR64 (state);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//AES-128 or AES-256
if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{
LFSR128d (state);
fprintf (stderr, "\n");
}
} }
if (slot == 1 && state->payload_algidR != 0) if (slot == 1 && state->payload_algidR != 0)
{ {
if (state->payload_miR != mi_final) if (state->payload_miR != mi_final)
{ {
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X ", state->payload_miR, mi_final); fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08llX : %08llX ", state->payload_miR, mi_final);
if (mi_crc_ok == 1) state->payload_miR = mi_final; if (mi_crc_ok == 1) state->payload_miR = mi_final;
if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)"); if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)");
else fprintf (stderr, "(CRC ERR)"); else fprintf (stderr, "(CRC ERR)");
fprintf (stderr, "\n"); fprintf (stderr, "\n");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
//run afterwards, or le verification won't match up properly
if (state->payload_algidR != 0x21) //run expansions afterwards, or le verification won't match up properly
//DES1
if (state->payload_algidR == 0x22)
{ {
LFSR64 (state); LFSR64 (state);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//AES-128 or AES-256
if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{
LFSR128d (state);
fprintf (stderr, "\n");
} }
} }
}
//run LFSR even if golay fails
else if (slot == 0 && state->payload_algid != 0)
{
//DES1
if (state->payload_algid == 0x22)
{
LFSR64 (state);
fprintf (stderr, "\n");
}
//AES-128 or AES-256
if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{
LFSR128d (state);
fprintf (stderr, "\n");
}
}
else if (slot == 1 && state->payload_algidR != 0)
{
//DES1
if (state->payload_algidR == 0x22)
{
LFSR64 (state);
fprintf (stderr, "\n");
}
//AES-128 or AES-256
if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{
LFSR128d (state);
fprintf (stderr, "\n");
}
}
} }
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state) void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
@@ -164,7 +204,37 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
LFSR (state); LFSR (state);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
//LFSR64/128 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
if (state->payload_algid == 0x02)
{
state->DMRvcL = 0;
//LFSR already calculated, just dump it now
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid);
fprintf (stderr, " MI(40): %010llX;", state->payload_mi);
fprintf (stderr, " Hytera Enhanced;");
fprintf (stderr, "%s\n", KNRM);
}
if (state->payload_algid == 0x35 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
state->DMRvcL = 0;
state->payload_mi = kirisun_lfsr(state->payload_mi);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid);
fprintf (stderr, " MI(32): %08llX;", state->payload_mi);
fprintf (stderr, " Kirisun");
if (state->payload_algid == 0x36)
fprintf (stderr, " Advanced;");
else if (state->payload_algid == 0x37)
fprintf (stderr, " Universal;");
else fprintf (stderr, " Encryption;");
fprintf (stderr, "%s\n", KNRM);
}
} }
if (state->currentslot == 1) if (state->currentslot == 1)
@@ -181,7 +251,37 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
LFSR (state); LFSR (state);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
//LFSR64/128 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
if (state->payload_algidR == 0x02)
{
state->DMRvcR = 0;
//LFSR already calculated, just dump it now
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR);
fprintf (stderr, " MI(40): %010llX;", state->payload_miR);
fprintf (stderr, " Hytera Enhanced;");
fprintf (stderr, "%s\n", KNRM);
}
if (state->payload_algidR == 0x35 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
{
state->DMRvcR = 0;
state->payload_miR = kirisun_lfsr(state->payload_miR);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR);
fprintf (stderr, " MI(32): %08llX;", state->payload_miR);
fprintf (stderr, " Kirisun");
if (state->payload_algidR == 0x36)
fprintf (stderr, " Advanced;");
else if (state->payload_algidR == 0x37)
fprintf (stderr, " Universal;");
else fprintf (stderr, " Encryption;");
fprintf (stderr, "%s\n", KNRM);
}
} }
@@ -195,8 +295,8 @@ void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
state->dropR = 256; state->dropR = 256;
state->DMRvcL = 0; state->DMRvcL = 0;
state->DMRvcR = 0; state->DMRvcR = 0;
state->payload_miP = 0; // state->payload_miP = 0; //running these clears out before we can create a new keystream
state->payload_miN = 0; // state->payload_miN = 0; //running these clears out before we can create a new keystream
} }
//handle Single Burst (Voice Burst F) or Reverse Channel Signalling //handle Single Burst (Voice Burst F) or Reverse Channel Signalling
@@ -307,6 +407,41 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
//NOTE: on above, I belive that we need to check by opcode as well, as a CRC3 can have multiple collisions //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?) //so we need to exclude op/alg 0 and 3 from the check (does algID 0x03/0x23 even exist?)
//Kirisun Placeholder
if (opts->dmr_le == 3)
{
if (irr_err != 0)
{
uint32_t sbrcpl = 0;
for(i = 0; i < 32; i++)
{
sbrcpl = sbrcpl << 1;
sbrcpl |= sbrc_interleaved[i] & 1;
}
if (opts->payload == 0) fprintf (stderr, "\n");
fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM);
if (opts->payload == 1) fprintf (stderr, "\n");
}
else if (irr_err == 0 && sbrc_hex != 0) //first batch had sbrc_hex 0x5F1, second batch had 0x5E1 instead, what changed?
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot %d", state->currentslot+1);
fprintf (stderr, " DMR LE SB Kirisun Encryption Identifier;");
fprintf (stderr, "%s ", KNRM);
if (state->currentslot == 0)
{
if (state->payload_algid == 0 && state->dmr_so & 0x40)
state->payload_algid = 0x35; //placeholder value
}
else
{
if (state->payload_algidR == 0 && state->dmr_soR & 0x40)
state->payload_algidR = 0x35; //placeholder value
}
}
}
if (opts->dmr_le == 1) if (opts->dmr_le == 1)
{ {
if (irr_err != 0) if (irr_err != 0)
@@ -344,7 +479,8 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
else if (sbrc_hex == 5) fprintf (stderr, " RC: Cease Transmission Request;"); else if (sbrc_hex == 5) fprintf (stderr, " RC: Cease Transmission Request;");
else fprintf (stderr, " RC: Reserved %02X;", sbrc_hex); else fprintf (stderr, " RC: Reserved %02X;", sbrc_hex);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n"); // if (opts->payload == 1)
fprintf (stderr, "\n");
} }
//if the call is interruptable (TXI) and the crc3 is okay and TXI Opcode //if the call is interruptable (TXI) and the crc3 is okay and TXI Opcode
@@ -382,12 +518,16 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
if (state->dmr_so & 0x40 && key != 0 && alg != 0) if (state->dmr_so & 0x40 && key != 0 && alg != 0)
{ {
//if we aren't forcing a particular alg or privacy key set //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, "\n");
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 1"); fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR LE SB ALG ID: 0x%02X KEY ID: 0x%02X", alg + 0x20, key); 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;");
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n"); if (opts->payload == 1) fprintf (stderr, "\n");
if (state->payload_keyid != key) if (state->payload_keyid != key)
@@ -404,12 +544,12 @@ void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
if (state->dmr_soR & 0x40 && key != 0 && alg != 0) if (state->dmr_soR & 0x40 && key != 0 && alg != 0)
{ {
//if we aren't forcing a particular alg or privacy key set //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, "\n");
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Slot 2"); fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR LE SB ALG ID: 0x%02X KEY ID: 0x%02X", alg + 0x20, key); fprintf (stderr, " DMR LE SB ALG ID: %02X; KEY ID: %02X;", alg + 0x20, key);
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
if (opts->payload == 1) fprintf (stderr, "\n"); if (opts->payload == 1) fprintf (stderr, "\n");
if (state->payload_keyidR != key) if (state->payload_keyidR != key)

View File

@@ -9,6 +9,8 @@
#include "dsd.h" #include "dsd.h"
#include "dmr_const.h" #include "dmr_const.h"
// #define PRINT_AMBE72 //enable to view 72-bit AMBE codewords
//A subroutine for processing MS voice //A subroutine for processing MS voice
void dmrMS (dsd_opts * opts, dsd_state * state) void dmrMS (dsd_opts * opts, dsd_state * state)
{ {
@@ -165,6 +167,16 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
} }
//Kirisun Check VC-F for Enc Identifiers -- has bad fec, unless its loaded differently
//SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun
// if (opts->dmr_le == 3 && vc == 6)
// {
// bool kokay = 0;
// // kokay = Golay_24_12_decode(syncdata);
// unsigned long long int kirisun = (unsigned long long int)ConvertBitIntoBytes(&syncdata[0], 48);
// fprintf (stderr, "Kiri: %012llX; Golay Okay: %d; \n", kirisun, kokay);
// }
for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i]; for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i];
for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40]; for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40];
@@ -244,8 +256,26 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
memcpy (m2, ambe_fr2, sizeof(m2)); memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3)); memcpy (m3, ambe_fr3, sizeof(m3));
if (state->directmode == 0) if (state->tyt_bp == 1)
{ {
tyt16_ambe2_codeword_keystream(state, ambe_fr, 0);
tyt16_ambe2_codeword_keystream(state, ambe_fr2, 1);
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
ambe2_codeword_print_i(opts, ambe_fr3);
#endif
processMbeFrame (opts, state, NULL, ambe_fr, NULL); processMbeFrame (opts, state, NULL, ambe_fr, NULL);
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
@@ -258,22 +288,6 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu)); memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
}
else
{
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
}
//TODO: Consider copying f_l to f_r for left and right channel saturation on MS mode //TODO: Consider copying f_l to f_r for left and right channel saturation on MS mode
if (opts->floating_point == 0) if (opts->floating_point == 0)
@@ -292,6 +306,10 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
if (vc == 6) if (vc == 6)
{ {
//this needs to run prior to embedded link control
if (state->payload_algid == 0x02)
hytera_enhanced_alg_refresh(state);
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB); dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
//check the single burst/reverse channel opportunity //check the single burst/reverse channel opportunity
dmr_sbrc (opts, state, power); dmr_sbrc (opts, state, power);
@@ -301,6 +319,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
} }
//collect the mi fragment //collect the mi fragment
if (opts->dmr_le != 2) //if not Hytera Enhanced
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3); dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
//errors in ms/mono since we skip the other slot //errors in ms/mono since we skip the other slot
@@ -327,6 +346,10 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
ncursesPrinter(opts, state); ncursesPrinter(opts, state);
} }
//slot 1
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
} // end loop } // end loop
END: END:
@@ -355,6 +378,9 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
timestr = NULL; timestr = NULL;
} }
//reset static ks counter
state->static_ks_counter[0] = 0;
} }
//collect buffered 1st half and get 2nd half voice payload and then jump to full MS Voice decoding. //collect buffered 1st half and get 2nd half voice payload and then jump to full MS Voice decoding.
@@ -363,6 +389,9 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
char * timestr = getTimeC(); char * timestr = getTimeC();
//reset static ks counter
state->static_ks_counter[0] = 0;
int i, dibit; int i, dibit;
int *dibit_p; int *dibit_p;
@@ -545,8 +574,26 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
memcpy (m2, ambe_fr2, sizeof(m2)); memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3)); memcpy (m3, ambe_fr3, sizeof(m3));
if (state->directmode == 0) if (state->tyt_bp == 1)
{ {
tyt16_ambe2_codeword_keystream(state, ambe_fr, 0);
tyt16_ambe2_codeword_keystream(state, ambe_fr2, 1);
tyt16_ambe2_codeword_keystream(state, ambe_fr3, 0);
}
if (state->csi_ee == 1)
{
csi72_ambe2_codeword_keystream(state, ambe_fr);
csi72_ambe2_codeword_keystream(state, ambe_fr2);
csi72_ambe2_codeword_keystream(state, ambe_fr3);
}
#ifdef PRINT_AMBE72
ambe2_codeword_print_i(opts, ambe_fr);
ambe2_codeword_print_i(opts, ambe_fr2);
ambe2_codeword_print_i(opts, ambe_fr3);
#endif
processMbeFrame (opts, state, NULL, ambe_fr, NULL); processMbeFrame (opts, state, NULL, ambe_fr, NULL);
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
@@ -559,23 +606,6 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf)); memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l)); memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu)); memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
}
else
{
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
}
//TODO: Consider copying f_l to f_r for left and right channel saturation on MS mode //TODO: Consider copying f_l to f_r for left and right channel saturation on MS mode
if (opts->floating_point == 0) if (opts->floating_point == 0)
@@ -593,6 +623,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
} }
//collect the mi fragment //collect the mi fragment
if (opts->dmr_le != 2) //if not Hytera Enhanced
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3); dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
//errors due to skipping other slot //errors due to skipping other slot

File diff suppressed because it is too large Load Diff

View File

@@ -12,9 +12,10 @@
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors) void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
{ {
UNUSED2(opts, CRCCorrect);
if((IrrecoverableErrors == 0)) uint8_t MFID = PI_BYTE[1];
if(IrrecoverableErrors == 0)
{ {
//update cc amd vc sync time for trunking purposes (particularly Con+) //update cc amd vc sync time for trunking purposes (particularly Con+)
@@ -24,30 +25,178 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->last_cc_sync_time = time(NULL); state->last_cc_sync_time = time(NULL);
} }
if (MFID == 0x0A && CRCCorrect == 1) //Kirisun
{
uint8_t so = PI_BYTE[2]; //in VLC and PI, this byte is 0x40, so thinking this could be SVC_OPT
uint8_t alg = PI_BYTE[0]; //observed 0x36 and 0x37 for Kirisun Advanced and Universal Privacy
// uint8_t kid = PI_BYTE[2]; //user info conveyed there is only 16 allotments for Key, and keys are channel specific, so no key id value
uint32_t target = ((unsigned long long int)PI_BYTE[7] << 16) | ((unsigned long long int)PI_BYTE[8] << 8) | ((unsigned long long int)PI_BYTE[9] << 0);
//TODO: Use ALG and TGT value here to produce a key id via hashing it
uint8_t hash = alg * target % 256; //more complex hash later (honestly, probably not)
//MI only appears to be 32-bit
uint32_t mi = ((uint32_t)PI_BYTE[3] << 24) | ((uint32_t)PI_BYTE[4] << 16) |
((uint32_t)PI_BYTE[5] << 8) | ((uint32_t)PI_BYTE[6] << 0);
if (state->currentslot == 0)
{
state->dmr_so = so;
state->payload_algid = alg;
state->payload_keyid = hash;
state->payload_mi = mi;
}
else
{
state->dmr_soR = so;
state->payload_algidR = alg;
state->payload_keyidR = hash;
state->payload_miR = mi;
}
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot %d", state->currentslot+1);
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08X;", alg, hash, mi);
fprintf (stderr, " Kirisun ");
if (alg == 0x36)
fprintf (stderr, "Advanced;");
else if (alg == 0x37)
fprintf (stderr, "Universal;");
else fprintf (stderr, "Encryption;");
fprintf (stderr, "%s", KNRM);
//disable late entry for DMRA, Flag 3 for future check of VC-F 48-bit region with Golay 24,12 Encoding
//SEE: https://patents.google.com/patent/CN102307075A/en?q=(kirisun)&q=(dmr)&oq=kirisun
//tested patent above with information provided, VC-F had a SB, but the 48-bit
//full value yielded bad Golay results, and user submitted info contradicts this patent for samples provided
//Late Entry MI does appear to work, however, and reports a good CRC for that as well,
//reverse engineered LFSR for Kirisun lines up with the late entry for 32-bit MI values.
opts->dmr_le = 3;
}
if (MFID == 0x68) //Hytera Enhanced
{
if (state->currentslot == 0)
{
state->dmr_so |= 0x40; //OR the enc bit onto the SO
state->payload_algid = PI_BYTE[0];
state->payload_keyid = PI_BYTE[2];
state->payload_mi = ((unsigned long long int)PI_BYTE[3] << 32ULL) | ((unsigned long long int)PI_BYTE[4] << 24) |
((unsigned long long int)PI_BYTE[5] << 16) | ((unsigned long long int)PI_BYTE[6] << 8) | ((unsigned long long int)PI_BYTE[7] << 0);
}
else
{
state->dmr_soR |= 0x40; //OR the enc bit onto the SO
state->payload_algidR = PI_BYTE[0];
state->payload_keyidR = PI_BYTE[2];
state->payload_miR = ((unsigned long long int)PI_BYTE[3] << 32ULL) | ((unsigned long long int)PI_BYTE[4] << 24) |
((unsigned long long int)PI_BYTE[5] << 16) | ((unsigned long long int)PI_BYTE[6] << 8) | ((unsigned long long int)PI_BYTE[7] << 0);
}
fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot %d", state->currentslot+1);
fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(40): %02X%02X%02X%02X%02X;",
PI_BYTE[0], PI_BYTE[2], PI_BYTE[3], PI_BYTE[4], PI_BYTE[5], PI_BYTE[6], PI_BYTE[7]);
//PI_BYTE[9] is a checksum of the other bytes combined
uint8_t checksum = 0;
for (int i = 0; i < 9; i++)
{
checksum += PI_BYTE[i];
checksum &= 0xFF;
}
checksum = ~checksum & 0xFF;
checksum++;
//debug
// fprintf (stderr, " CHK: %02X / %02X;", checksum, PI_BYTE[9]);
if (checksum == PI_BYTE[9])
{
fprintf (stderr, " Hytera Enhanced; ");
if (state->currentslot == 0 && state->R != 0)
fprintf (stderr, "Key: %010llX; ", state->R);
if (state->currentslot == 1 && state->RR != 0)
fprintf (stderr, "Key: %010llX; ", state->RR);
//disable late entry for DMRA (hopefully, there aren't any systems running both DMRA and Hytera Enhanced mixed together)
opts->dmr_le = 2;
// fprintf (stderr, " (Checksum Okay);");
}
else
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (Checksum Err);");
}
fprintf (stderr, "%s", KNRM);
}
else if (MFID == 0x10) //DMRA
{
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
state->payload_algid = PI_BYTE[0]; state->payload_algid = PI_BYTE[0];
state->payload_keyid = PI_BYTE[2]; state->payload_keyid = PI_BYTE[2];
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) ); 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 < 0x26)
{ {
fprintf (stderr, "%s ", KYEL); fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot 1"); fprintf (stderr, "\n Slot 1");
fprintf (stderr, " DMR PI H- ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algid, state->payload_keyid, state->payload_mi); fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08llX;", state->payload_algid, state->payload_keyid, state->payload_mi);
//Anytone RC4 Shim //check for any values that aren't 0x2X but just 0x0X
if (state->payload_algid == 0x01) //going to be very generic here to avoid any particular vendors using 0x2X and not 0x0X
if (state->payload_algid & 0x20)
fprintf (stderr, " DMRA");
else fprintf (stderr, " DMRA Compatible");
if ((state->payload_algid & 0x07) == 0x01)
{ {
fprintf (stderr, " Anytone (0x01)"); fprintf (stderr, " RC4;");
state->payload_algid = 0x21; state->payload_algid = 0x21;
} }
else if ((state->payload_algid & 0x07) == 0x02)
{
fprintf (stderr, " DES;");
state->payload_algid = 0x22;
}
else if ((state->payload_algid & 0x07) == 0x04)
{
fprintf (stderr, " AES-128;");
state->payload_algid = 0x24;
}
else if ((state->payload_algid & 0x07) == 0x05)
{
fprintf (stderr, " AES-256;");
state->payload_algid = 0x25;
}
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
if (state->payload_algid != 0x21)
//expand the 32-bit MI to a 64-bit DES IV
if (state->payload_algid == 0x22)
{ {
fprintf (stderr, "\n"); fprintf (stderr, "\n");
LFSR64 (state); LFSR64 (state);
} }
//expand the 32-bit MI to a 128-bit AES IV
if (state->payload_algid == 0x24 || state->payload_algid == 0x25)
{
fprintf (stderr, "\n");
LFSR128d (state);
}
} }
if (state->payload_algid >= 0x26) if (state->payload_algid >= 0x26)
@@ -63,26 +212,59 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
state->payload_algidR = PI_BYTE[0]; state->payload_algidR = PI_BYTE[0];
state->payload_keyidR = PI_BYTE[2]; state->payload_keyidR = PI_BYTE[2];
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) ); state->payload_miR = ((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_algidR < 0x26) if (state->payload_algidR < 0x26)
{ {
fprintf (stderr, "%s ", KYEL); fprintf (stderr, "%s ", KYEL);
fprintf (stderr, "\n Slot 2"); fprintf (stderr, "\n Slot 2");
fprintf (stderr, " DMR PI H- ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algidR, state->payload_keyidR, state->payload_miR); fprintf (stderr, " DMR PI H- ALG ID: %02X; KEY ID: %02X; MI(32): %08llX;", state->payload_algidR, state->payload_keyidR, state->payload_miR);
//Anytone RC4 Shim //check for any values that aren't 0x2X but just 0x0X
if (state->payload_algidR == 0x01) //going to be very generic here to avoid any particular vendors using 0x2X and not 0x0X
if (state->payload_algidR & 0x20)
fprintf (stderr, " DMRA");
else fprintf (stderr, " DMRA Compatible");
if ((state->payload_algidR & 0x07) == 0x01)
{ {
fprintf (stderr, " Anytone (0x01)"); fprintf (stderr, " RC4;");
state->payload_algidR = 0x21; state->payload_algidR = 0x21;
} }
else if ((state->payload_algidR & 0x07) == 0x02)
{
fprintf (stderr, " DES;");
state->payload_algidR = 0x22;
}
else if ((state->payload_algidR & 0x07) == 0x04)
{
fprintf (stderr, " AES-128;");
state->payload_algidR = 0x24;
}
else if ((state->payload_algidR & 0x07) == 0x05)
{
fprintf (stderr, " AES-256;");
state->payload_algidR = 0x25;
}
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
if (state->payload_algidR != 0x21)
//expand the 32-bit MI to a 64-bit DES IV
if (state->payload_algidR == 0x22)
{ {
fprintf (stderr, "\n"); fprintf (stderr, "\n");
LFSR64 (state); LFSR64 (state);
} }
//expand the 32-bit MI to a 128-bit AES IV
if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25)
{
fprintf (stderr, "\n");
LFSR128d (state);
}
} }
if (state->payload_algidR >= 0x26) if (state->payload_algidR >= 0x26)
@@ -94,12 +276,14 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
} }
} //end DMRA
} }
} }
void LFSR(dsd_state * state) void LFSR(dsd_state * state)
{ {
int lfsr = 0; unsigned long long int lfsr = 0;
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
lfsr = state->payload_mi; lfsr = state->payload_mi;
@@ -111,16 +295,19 @@ void LFSR(dsd_state * state)
for(cnt=0;cnt<32;cnt++) for(cnt=0;cnt<32;cnt++)
{ {
// Polynomial is C(x) = x^32 + x^4 + x^2 + 1 // Polynomial is C(x) = x^32 + x^4 + x^2 + 1
int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 0x1; unsigned long long int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 0x1;
lfsr = (lfsr << 1) | (bit); lfsr = (lfsr << 1) | bit;
} }
lfsr &= 0xFFFFFFFF;
if (state->currentslot == 0) if (state->currentslot == 0)
{ {
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1"); fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid); fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid);
fprintf(stderr, " MI: 0x%08X", lfsr); fprintf (stderr, " MI(32): %08llX;", lfsr);
fprintf (stderr, " RC4;");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->payload_mi = lfsr; state->payload_mi = lfsr;
} }
@@ -130,13 +317,15 @@ void LFSR(dsd_state * state)
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2"); fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR); fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR);
fprintf(stderr, " MI: 0x%08X", lfsr); fprintf(stderr, " MI(32): %08llX;", lfsr);
fprintf (stderr, " RC4;");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->payload_miR = lfsr; state->payload_miR = lfsr;
} }
} }
//Expand a 32-bit MI into a 64-bit IV for DES
void LFSR64(dsd_state * state) void LFSR64(dsd_state * state)
{ {
{ {
@@ -160,21 +349,209 @@ void LFSR64(dsd_state * state)
{ {
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1"); fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid); fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algid, state->payload_keyid);
fprintf (stderr, " MI: 0x%016llX", lfsr); fprintf (stderr, " MI(64): %016llX;", lfsr);
fprintf (stderr, " DES;");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->payload_mi = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification state->payload_mi = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
state->payload_miP = lfsr;
state->DMRvcL = 0;
} }
if (state->currentslot == 1) if (state->currentslot == 1)
{ {
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2"); fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR); fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X;", state->payload_algidR, state->payload_keyidR);
fprintf (stderr, " MI: 0x%016llX", lfsr); fprintf (stderr, " MI(64): %016llX;", lfsr);
fprintf (stderr, " DES;");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->payload_miR = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification state->payload_miR = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
state->payload_miN = lfsr;
state->DMRvcR = 0;
} }
} }
} }
//Expand a 32-bit MI into a 128-bit IV for AES
void LFSR128d(dsd_state * state)
{
unsigned long long int lfsr = 0;
if (state->currentslot == 0)
lfsr = state->payload_mi;
else lfsr = state->payload_miR;
unsigned long long int next_mi;
//start packing aes_iv
if (state->currentslot == 0)
{
state->aes_iv[0] = (lfsr >> 24) & 0xFF;
state->aes_iv[1] = (lfsr >> 16) & 0xFF;
state->aes_iv[2] = (lfsr >> 8 ) & 0xFF;
state->aes_iv[3] = (lfsr >> 0 ) & 0xFF;
}
else if (state->currentslot == 1)
{
state->aes_ivR[0] = (lfsr >> 24) & 0xFF;
state->aes_ivR[1] = (lfsr >> 16) & 0xFF;
state->aes_ivR[2] = (lfsr >> 8 ) & 0xFF;
state->aes_ivR[3] = (lfsr >> 0 ) & 0xFF;
}
int cnt = 0; int x = 32;
unsigned long long int bit;
for(cnt=0;cnt<96;cnt++)
{
//32,22,2,1
bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
lfsr = (lfsr << 1) | bit;
//continue packing aes_iv
if (state->currentslot == 0)
state->aes_iv[x/8] = (state->aes_iv[x/8] << 1) + bit;
else if (state->currentslot == 1)
state->aes_ivR[x/8] = (state->aes_ivR[x/8] << 1) + bit;
x++;
}
//assign the next 32-bit short MI from 4,5,6,7 so it'll match up with OTA late entry
if (state->currentslot == 0)
next_mi = ((unsigned long long int)state->aes_iv[4] << 24) | ((unsigned long long int)state->aes_iv[5] << 16) |
((unsigned long long int)state->aes_iv[6] << 8) | ((unsigned long long int)state->aes_iv[7] << 0);
if (state->currentslot == 1)
next_mi = ((unsigned long long int)state->aes_ivR[4] << 24) | ((unsigned long long int)state->aes_ivR[5] << 16) |
((unsigned long long int)state->aes_ivR[6] << 8) | ((unsigned long long int)state->aes_ivR[7] << 0);
if (state->currentslot == 0)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 1");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X; MI(128): ", state->payload_algid, state->payload_keyid);
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", state->aes_iv[x]);
fprintf (stderr, "%s", KNRM);
fprintf (stderr, ";");
if (state->payload_algid == 0x24)
fprintf (stderr, " AES-128;");
else fprintf (stderr, " AES-256;");
state->payload_mi = next_mi;
state->DMRvcL = 0;
}
if (state->currentslot == 1)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " Slot 2");
fprintf (stderr, " DMR PI C- ALG ID: %02X; KEY ID: %02X; MI(128): ", state->payload_algidR, state->payload_keyidR);
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", state->aes_ivR[x]);
fprintf (stderr, "%s", KNRM);
fprintf (stderr, ";");
if (state->payload_algidR == 0x24)
fprintf (stderr, " AES-128;");
else fprintf (stderr, " AES-256;");
state->payload_miR = next_mi;
state->DMRvcR = 0;
}
}
unsigned long long int hytera_lfsr(uint8_t * mi, uint8_t * taps, uint8_t len)
{
for (uint8_t i = 0; i < len; i++)
{
uint8_t bit = (mi[i] >> 7) & 1;
mi[i] <<= 1;
if (bit) mi[i] ^= taps[i%len];
mi[i] |= bit;
}
unsigned long long int mi_value = 0;
for (uint8_t i = 0; i < len; i++)
{
mi_value <<= 8;
mi_value |= mi[i];
}
//debug
// fprintf (stderr, " Next MI: %010llX \n", mi_value);
return mi_value;
}
void hytera_enhanced_alg_refresh(dsd_state * state)
{
uint8_t mi[5]; memset (mi, 0, sizeof(mi));
unsigned long long int mi_value = 0;
if (state->currentslot == 0)
mi_value = state->payload_mi;
else mi_value = state->payload_miR;
//load mi_value into mi array
mi[0] = ((mi_value & 0xFF00000000) >> 32UL);
mi[1] = ((mi_value & 0xFF000000) >> 24);
mi[2] = ((mi_value & 0xFF0000) >> 16);
mi[3] = ((mi_value & 0xFF00) >> 8);
mi[4] = ((mi_value & 0xFF) >> 0);
//calculate the next MI value
uint8_t taps[5]; memset(taps, 0, sizeof(taps));
//the tap values
taps[0] = 0x12;
taps[1] = 0x24;
taps[2] = 0x48;
taps[3] = 0x22;
taps[4] = 0x14;
mi_value = hytera_lfsr(mi, taps, 5);
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

@@ -10,7 +10,7 @@
#include "dsd.h" #include "dsd.h"
//modified to accept variable payload size and len //modified to accept variable payload size and len
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len) uint16_t ComputeCrcCCITT16d(const uint8_t * buf, uint32_t len)
{ {
uint32_t i; uint32_t i;
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */ uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */

View File

@@ -376,7 +376,7 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
//check other as well before assigning //check other as well before assigning
if(state->dPMRVoiceFS2Frame.CallingIDOk) sprintf (state->dpmr_target_id, "%s", CalledID); if(state->dPMRVoiceFS2Frame.CalledIDOk) sprintf (state->dpmr_target_id, "%s", CalledID);
} }
else else

825
src/dsd_alias.c Normal file
View File

@@ -0,0 +1,825 @@
/*-------------------------------------------------------------------------------
* dsd_alias.c
* Talker Alias Handling for Various Protocols and Vendors
*
* LWVMOBILE
* 2025-02 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//Motorola P25 OTA Alias Decoding ripped/demystified from Ilya Smirnov's SDRTrunk Voodoo Code
uint8_t moto_alias_lut[256] = {
0xD2, 0xF6, 0xD4, 0x2B, 0x63, 0x49, 0x94, 0x5E, 0xA7, 0x5C, 0x70, 0x69, 0xF7, 0x08, 0xB1, 0x7D,
0x38, 0xCF, 0xCC, 0xD8, 0x51, 0x8F, 0xD5, 0x93, 0x6A, 0xF3, 0xEF, 0x7E, 0xFB, 0x64, 0xF4, 0x35,
0x27, 0x07, 0x31, 0x14, 0x87, 0x98, 0x76, 0x34, 0xCA, 0x92, 0x33, 0x1B, 0x4F, 0x8C, 0x09, 0x40,
0x32, 0x36, 0x77, 0x12, 0xD3, 0xC3, 0x01, 0xAB, 0x72, 0x81, 0x95, 0xC9, 0xC0, 0xE9, 0x65, 0x52,
0x24, 0x30, 0x1C, 0xDB, 0x88, 0xE8, 0x97, 0x9D, 0x58, 0x26, 0x04, 0x39, 0xAC, 0x2A, 0x9E, 0xAA,
0x25, 0xD7, 0xCE, 0xEB, 0x96, 0xF5, 0x0E, 0x8D, 0xDC, 0xA9, 0x2F, 0xDD, 0x1F, 0xEA, 0x91, 0xB7,
0xD6, 0x89, 0x8B, 0xD1, 0xB0, 0x99, 0x13, 0x7A, 0xE7, 0x9A, 0xB5, 0x86, 0xFF, 0x46, 0x85, 0xB2,
0x73, 0xDA, 0xBF, 0xD0, 0x71, 0xCB, 0x4D, 0x80, 0x15, 0x67, 0x16, 0x1A, 0x20, 0x8E, 0x45, 0x3E,
0xF2, 0x2E, 0x66, 0x90, 0x74, 0x8A, 0x6F, 0x78, 0xBB, 0x53, 0x03, 0x11, 0x68, 0xCD, 0x44, 0x17,
0x28, 0x5F, 0x1E, 0x84, 0x75, 0x79, 0x6E, 0x9B, 0x2C, 0xBE, 0x62, 0x2D, 0xF1, 0x7C, 0xB8, 0x83,
0xD9, 0x4E, 0x6D, 0x02, 0x61, 0x3D, 0xA8, 0x06, 0xB9, 0xF8, 0x9C, 0x37, 0x3A, 0x23, 0xC1, 0x50,
0xED, 0x9F, 0xAF, 0x3B, 0xBD, 0x82, 0xBA, 0xA0, 0xDF, 0xC2, 0x47, 0x22, 0xF0, 0xEE, 0xA1, 0xFE,
0xA2, 0x10, 0x5B, 0x48, 0x57, 0xA3, 0x05, 0x60, 0x7B, 0x0D, 0xF9, 0x6C, 0xB3, 0x56, 0x4C, 0xBC,
0x29, 0xA4, 0x0F, 0xEC, 0xB6, 0xA5, 0xA6, 0x3C, 0x7F, 0x6B, 0xB4, 0x21, 0xAD, 0xAE, 0xC4, 0xC8,
0xC5, 0x5D, 0xDE, 0xE0, 0x1D, 0x19, 0x4B, 0xC6, 0x0C, 0x3F, 0x5A, 0xC7, 0xE1, 0x59, 0x55, 0x54,
0x4A, 0x43, 0x42, 0xE2, 0xE3, 0xFA, 0x00, 0xE4, 0xE5, 0x18, 0x41, 0x0B, 0x0A, 0xE6, 0xFC, 0xFD
};
void apx_embedded_alias_test_phase1 (dsd_opts * opts, dsd_state * state)
{
uint64_t temp_a = 0x15902D2806010005; uint64_t temp_b = 0xDE; //72 bits, so break into segments
uint8_t lcw[72]; memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x1790010BEE0740F0; temp_b = 0x4E;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x17900200DD2D2168; temp_b = 0x1A;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x17900301B52FFBFB; temp_b = 0xFE;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x1790040E53FE86BE; temp_b = 0xF7;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x17900508FD5AB910; temp_b = 0xB2;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x1790060376F9D800; temp_b = 0x00;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
//Harris Phase 1 GPS
state->lastsrc = 1000;
temp_a = 0x2AA41D4C24262328; temp_b = 0xAF;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
temp_a = 0x2BA44E0DB2660108; temp_b = 0x14;
memset (lcw, 0, sizeof(lcw));
for (uint64_t i = 0; i < 64; i++)
lcw[i] = (temp_a >> (63-i)) & 1;
for (uint64_t i = 0; i < 8; i++)
lcw[i+64] = (temp_b >> (7-i)) & 1;
p25_lcw(opts, state, lcw, 0);
state->lastsrc = 0;
}
void apx_embedded_alias_header_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
UNUSED(opts);
uint8_t ta_len = (uint8_t)ConvertBitIntoBytes(&lc_bits[32], 8); //len in blocks of associated talker alias
uint8_t sn = (uint8_t)ConvertBitIntoBytes(&lc_bits[56], 4);
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " BN: 0/%d;", ta_len);
//use dmr_pdu_sf for storage, store entire header (will be used to verify complete reception of full alias)
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot])); //reset storage for header and blocks
memcpy (state->dmr_pdu_sf[slot], lc_bits, 72*sizeof(uint8_t));
}
void apx_embedded_alias_blocks_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
UNUSED(opts);
uint8_t bn = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8); //current block number
uint8_t sn = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 4); //is a static value on all block sequences
uint8_t ta_len = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][32], 8); //len in blocks pulled from stored header
uint16_t header = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][0], 16); //header check, should be 0x1590
if (ta_len == 0 || header != 0x1590) //checkdown, make sure we have an up to date header for this with a good len value
{
fprintf (stderr, " Missing Header");
fprintf (stderr, " BN: %d/??;", bn);
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " Partial: ");
for (uint8_t i = 7; i < 18; i++)
fprintf (stderr, "%0X", (uint8_t)ConvertBitIntoBytes(&lc_bits[0+(i*4)], 4));
//clear out now stale storage
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot]));
}
else //good len and header stored
{
//sanity check, bn cannot equal zero (this shouldn't happen, but bad decode could occur)
if (bn == 0) bn = 1;
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " BN: %d/%d;", bn, ta_len);
//use dmr_pdu_sf for storage, store data relevant portion at ptr of (bn-1) * 44 + 72 offset for header
memcpy(state->dmr_pdu_sf[slot]+(((bn-1)*44)+72), lc_bits+28, 44*sizeof(uint8_t));
if (ta_len == bn) //this is the last block, proceed to decoding
{
//Calculate variable len bits determined by non-zero octets
int16_t num_bits = 56; //starting value is the static bit count on the fqsuid
// //evaluate the storage and determine how many octets/bits are present at this point (fall back to this if needed)
// for (int16_t i = 0; i < 368; i++) //(3072-128)/8
// {
// uint8_t byte = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][72+56+(i*8)], 8);
// if (byte == 0)
// break;
// else
// num_bits += 8;
// }
//evaluate the storage and determine how many octets/bits are present at this point (expanded to two octets each, CRC with a 00xx pattern failed this)
for (int16_t i = 0; i < 184; i++) //(3072-128)/16
{
uint16_t bytes = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][72+56+(i*16)], 16);
if (bytes == 0)
break;
else
num_bits += 16;
}
//pass to alias decoder
apx_embedded_alias_decode (opts, state, slot, num_bits, state->dmr_pdu_sf[slot]);
//clear out now stale storage
memset (state->dmr_pdu_sf[slot], 0, sizeof(state->dmr_pdu_sf[slot]));
}
}
}
void apx_embedded_alias_header_phase2 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
UNUSED(opts);
uint8_t ta_len = (uint8_t)ConvertBitIntoBytes(&lc_bits[40], 8);
uint8_t sn = (uint8_t)ConvertBitIntoBytes(&lc_bits[64], 4);
uint8_t bn = (uint8_t)ConvertBitIntoBytes(&lc_bits[56], 8);
fprintf (stderr, " SN: %X;", sn); //NOTE: vPDU header is also a partial block, and has a block num and SN value in it
fprintf (stderr, " BN: %d/%d;", bn, ta_len);
//bit array to rearrange input lc_bits from phase 2 input to match the phase 1 header and block handling
uint8_t bits[136]; memset(bits, 0, sizeof(bits));
memcpy (bits, lc_bits, 2*8*sizeof(uint8_t)); //header 0x9190
memcpy (bits+16, lc_bits+24, 4*8*sizeof(uint8_t)); //BN, SN, etc
memcpy (bits+56, lc_bits+56, 10*8*sizeof(uint8_t)); //adding 8 bits of extra padding here
int16_t alias_st = 136; //start of the encoded alias (the copied size of this header, basically)
//debug, dump header arranged at this end
// fprintf (stderr, " Header: ");
// for (int16_t i = 0; i < alias_st/8; i++) //double check and adjust
// fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(&bits[0+(i*8)], 8));
//use dmr_pdu_sf for storage, store entire header (will be used to verify complete reception of full alias)
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot])); //reset storage for header and blocks
memcpy (state->dmr_pdu_sf[slot], bits, alias_st*sizeof(uint8_t)); //this header block has 128 bits of relevant data (through the fqsuid)
}
void apx_embedded_alias_blocks_phase2 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
UNUSED(opts);
int16_t rel_bits = 100; //number of relevant bits in each block
int16_t rel_st = 36; //start of relevant bits in this block
int16_t alias_st = 136; //start of the encoded alias
uint8_t bn = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 8); //current block number
uint8_t sn = (uint8_t)ConvertBitIntoBytes(&lc_bits[32], 4); //is a static value on all block sequences
uint8_t ta_len = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][32], 8); //len in blocks pulled from stored header
uint16_t header = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][0], 16); //header check, should be 0x9190
if (ta_len == 0 || header != 0x9190)
{
fprintf (stderr, " Missing Header");
fprintf (stderr, " BN: %d/??;", bn);
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " Partial: ");
for (uint8_t i = 9; i < 32; i++) //double check and adjust
fprintf (stderr, "%0X", (uint8_t)ConvertBitIntoBytes(&lc_bits[0+(i*4)], 4));
//clear out now stale storage
memset (state->dmr_pdu_sf[slot], 0, sizeof(state->dmr_pdu_sf[slot]));
}
else //good len and header stored
{
//sanity check, bn cannot equal zero (this shouldn't happen, but bad decode could occur)
if (bn == 0) bn = 1;
fprintf (stderr, " SN: %X;", sn);
fprintf (stderr, " BN: %d/%d;", bn, ta_len);
//use dmr_pdu_sf for storage, store data relevant portion at ptr calculated below
memcpy(state->dmr_pdu_sf[slot]+(alias_st+((bn-1)*rel_bits)), lc_bits+rel_st, rel_bits*sizeof(uint8_t)); //Fix this value when samples arrive
//debug, dump accumulated data at this end
// fprintf (stderr, " Accumulated: ");
// for (int16_t i = 0; i < (alias_st+(rel_bits*bn))/8; i++) //double check and adjust
// fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][0+(i*8)], 8));
if (ta_len == bn) //this is the last block, proceed to decoding
{
//Calculate variable len bits determined by non-zero octets
int16_t num_bits = 56; //starting value is the static bit count on the fqsuid
// //evaluate the storage and determine how many octets/bits are present at this point (fall back to this if needed)
// for (int16_t i = 0; i < 368; i++) //(3072-128)/8
// {
// uint8_t byte = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][72+56+(i*8)], 8);
// if (byte == 0)
// break;
// else
// num_bits += 8;
// }
//evaluate the storage and determine how many octets/bits are present at this point (expanded to two octets each, CRC with a 00xx pattern failed this)
for (int16_t i = 0; i < 184; i++) //(3072-128)/16
{
uint16_t bytes = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][72+56+(i*16)], 16);
if (bytes == 0)
break;
else
num_bits += 16;
}
//pass to alias decoder
apx_embedded_alias_decode (opts, state, slot, num_bits, state->dmr_pdu_sf[slot]);
//clear out now stale storage
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot]));
}
}
}
void apx_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t num_bits, uint8_t * input)
{
UNUSED(opts);
UNUSED(state);
UNUSED(slot);
//debug, dump completed data set
// fprintf (stderr, "\n Full: ");
// for (int16_t i = 0; i < (72+num_bits)/4; i++)
// fprintf (stderr, "%X", (uint8_t)ConvertBitIntoBytes(&input[0+(i*4)], 4));
//extract CRC
uint16_t crc_ext = (uint16_t)ConvertBitIntoBytes(&input[(72+num_bits-16)], 16);
//compute CRC
uint16_t crc_cmp = ComputeCrcCCITT16d(&input[72], num_bits-16);
//print comparison
// fprintf (stderr, " CRC EXT: %04X CMP: %04X;", crc_ext, crc_cmp);
if (crc_ext != crc_cmp)
fprintf (stderr, " Alias CRC Error;");
// else fprintf (stderr, " Alias Okay;");
//start decoding the alias
if (crc_ext == crc_cmp)
{
//extract fully qualified SUID
uint32_t wacn = (uint32_t)ConvertBitIntoBytes(&input[72], 20);
uint32_t sys = (uint32_t)ConvertBitIntoBytes(&input[92], 12);
uint32_t rid = (uint32_t)ConvertBitIntoBytes(&input[104], 24);
//print fully qualified SUID
fprintf (stderr, "\n FQ-SUID: %05X.%03X.%06X (%d);", wacn, sys, rid, rid);
//WIP: Working, needs more samples to verify various num_bits values
uint16_t ptr = 128; //starting point of encoded alias
uint8_t encoded[200]; memset(encoded, 0, sizeof(encoded));
uint8_t decoded[200]; memset(decoded, 0, sizeof(decoded));
uint16_t num_bytes = (num_bits / 8) - 9; //subtract 2 CRC and 7 FQSUID
//sanity check
if (num_bytes <= 0) num_bytes = 1;
for (int16_t i = 0; i < num_bytes; i++)
{
encoded[i] = (uint8_t)ConvertBitIntoBytes(&input[ptr], 8);
ptr += 8;
}
uint16_t accumulator = num_bytes;
//Ilya's Voodoo Code
for (uint16_t i = 0; i < num_bytes; i++)
{
// Multiplication step 1
uint16_t accum_mult = accumulator * 293 + 0x72E9;
// Lookup table step
uint8_t lut = moto_alias_lut[encoded[i]];
uint8_t mult1 = lut - (accum_mult >> 8);
// Incrementing step
uint8_t mult2 = 1;
uint8_t shortstop = accum_mult | 0x1;
uint8_t increment = shortstop << 1;
//clang warning -- warning: result of comparison of constant -1 with expression of type 'uint8_t' (aka 'unsigned char') is always true [-Wtautological-constant-out-of-range-compare]
// while(mult2 != -1 && shortstop != 1) //clang warning can't be -1 if uint8_t (set to 255 instead?)
while(shortstop != 1) //this one tests out okay, so may use it instead
{
shortstop += increment;
mult2 += 2;
}
// Multiplication step 2
decoded[i] = mult1 * mult2;
// Update the accumulator
accumulator += encoded[i] + 1;
}
// //debug, dump just the encoded alias portion
// fprintf (stderr, "\n Encoded: ");
// for (int16_t i = 0; i < num_bytes; i++)
// fprintf (stderr, "%02X", encoded[i]);
// //debug, dump the decoded payload as hex octets
// fprintf (stderr, "\n Decoded: ");
// for (int16_t i = 0; i < num_bytes; i++)
// fprintf (stderr, "%02X", decoded[i]);
//dump the decoded payload as long chars
// fprintf (stderr, "\n");
fprintf (stderr, " Alias: ");
for (int16_t i = 0; i < num_bytes/2; i++)
fprintf (stderr, "%lc", ((decoded[(i*2)+0])<<8) | ((decoded[(i*2)+1])<<0) );
apx_embedded_alias_dump (opts, state, slot, num_bytes, input, decoded);
}
}
void apx_embedded_alias_dump (dsd_opts * opts, dsd_state * state, uint8_t slot, uint16_t num_bytes, uint8_t * input, uint8_t * decoded)
{
char str[50]; memset(str, 0, sizeof(str));
char fqs[50]; memset(fqs, 0, sizeof(fqs));
//check num_bytes, if greter than 100, then set to 100
if (num_bytes >= 98) num_bytes = 98;
for (int16_t i = 0; i < num_bytes/2; i++)
{
if (decoded[(i*2)+1] == 0x2C) //remove a comma if it exists, change it to a 0x2E dot
str[i] = 0x2E;
else if (decoded[(i*2)+1] > 0x1F && decoded[(i*2)+1] < 0x7F) //may not need or use this restriction
str[i] = decoded[(i*2)+1];
else str[i] = 0x20; //space
}
//flag to indicate this already exists in import or group struct
uint8_t wr = 0;
//fully qualified SUID
uint32_t wacn = (uint32_t)ConvertBitIntoBytes(&input[72], 20);
uint32_t sys = (uint32_t)ConvertBitIntoBytes(&input[92], 12);
uint32_t rid = (uint32_t)ConvertBitIntoBytes(&input[104], 24);
sprintf (fqs, " FQ-SUID: %05X:%03X.%06X (%d);", wacn, sys, rid, rid);
if (rid != 0 && state->event_history_s[slot].Event_History_Items[0].source_id == rid)
{
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s; ", str);
strcat (state->event_history_s[slot].Event_History_Items[0].alias, fqs);
}
for (int16_t i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == rid)
{
wr = 1; //already in there, so no need to assign it
break;
}
}
if (wr == 0) //not already in there, so save it there now
{
state->group_array[state->group_tally].groupNumber = rid;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "D");
sprintf (state->group_array[state->group_tally].groupName, "%s", str);
state->group_tally++;
//if we have an opened group file, let's write what info we found into it
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,D,", rid); //may want to not use this one
fprintf (pFile, "%s", str);
fprintf (pFile, ",FQS:%05X.%03X.%06X(%d),Moto\n", wacn, sys, rid, rid);
fclose (pFile);
}
}
}
//end Motorola P25 OTA Alias Decoding
//TODO: Migrate Other OTA Alias functions here
void l3h_embedded_alias_blocks_phase1 (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
uint8_t op = (uint8_t)ConvertBitIntoBytes(&lc_bits[0], 8);
uint8_t ptr = op-0x32;
uint8_t bytes[7]; memset(bytes, 0, sizeof(bytes));
for (uint8_t i = 0; i < 7; i++)
bytes[i] = (uint8_t)ConvertBitIntoBytes(&lc_bits[16+(i*8)], 8);
//use +4 offset to match the MAC vPDU since that was already worked out long ago
memcpy(state->dmr_pdu_sf[slot]+4+(ptr*7), bytes, sizeof(bytes));
//to be tested
if (ptr == 4) //is there always 4 blocks, or is it a variable amount?
l3h_embedded_alias_decode(opts, state, slot, 4+28, state->dmr_pdu_sf[slot]);
}
void l3h_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t len, uint8_t * input)
{
//storage info for storing to groupName, if not available
char str[40]; memset (str, 0, sizeof(str));
char ttemp[40]; memset (ttemp, 0, sizeof(ttemp));
uint8_t wr = 0;
uint32_t tsrc = 0, ttg = 0;
if (slot == 0 && state->lastsrc != 0) tsrc = state->lastsrc;
if (slot == 1 && state->lastsrcR != 0) tsrc = state->lastsrcR;
if (slot == 0 && state->lasttg != 0) ttg = state->lasttg;
if (slot == 1 && state->lasttgR != 0) ttg = state->lasttgR;
int8_t ptr = 0;
if (tsrc != 0)
fprintf (stderr, " TG: %d; SRC: %d; Talker Alias: ", ttg, tsrc);
else fprintf (stderr, " TG: UNK; SRC: UNK; Talker Alias: ");
for (int8_t i = 4; i <= len; i++)
{
if ( (input[i] > 0x19) && (input[i] < 0x7F) )
fprintf (stderr, "%c", (char)input[i]);
else fprintf (stderr, " ");
if (input[i] == 0x2C) //remove a comma if it exists, change it to a 0x2E dot
ttemp[ptr] = 0x2E;
else if ( (input[i] > 0x19) && (input[i] < 0x7F) )
ttemp[ptr] = input[i];
else if (input[i] != 0)
ttemp[ptr] = 0x20; //space
ptr++;
}
//assign completed talker to a more useful string instead
snprintf (str, ptr+1, "%s", ttemp);
if (state->event_history_s[slot].Event_History_Items[0].source_id == tsrc && tsrc != 0)
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s", str);
//The Duke Energy system may relay two src values, may be a good idea to pick one and stick with it
if (tsrc != 0)
{
for (int16_t i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == tsrc)
{
wr = 1; //already in there, so no need to assign it
break;
}
}
if (wr == 0) //not already in there, so save it there now
{
state->group_array[state->group_tally].groupNumber = tsrc;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "D");
sprintf (state->group_array[state->group_tally].groupName, "%s", str);
state->group_tally++;
//if we have an opened group file, let's write what info we found into it
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,D,", tsrc);
fprintf (pFile, "%s", str);
fprintf (pFile, ",TG:%d,SYS:%03llX,RFSS:%lld,SITE:%lld,Harris\n", ttg, state->p2_sysid, state->p2_rfssid, state->p2_siteid);
fclose (pFile);
}
}
}
//debug
// fprintf (stderr, "\n WR: %d TG: %d SRC: %d Res: %d Len: %d STR: %s", wr, ttg, tsrc, res, len, str);
//reset storage
memset (state->dmr_pdu_sf[slot], 0, sizeof (state->dmr_pdu_sf[slot]));
}
void tait_iso7_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, int16_t len, uint8_t * input)
{
UNUSED(slot);
uint8_t alias[24]; memset(alias, 0, sizeof(alias));
for (uint8_t i = 0; i < len; i++)
{
alias[i] = (uint8_t)ConvertBitIntoBytes(&input[16+(i*7)], 7);
fprintf (stderr, "%c", alias[i]);
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;
}
//flag to indicate this already exists in import or group struct
uint8_t wr = 0;
uint32_t rid = state->lastsrc;
uint16_t nac = state->nac;
if (state->event_history_s[slot].Event_History_Items[0].source_id == rid)
sprintf (state->event_history_s[slot].Event_History_Items[0].alias, "%s", alias);
if (rid != 0)
{
for (int16_t i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == rid)
{
wr = 1; //already in there, so no need to assign it
break;
}
}
if (wr == 0) //not already in there, so save it there now
{
state->group_array[state->group_tally].groupNumber = rid;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "D");
sprintf (state->group_array[state->group_tally].groupName, "%s", alias);
state->group_tally++;
//if we have an opened group file, let's write what info we found into it
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,D,", rid); //may want to not use this one
fprintf (pFile, "%s,", alias);
fprintf (pFile, "%03X,", nac); //if we find this on a trunking system, may want to add the site and rfss id
fprintf (pFile, "%s", ",Tait\n");
fclose (pFile);
}
}
}
}
void dmr_talker_alias_lc_header (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t * lc_bits)
{
uint8_t format = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 2);
uint8_t block_len = (uint8_t)ConvertBitIntoBytes(&lc_bits[18], 5);
uint8_t char_size = 0;
if (format == 0) char_size = 7;
else if (format == 1 || format == 2) char_size = 8;
else char_size = 16;
state->dmr_alias_format[slot] = format;
state->dmr_alias_block_len[slot] = block_len; //data len, see below
state->dmr_alias_char_size[slot] = char_size;
//load into dmr_pdu_sf as bit wise values for this since iso7 has 49 bits in this header, otherwise, load with 48?
if (char_size == 7)
memcpy(state->dmr_pdu_sf[slot], lc_bits+23, 49*sizeof(uint8_t));
else if (char_size == 8)
memcpy(state->dmr_pdu_sf[slot], lc_bits+24, 48*sizeof(uint8_t));
else if (char_size == 16)
memcpy(state->dmr_pdu_sf[slot], lc_bits+24, 48*sizeof(uint8_t));
//TEST: The Block Len (Data Lan) value is, according to my interpretation, the number of encoded
//character units in the alias, test to verify, but fall back to the ptr index if necessary
//this is referenced in 5.4.3 ETSI TS 102 361-2 V2.5.1 (2023-05) 7.2.19 Talker Alias Data Length
fprintf (stderr, " Slot %d - Talker Alias LC Header; Format %d; Char Len: %d; Char Size: %d;", slot, format, block_len, char_size);
//Decode the header's alias portion
fprintf (stderr, "\n");
dmr_talker_alias_lc_decode(opts, state, slot, 0, char_size, block_len);
}
void dmr_talker_alias_lc_blocks (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t block_num, uint8_t * lc_bits)
{
UNUSED(opts); //delete if we don't use this, but may want it if we dump alias to a file later on
uint8_t block_len = state->dmr_alias_block_len[slot];
uint8_t char_size = state->dmr_alias_char_size[slot];
uint16_t ptr = 0;
uint16_t end = block_len;
//Note: The Joann sample only carries block 5, and no header on block 4
//another radio on that ham setup also has a broken alias similar, but
//the other talkers have the 04 and the sample has a good decode otherwise
//debug broken aliasing on ham radio
// if (char_size == 0) //Enable this is you need to check alias even with missing header (set 7, 8, or 16)
// char_size = 8;
/*
22:54:51 Sync: +DMR slot1 [SLOT2] | Color Code=01 | VC6
Slot 1 - Talker Alias Block Num: 1; Valid Block; Talker Alias: Joann
DMR PDU Payload [05][00][4A][6F][61][6E][6E][00][FF]
*/
//set the pointer to the current index of the dmr_pdu_sf
if (char_size == 16)
ptr = 48 + (block_num * 56);
else if (char_size == 8)
ptr = 48 + (block_num * 56);
else if (char_size == 7)
ptr = 49 + (block_num * 56);
if (char_size == 0) //unset, no header received
fprintf (stderr, " Slot %d - Talker Alias Block Num: %d; Invalid Header;", slot, block_num+1);
else if (block_num > 3) //invalid block (data error)
fprintf (stderr, " Slot %d - Talker Alias Block Num: %d; Invalid Block;", slot, block_num+1);
else //valid header present, continue
{
if (char_size == 7)
{
memcpy(state->dmr_pdu_sf[slot]+ptr, lc_bits+16, 56*sizeof(uint8_t));
ptr += 56;
if (block_len == 0)
end = ptr / 7;
else end = block_len;
}
else if (char_size == 8)
{
memcpy(state->dmr_pdu_sf[slot]+ptr, lc_bits+16, 56*sizeof(uint8_t));
ptr += 56;
if (block_len == 0)
end = ptr / 8;
else end = block_len;
}
else if (char_size == 16)
{
memcpy(state->dmr_pdu_sf[slot]+ptr, lc_bits+16, 56*sizeof(uint8_t));
ptr += 56;
if (block_len == 0)
end = ptr / 16;
else end = block_len;
}
dmr_talker_alias_lc_decode (opts, state, slot, block_num+1, char_size, end);
}
}
//Decode partial or completed alias
void dmr_talker_alias_lc_decode (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t block_num, uint8_t char_size, uint16_t end)
{
UNUSED(opts);
uint16_t i = 0;
fprintf (stderr, " Slot %d - Talker Alias Block Num: %d; Valid Block;", slot, block_num+1);
fprintf (stderr, " Talker Alias: ");
char alias_string[500]; memset (alias_string, 0, sizeof(alias_string));
sprintf (alias_string, "%s", "");
if (char_size == 7)
{
for (i = 0; i < end; i++)
{
uint8_t character = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][(i*7)], 7);
char ch[2]; ch[0] = character; ch[1] = 0;
if (character >= 0x20 && character <= 0x7E) //Standard ASCII Set
{
fprintf (stderr, "%c", character);
strcat (alias_string, ch);
}
// else if (character == 0)
// {
// strcat (alias_string, " ");
// fprintf (stderr, " ");
// break;
// }
else
{
strcat (alias_string, " ");
fprintf (stderr, " ");
}
}
}
else if (char_size == 8)
{
for (i = 0; i < end; i++)
{
uint8_t character = (uint8_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[slot][(i*8)], 8);
char ch[2]; ch[0] = character; ch[1] = 0;
// if (character >= 0x20 && character <= 0x7E) //Standard ASCII Set
if (character >= 0x20 && character != 0x7F) //allow some extended UTF diacritical characters as well
{
fprintf (stderr, "%c", character);
strcat (alias_string, ch);
}
// else if (character == 0)
// {
// strcat (alias_string, " ");
// fprintf (stderr, " ");
// break;
// }
else
{
strcat (alias_string, " ");
fprintf (stderr, " ");
}
}
}
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);
if (character >= 0x20 && character != 0x7F)
fprintf (stderr, "%lc", character);
else fprintf (stderr, " ");
//Encode to UTF-8
ptr += utf8_encode(character, alias_string+ptr);
//debug
// fprintf (stderr, " [%04X], ", character);
}
}
//assign to string for event history and ncurses display
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; ", alias_string);
sprintf (state->generic_talker_alias[slot], "Talker Alias: %s; ", alias_string);
}

View File

@@ -75,7 +75,7 @@ void openPulseOutput(dsd_opts * opts)
pa_channel_map* fl = 0; //NULL and 0 are same in this context pa_channel_map* fl = 0; //NULL and 0 are same in this context
pa_channel_map* ss = 0; //NULL and 0 are same in this context pa_channel_map* ss = 0; //NULL and 0 are same in this context
if (opts->floating_point == 0) if (opts->floating_point == 0 && opts->analog_only == 0)
{ {
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, dev, opts->output_name, &tt, ss, NULL, &err); opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, dev, opts->output_name, &tt, ss, NULL, &err);
@@ -89,7 +89,7 @@ void openPulseOutput(dsd_opts * opts)
} }
} }
if (opts->floating_point == 1) if (opts->floating_point == 1 && opts->analog_only == 0)
{ {
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, dev, opts->output_name, &ff, fl, NULL, &err); opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, dev, opts->output_name, &ff, fl, NULL, &err);
@@ -137,9 +137,11 @@ void openPulseInput(dsd_opts * opts)
lt.maxlength = -1; lt.maxlength = -1;
lt.prebuf = -1; lt.prebuf = -1;
lt.tlength = -1; lt.tlength = -1;
if (opts->m17encoder == 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); // 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); // 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) 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) void openOSSOutput (dsd_opts * opts)
{ {
int fmt; int fmt;
@@ -205,6 +215,7 @@ void openOSSOutput (dsd_opts * opts)
exit(1); exit(1);
} }
fmt = 0; fmt = 0;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0) if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
{ {
@@ -236,7 +247,7 @@ void openOSSOutput (dsd_opts * opts)
} }
} }
if (opts->audio_in_type != 5) //split == 1 if (opts->audio_in_type != 5 || opts->split == 1) //split == 1
{ {
if((strncmp(opts->audio_out_dev, "/dev/dsp", 8) == 0)) if((strncmp(opts->audio_out_dev, "/dev/dsp", 8) == 0))
@@ -301,6 +312,7 @@ void openOSSOutput (dsd_opts * opts)
} }
} }
} }
#endif
void void
processAudio (dsd_opts * opts, dsd_state * state) processAudio (dsd_opts * opts, dsd_state * state)
@@ -650,6 +662,42 @@ void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
} }
//short Mono to Stereo version for new static .wav files in stereo format for TDMA
void writeSynthesizedVoiceMS (dsd_opts * opts, dsd_state * state)
{
int n;
short aout_buf[160];
short *aout_buf_p;
aout_buf_p = aout_buf;
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
for (n = 0; n < 160; n++)
{
if (*state->audio_out_temp_buf_p > (float) 32767)
{
*state->audio_out_temp_buf_p = (float) 32767;
}
else if (*state->audio_out_temp_buf_p < (float) -32768)
{
*state->audio_out_temp_buf_p = (float) -32768;
}
*aout_buf_p = (short) *state->audio_out_temp_buf_p;
aout_buf_p++;
state->audio_out_temp_buf_p++;
}
short ss[320];
for (n = 0; n < 160; n++)
{
ss[(n*2)+0] = aout_buf[n];
ss[(n*2)+1] = aout_buf[n];
}
sf_write_short(opts->wav_out_f, ss, 320);
}
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample) void writeRawSample (dsd_opts * opts, dsd_state * state, short sample)
{ {
UNUSED(state); UNUSED(state);
@@ -901,7 +949,7 @@ openAudioInDevice (dsd_opts * opts)
} }
//test .rrc files with hardset wav file settings //test .rrc files with hardset wav file settings
else if (strncmp(extension, ".rrc", 3) == 0) else if (strncmp(extension, ".rrc", 4) == 0)
{ {
//debug //debug
fprintf (stderr, "Opening M17 .rrc headless wav file\n"); fprintf (stderr, "Opening M17 .rrc headless wav file\n");
@@ -921,19 +969,19 @@ openAudioInDevice (dsd_opts * opts)
} }
} }
//TODO: test .sym files as symbol capture .bin files //Open .raw files as a float input type
else if (strncmp(extension, ".sym", 3) == 0) else if (strncmp(extension, ".raw", 4) == 0)
{ {
struct stat stat_buf; struct stat stat_buf;
if (stat(opts->audio_in_dev, &stat_buf) != 0) if (stat(opts->audio_in_dev, &stat_buf) != 0)
{ {
fprintf (stderr,"Error, couldn't open bin file %s\n", opts->audio_in_dev); fprintf (stderr,"Error, couldn't open raw (float) file %s\n", opts->audio_in_dev);
exit(1); exit(1);
} }
if (S_ISREG(stat_buf.st_mode)) if (S_ISREG(stat_buf.st_mode))
{ {
opts->symbolfile = fopen(opts->audio_in_dev, "r"); opts->symbolfile = fopen(opts->audio_in_dev, "r");
opts->audio_in_type = 4; //symbol capture bin files opts->audio_in_type = 44; //float symbol input
} }
else else
{ {
@@ -941,7 +989,27 @@ openAudioInDevice (dsd_opts * opts)
} }
} }
else if (strncmp(extension, ".bin", 3) == 0) //Open .raw files as a float input type
else if (strncmp(extension, ".sym", 4) == 0)
{
struct stat stat_buf;
if (stat(opts->audio_in_dev, &stat_buf) != 0)
{
fprintf (stderr,"Error, couldn't open sym (float) file %s\n", opts->audio_in_dev);
exit(1);
}
if (S_ISREG(stat_buf.st_mode))
{
opts->symbolfile = fopen(opts->audio_in_dev, "r");
opts->audio_in_type = 44; //float symbol input
}
else
{
opts->audio_in_type = 0;
}
}
else if (strncmp(extension, ".bin", 4) == 0)
{ {
struct stat stat_buf; struct stat stat_buf;
if (stat(opts->audio_in_dev, &stat_buf) != 0) if (stat(opts->audio_in_dev, &stat_buf) != 0)

View File

@@ -93,13 +93,21 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x21) else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22)
{ {
if (state->R != 0) if (state->R != 0)
{ {
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
} }
if (encR) if (encR)
@@ -111,14 +119,26 @@ void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state)
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR == 0x21) else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22)
{ {
if (state->RR != 0) if (state->RR != 0)
{ {
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
} }
}
}
//debug disable enc check for AES testing
// encL = 0;
// encR = 0;
//TODO: add option to bypass enc with a toggle as well //TODO: add option to bypass enc with a toggle as well
@@ -312,6 +332,20 @@ void playSynthesizedVoiceFS4 (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x81)
{
if (state->R != 0)
{
encL = 0;
}
}
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89)
{
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
} }
if (encR) if (encR)
@@ -323,6 +357,20 @@ void playSynthesizedVoiceFS4 (dsd_opts * opts, dsd_state * state)
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR == 0x81)
{
if (state->R != 0)
{
encR = 0;
}
}
else if (state->payload_algidR == 0x84 || state->payload_algidR == 0x89)
{
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
}
}
} }
//CHEAT: Using the slot on/off, use that to set encL or encR back on //CHEAT: Using the slot on/off, use that to set encL or encR back on
@@ -520,13 +568,20 @@ void playSynthesizedVoiceFS (dsd_opts * opts, dsd_state * state)
//checkdown to see if we can lift the 'mute' if a key is available //checkdown to see if we can lift the 'mute' if a key is available
if (encL) if (encL)
{ {
if (state->payload_algid == 0xAA) if (state->payload_algid == 0xAA || state->payload_algid == 0x81 || state->payload_algid == 0x9F)
{ {
if (state->R != 0) if (state->R != 0)
{ {
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->payload_algid == 0x83)
{
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
} }
//TODO: add option to bypass enc with a toggle as well //TODO: add option to bypass enc with a toggle as well
@@ -654,13 +709,20 @@ void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state)
//checkdown to see if we can lift the 'mute' if a key is available //checkdown to see if we can lift the 'mute' if a key is available
if (encL) if (encL)
{ {
if (state->payload_algid == 0xAA || state->nxdn_cipher_type == 0x1) if (state->payload_algid == 0xAA || state->payload_algid == 0x81 || state->payload_algid == 0x83 || state->payload_algid == 0x9F || state->nxdn_cipher_type == 0x1 || state->nxdn_cipher_type == 0x2)
{ {
if (state->R != 0) if (state->R != 0)
{ {
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
} }
//WIP: Mute if on B list (or not W list) //WIP: Mute if on B list (or not W list)
@@ -766,6 +828,31 @@ void playSynthesizedVoiceMS (dsd_opts * opts, dsd_state * state)
if (opts->audio_out_type == 1 || opts->audio_out_type == 2 || opts->audio_out_type == 5) //STDOUT or OSS if (opts->audio_out_type == 1 || opts->audio_out_type == 2 || opts->audio_out_type == 5) //STDOUT or OSS
write (opts->audio_out_fd, mono_samp, len*2); write (opts->audio_out_fd, mono_samp, len*2);
//this one needs testing w/ 48000 OSS output when audio is not split
if (opts->wav_out_f != NULL && opts->static_wav_file == 1)
{
//convert to stereo for new static wav file setup
short ss[320];
memset (ss, 0, sizeof(ss));
if (len == 160)
{
for (i = 0; i < 160; i++)
{
ss[(i*2)+0] = mono_samp[i];
ss[(i*2)+1] = mono_samp[i];
}
}
else if (len == 960)
{
for (i = 0; i < 160; i++)
{
ss[(i*2)+0] = mono_samp[i*6]; //grab every 6th sample to downsample
ss[(i*2)+1] = mono_samp[i*6]; //grab every 6th sample to downsample
}
}
sf_write_short(opts->wav_out_f, ss, 320);
}
MS_END: MS_END:
//run cleanup since we pulled stuff from processAudio //run cleanup since we pulled stuff from processAudio
@@ -827,6 +914,31 @@ void playSynthesizedVoiceMSR (dsd_opts * opts, dsd_state * state)
if (opts->audio_out_type == 1 || opts->audio_out_type == 2 || opts->audio_out_type == 5) //STDOUT or OSS if (opts->audio_out_type == 1 || opts->audio_out_type == 2 || opts->audio_out_type == 5) //STDOUT or OSS
write (opts->audio_out_fd, mono_samp, len*2); write (opts->audio_out_fd, mono_samp, len*2);
//this one needs testing w/ 48000 OSS output when audio is not split
if (opts->wav_out_f != NULL && opts->static_wav_file == 1)
{
//convert to stereo for new static wav file setup
short ss[320];
memset (ss, 0, sizeof(ss));
if (len == 160)
{
for (i = 0; i < 160; i++)
{
ss[(i*2)+0] = mono_samp[i];
ss[(i*2)+1] = mono_samp[i];
}
}
else if (len == 960)
{
for (i = 0; i < 160; i++)
{
ss[(i*2)+0] = mono_samp[i*6]; //grab every 6th sample to downsample
ss[(i*2)+1] = mono_samp[i*6]; //grab every 6th sample to downsample
}
}
sf_write_short(opts->wav_out_f, ss, 320);
}
MS_ENDR: MS_ENDR:
//run cleanup since we pulled stuff from processAudioR //run cleanup since we pulled stuff from processAudioR
@@ -870,13 +982,20 @@ void playSynthesizedVoiceSS (dsd_opts * opts, dsd_state * state)
//checkdown to see if we can lift the 'mute' if a key is available //checkdown to see if we can lift the 'mute' if a key is available
if (encL) if (encL)
{ {
if (state->payload_algid == 0xAA) if (state->payload_algid == 0xAA || state->payload_algid == 0x81 || state->payload_algid == 0x9F || state->payload_algid == 0x83)
{ {
if (state->R != 0) if (state->R != 0)
{ {
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89)
{
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
} }
//TODO: add option to bypass enc with a toggle as well //TODO: add option to bypass enc with a toggle as well
@@ -939,6 +1058,8 @@ void playSynthesizedVoiceSS (dsd_opts * opts, dsd_state * state)
if (opts->audio_out_type == 1 || opts->audio_out_type == 2) //STDOUT or OSS 8k/2 if (opts->audio_out_type == 1 || opts->audio_out_type == 2) //STDOUT or OSS 8k/2
write (opts->audio_out_fd, stereo_samp1, 320*2); write (opts->audio_out_fd, stereo_samp1, 320*2);
if (opts->wav_out_f != NULL && opts->static_wav_file == 1)
sf_write_short(opts->wav_out_f, stereo_samp1, 320);
SSM_END: SSM_END:
@@ -1003,13 +1124,21 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x21) else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22)
{ {
if (state->R != 0) if (state->R != 0)
{ {
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
} }
if (encR) if (encR)
@@ -1021,14 +1150,25 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR == 0x21) else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22)
{ {
if (state->RR != 0) if (state->RR != 0)
{ {
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37)
{
//going to need a better check for this later on, or seperated keys or something
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
} }
}
}
//debug for AES
// encL = encR = 0;
//TODO: add option to bypass enc with a toggle as well //TODO: add option to bypass enc with a toggle as well
@@ -1270,6 +1410,13 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
write (opts->audio_out_fd, stereo_samp3, 320*2); write (opts->audio_out_fd, stereo_samp3, 320*2);
} }
if (opts->wav_out_f != NULL && opts->static_wav_file == 1)
{
sf_write_short(opts->wav_out_f, stereo_samp1, 320);
sf_write_short(opts->wav_out_f, stereo_samp2, 320);
sf_write_short(opts->wav_out_f, stereo_samp3, 320);
}
SS3_END: SS3_END:
//run cleanup since we pulled stuff from processAudio //run cleanup since we pulled stuff from processAudio
@@ -1342,6 +1489,20 @@ void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state)
encL = 0; encL = 0;
} }
} }
else if (state->payload_algid == 0x81)
{
if (state->R != 0)
{
encL = 0;
}
}
else if (state->payload_algid == 0x84 || state->payload_algid == 0x89)
{
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
} }
if (encR) if (encR)
@@ -1353,6 +1514,20 @@ void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state)
encR = 0; encR = 0;
} }
} }
else if (state->payload_algidR == 0x81)
{
if (state->R != 0)
{
encR = 0;
}
}
else if (state->payload_algidR == 0x84 || state->payload_algidR == 0x89)
{
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
}
}
} }
@@ -1495,6 +1670,18 @@ void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state)
write (opts->audio_out_fd, stereo_samp4, 320*2); write (opts->audio_out_fd, stereo_samp4, 320*2);
} }
if (opts->wav_out_f != NULL && opts->static_wav_file == 1)
{
sf_write_short(opts->wav_out_f, stereo_samp1, 320);
sf_write_short(opts->wav_out_f, stereo_samp2, 320);
//only write these two if not a single 2v or double 2v
if (memcmp(empty, stereo_samp3, sizeof(empty)) != 0)
sf_write_short(opts->wav_out_f, stereo_samp3, 320);
if (memcmp(empty, stereo_samp4, sizeof(empty)) != 0)
sf_write_short(opts->wav_out_f, stereo_samp4, 320);
}
SS4_END: SS4_END:
//run cleanup since we pulled stuff from processAudio //run cleanup since we pulled stuff from processAudio
@@ -1552,25 +1739,51 @@ void playSynthesizedVoiceSS18 (dsd_opts * opts, dsd_state * state)
//checkdown to see if we can lift the 'mute' if a key is available //checkdown to see if we can lift the 'mute' if a key is available
if (encL) if (encL)
{ {
if (state->payload_algid == 0xAA) //RC4 or DES
if (state->payload_algid == 0xAA || state->payload_algid == 0x81)
{ {
if (state->R != 0) if (state->R != 0)
{ {
encL = 0; encL = 0;
} }
} }
//AES
if (state->payload_algid == 0x84 || state->payload_algid == 0x89)
{
if (state->aes_key_loaded[0] == 1)
{
encL = 0;
}
}
} }
if (encR) if (encR)
{ {
if (state->payload_algidR == 0xAA) //RC4 or DES
if (state->payload_algidR == 0xAA || state->payload_algidR == 0x81)
{ {
if (state->RR != 0) if (state->RR != 0)
{ {
encR = 0; encR = 0;
} }
} }
//AES
if (state->payload_algidR == 0x84 || state->payload_algidR == 0x89)
{
if (state->aes_key_loaded[1] == 1)
{
encR = 0;
} }
}
}
//debug, unmute both slots
// encL = 0;
// encR = 0;
//WIP: Mute if on B list (or not W list) //WIP: Mute if on B list (or not W list)
char modeL[8]; char modeL[8];
@@ -1773,6 +1986,15 @@ void playSynthesizedVoiceSS18 (dsd_opts * opts, dsd_state * state)
} }
} }
if (opts->wav_out_f != NULL && opts->static_wav_file == 1)
{
for (j = 0; j < 18; j++)
{
if (memcmp(empty, stereo_sf[j], sizeof(empty)) != 0)
sf_write_short(opts->wav_out_f, stereo_sf[j], 320);
}
}
SS18_END: SS18_END:
//run cleanup since we pulled stuff from processAudio //run cleanup since we pulled stuff from processAudio
@@ -1986,3 +2208,127 @@ void analog_gain (dsd_opts * opts, dsd_state * state, short * input, int len)
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
input[i] *= gain; input[i] *= gain;
} }
void beeper (dsd_opts * opts, dsd_state * state, int lr, int id, int ad, int len)
{
UNUSED(state);
int i, j, n;
//use lr as left or right channel designation in stereo config
float samp_f[160]; //mono float sample
float samp_fs[320]; //stereo float sample
short samp_s[160]; //mono short sample
short samp_ss[320]; //stereo short sample
short samp_su[960]; //mono short upsample
short outbuf[6]; //temp storage for upsample
n = 0; //rolling sine wave 'degree'
//double len if not using Pulse Audio,
//anything over UDP or using OSS may
//not clear the buffer at the shorter len
if (opts->audio_out_type != 0)
len *= 2;
//each j increment is 20 ms at 160 samples / 8 kHz
for (j = 0; j < len; j++)
{
//'zero' out stereo mix samples
memset (samp_fs, 0, sizeof(samp_fs));
memset (samp_ss, 0, sizeof(samp_ss));
//generate a tone with supplied tone ID and AD value
soft_tonef(samp_f, n, id, ad);
//convert float to short if required
if (opts->floating_point == 0)
{
mbe_floattoshort(samp_f, samp_s);
for (i = 0; i < 160; i++)
{
samp_s[i] *= 4000; //apply gain
samp_ss[(i*2)+lr] = samp_s[i];
}
}
//load returned tone sample into appropriate channel -- left = +0; right = +1;
for (i = 0; i < 160; i++)
samp_fs[(i*2)+lr] = samp_f[i];
//play sample 3 times (20ms x 3 = 60ms)
if (opts->audio_out_type == 0) //Pulse Audio
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1)
pa_simple_write(opts->pulse_digi_dev_out, samp_fs, 320*4, NULL);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1)
pa_simple_write(opts->pulse_digi_dev_out, samp_f, 160*4, NULL);
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
pa_simple_write(opts->pulse_digi_dev_out, samp_ss, 320*2, NULL);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
pa_simple_write(opts->pulse_digi_dev_out, samp_s, 160*2, NULL);
}
else if (opts->audio_out_type == 8) //UDP Audio
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1)
udp_socket_blaster (opts, state, 320*4, samp_fs);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1)
udp_socket_blaster (opts, state, 160*4, samp_f);
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
udp_socket_blaster (opts, state, 320*2, samp_ss);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
udp_socket_blaster (opts, state, 160*2, samp_s);
}
else if (opts->audio_out_type == 1) //STDOUT
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1)
write(opts->audio_out_fd, samp_fs, 320*4);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1)
write(opts->audio_out_fd, samp_f, 160*4);
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_ss, 320*2);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_s, 160*2);
}
else if (opts->audio_out_type == 2) //OSS Variable Output (no float)
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_ss, 320*2);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_s, 160*2);
}
else if (opts->audio_out_type == 5) //OSS 48k/1 configuration with upsample
{
short prev = 0;
for (i = 0; i < 160; i++)
{
upsampleS (samp_s[i], prev, outbuf);
samp_su[(i*6)+0] = outbuf[0];
samp_su[(i*6)+1] = outbuf[1];
samp_su[(i*6)+2] = outbuf[2];
samp_su[(i*6)+3] = outbuf[3];
samp_su[(i*6)+4] = outbuf[4];
samp_su[(i*6)+5] = outbuf[5];
}
write (opts->audio_out_fd, samp_su, 960*2);
}
}
}

870
src/dsd_events.c Normal file
View File

@@ -0,0 +1,870 @@
/*-------------------------------------------------------------------------------
* dsd_events.c
* DSD-FME event history init, watchdog, push, and related functions
*
*
* LWVMOBILE
* 2025-05 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "git_ver.h"
#include "synctype.h"
//init each event history struct passed into here
void init_event_history (Event_History_I * event_struct, uint8_t start, uint8_t stop)
{
for (uint8_t i = start; i < stop; i++)
{
event_struct->Event_History_Items[i].write = 0;
event_struct->Event_History_Items[i].color_pair = 4;
event_struct->Event_History_Items[i].systype = -1;
event_struct->Event_History_Items[i].subtype = -1;
event_struct->Event_History_Items[i].sys_id1 = 0;
event_struct->Event_History_Items[i].sys_id2 = 0;
event_struct->Event_History_Items[i].sys_id3 = 0;
event_struct->Event_History_Items[i].sys_id4 = 0;
event_struct->Event_History_Items[i].sys_id5 = 0;
event_struct->Event_History_Items[i].gi = 0;
event_struct->Event_History_Items[i].enc = 0;
event_struct->Event_History_Items[i].enc_alg = 0;
event_struct->Event_History_Items[i].enc_key = 0;
event_struct->Event_History_Items[i].mi = 0;
event_struct->Event_History_Items[i].svc = 0;
event_struct->Event_History_Items[i].source_id = 0;
event_struct->Event_History_Items[i].target_id = 0;
sprintf (event_struct->Event_History_Items[i].src_str, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].tgt_str, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].t_name, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].s_name, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].t_mode, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].s_mode, "%s", "BUMBLEBEETUNA");
event_struct->Event_History_Items[i].channel = 0;
event_struct->Event_History_Items[i].event_time = 0;
memset (event_struct->Event_History_Items[i].pdu, 0, sizeof(event_struct->Event_History_Items[0].pdu));
sprintf (event_struct->Event_History_Items[i].sysid_string, "%s", "");
sprintf (event_struct->Event_History_Items[i].alias, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].gps_s, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].text_message, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].event_string, "%s", "BUMBLEBEETUNA");
sprintf (event_struct->Event_History_Items[i].internal_str, "%s", "BUMBLEBEETUNA");
}
}
void push_event_history (Event_History_I * event_struct)
{
//Fixed, had it going in the wrong direction first time
for (uint8_t i = 254; i >= 1; i--)
{
event_struct->Event_History_Items[i].write = event_struct->Event_History_Items[i-1].write;
event_struct->Event_History_Items[i].color_pair = event_struct->Event_History_Items[i-1].color_pair;
event_struct->Event_History_Items[i].systype = event_struct->Event_History_Items[i-1].systype;
event_struct->Event_History_Items[i].subtype = event_struct->Event_History_Items[i-1].subtype;
event_struct->Event_History_Items[i].sys_id1 = event_struct->Event_History_Items[i-1].sys_id1;
event_struct->Event_History_Items[i].sys_id2 = event_struct->Event_History_Items[i-1].sys_id2;
event_struct->Event_History_Items[i].sys_id3 = event_struct->Event_History_Items[i-1].sys_id3;
event_struct->Event_History_Items[i].sys_id4 = event_struct->Event_History_Items[i-1].sys_id4;
event_struct->Event_History_Items[i].sys_id5 = event_struct->Event_History_Items[i-1].sys_id5;
event_struct->Event_History_Items[i].gi = event_struct->Event_History_Items[i-1].gi;
event_struct->Event_History_Items[i].enc = event_struct->Event_History_Items[i-1].enc;
event_struct->Event_History_Items[i].enc_alg = event_struct->Event_History_Items[i-1].enc_alg;
event_struct->Event_History_Items[i].enc_key = event_struct->Event_History_Items[i-1].enc_key;
event_struct->Event_History_Items[i].mi = event_struct->Event_History_Items[i-1].mi;
event_struct->Event_History_Items[i].svc = event_struct->Event_History_Items[i-1].svc;
event_struct->Event_History_Items[i].source_id = event_struct->Event_History_Items[i-1].source_id;
event_struct->Event_History_Items[i].target_id = event_struct->Event_History_Items[i-1].target_id;
sprintf (event_struct->Event_History_Items[i].src_str, "%s", event_struct->Event_History_Items[i-1].src_str);
sprintf (event_struct->Event_History_Items[i].tgt_str, "%s", event_struct->Event_History_Items[i-1].tgt_str);
sprintf (event_struct->Event_History_Items[i].t_name, "%s", event_struct->Event_History_Items[i-1].t_name);
sprintf (event_struct->Event_History_Items[i].s_name, "%s", event_struct->Event_History_Items[i-1].s_name);
sprintf (event_struct->Event_History_Items[i].t_mode, "%s", event_struct->Event_History_Items[i-1].t_mode);
sprintf (event_struct->Event_History_Items[i].s_mode, "%s", event_struct->Event_History_Items[i-1].s_mode);
event_struct->Event_History_Items[i].channel = event_struct->Event_History_Items[i-1].channel;
event_struct->Event_History_Items[i].event_time = event_struct->Event_History_Items[i+1].event_time;
memcpy (event_struct->Event_History_Items[i].pdu, event_struct->Event_History_Items[i-1].pdu, sizeof(event_struct->Event_History_Items[0].pdu));
sprintf (event_struct->Event_History_Items[i].sysid_string, "%s", event_struct->Event_History_Items[i-1].sysid_string);
sprintf (event_struct->Event_History_Items[i].alias, "%s", event_struct->Event_History_Items[i-1].alias);
sprintf (event_struct->Event_History_Items[i].gps_s, "%s", event_struct->Event_History_Items[i-1].gps_s);
sprintf (event_struct->Event_History_Items[i].text_message, "%s", event_struct->Event_History_Items[i-1].text_message);
sprintf (event_struct->Event_History_Items[i].event_string, "%s", event_struct->Event_History_Items[i-1].event_string);
sprintf (event_struct->Event_History_Items[i].internal_str, "%s", event_struct->Event_History_Items[i-1].internal_str);
}
}
void write_event_to_log_file (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t swrite, char * event_string) //pass completed event string here that is in the struct
{
//open log file
FILE * event_log_file;
event_log_file = fopen(opts->event_out_file, "a");
fprintf (event_log_file, "%s ", event_string);
if (swrite == 1)
fprintf (event_log_file, "Slot %d; ", slot+1);
fprintf (event_log_file,"\n");
char text_string[2000]; sprintf (text_string, "%s", "BUMBLEBEETUNA");
if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].text_message, 13) != 0)
fprintf (event_log_file, "%s \n", state->event_history_s[slot].Event_History_Items[0].text_message);
if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].alias, 13) != 0)
fprintf (event_log_file, " Talker Alias: %s \n", state->event_history_s[slot].Event_History_Items[0].alias);
if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].gps_s, 13) != 0)
fprintf (event_log_file, " GPS: %s \n", state->event_history_s[slot].Event_History_Items[0].gps_s);
if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].internal_str, 13) != 0)
fprintf (event_log_file, " DSD-FME: %s \n", state->event_history_s[slot].Event_History_Items[0].internal_str);
//flush and close log file
fflush (event_log_file);
fclose (event_log_file);
}
// run once per loop to check for and push and update event history
void watchdog_event_history (dsd_opts * opts, dsd_state * state, uint8_t slot)
{
//create a pointer to the current slot event history
Event_History_I * event_struct = &state->event_history_s[slot];
//is this a TDMA slot (append Slot value to end of written event history)
uint8_t swrite = 0;
//who is currently talking
uint32_t source_id = 0;
//last values pulled from the event history
uint32_t last_source_id = event_struct->Event_History_Items[0].source_id;
if (slot == 0)
source_id = state->lastsrc;
else
source_id = state->lastsrcR;
//if DMR BS or P25P2, then flag the swrite, so write can append slot value to event history log //|| state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 MS
if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13)
swrite = 1;
else if (state->lastsynctype == 35 || state->lastsynctype == 36)
swrite = 1;
if (slot == 0) //BUGFIX: generic catch on FDMA systems so that we don't write duplicate data to slot 2 event history
{
//NXDN RID (TODO: Changeover to lastsrc later on)
if (state->lastsynctype == 28 || state->lastsynctype == 29)
source_id = state->nxdn_last_rid;
if (state->lastsynctype == 30 || state->lastsynctype == 31) //YSF Fusion
{
source_id = 0;
if (strncmp(state->ysf_src, " ", 10) != 0) //if this field does not have ten spaces in it
{
for (uint8_t i = 0; i < 11; i++)
source_id += state->ysf_src[i]; //convert to sum value to make a distinct enough src value
}
}
if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17) //M17 STR
{
source_id = (uint32_t)state->m17_src;
}
if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR
{
source_id = 0;
for (uint8_t i = 0; i < 12; i++)
source_id += state->dstar_src[i]; //convert to sum value to make a distinct enough src value
//need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history
if (strncmp(state->dstar_src, " ", 8) == 0)
source_id = 0;
}
if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR
{
source_id = 0;
for (uint8_t i = 0; i < 20; i++)
source_id += state->dpmr_caller_id[i]; //convert to sum value to make a distinct enough src value
//need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history
if (strncmp(state->dpmr_caller_id, " ", 6) == 0)
source_id = 0;
}
if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls
{
source_id = 0;
if (opts->p25_is_tuned == 1)
source_id = state->lastsrc;
}
}
//call alert beep when new call detected
if (last_source_id == 0 && source_id != 0 && opts->call_alert == 1)
beeper (opts, state, slot, 40, 86, 3);
if (source_id != last_source_id && last_source_id != 0)
{
if (opts->event_out_file[0] != 0)
write_event_to_log_file(opts, state, slot, swrite, event_struct->Event_History_Items[0].event_string);
event_struct->Event_History_Items[0].write = 1; //written, or pushed at this point
if (opts->static_wav_file == 0)
{
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 = 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 = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0);
}
}
push_event_history (event_struct);
init_event_history (event_struct, 0, 1);
//clear out some strings and things
memset(state->ysf_txt, 0, sizeof(state->ysf_txt));
memset(state->dstar_gps, 0, sizeof(state->dstar_gps));
memset(state->dstar_txt, 0, sizeof(state->dstar_txt));
state->gi[slot] = -1; //return to an unset value
//end of voice call alert
if (opts->call_alert == 1)
beeper (opts, state, slot, 40, 86, 3);
}
}
//similar to above, but constantly testing and checking the most recent event only
//this will hopefully be more useful when dealing with an ongoing event with
//features that update over time with embedded signalling, etc
void watchdog_event_current (dsd_opts * opts, dsd_state * state, uint8_t slot)
{
//create a pointer to the current slot event history
Event_History_I * event_struct = &state->event_history_s[slot];
//ncurses color pairs
uint8_t color_pair = 4; //default voice color (unknown gi)
if (state->gi[slot] == 1)
color_pair = 4; //default private voice color
else if (state->gi[slot] == 0)
color_pair = 4; //default group voice color
//TODO: Flesh out more later on.
uint32_t source_id = 0;
uint32_t target_id = 0;
char src_str[200]; memset (src_str, 0, sizeof(src_str));
sprintf (src_str, "%s", "BUMBLEBEETUNA");
char tgt_str[200]; memset (tgt_str, 0, sizeof(tgt_str));
sprintf (tgt_str, "%s", "BUMBLEBEETUNA");
//group import items
char t_name[200]; memset (t_name, 0, sizeof(t_name));
sprintf (t_name, "%s", "BUMBLEBEETUNA");
char s_name[200]; memset (s_name, 0, sizeof(s_name));
sprintf (s_name, "%s", "BUMBLEBEETUNA");
char t_mode[200]; memset (t_mode, 0, sizeof(t_mode));
sprintf (t_mode, "%s", "BUMBLEBEETUNA");
char s_mode[200]; memset (s_mode, 0, sizeof(s_mode));
sprintf (s_mode, "%s", "BUMBLEBEETUNA");
uint16_t svc_opts = 0;
uint8_t subtype = 0;
uint8_t mfid = 0;
uint32_t sys_id1 = 0;
uint32_t sys_id2 = 0;
uint32_t sys_id3 = 0;
uint32_t sys_id4 = 0;
uint32_t sys_id5 = 0;
uint32_t channel = 0;
uint8_t enc = 0;
uint8_t alg_id = 0;
uint16_t key_id = 0;
unsigned long long int mi;
char sysid_string[200]; memset(sysid_string, 0, sizeof(sysid_string));
sprintf (sysid_string, "%s", "");
if (slot == 0)
{
source_id = state->lastsrc;
target_id = state->lasttg;
subtype = state->dmrburstL;
mfid = state->dmr_fid;
svc_opts = state->dmr_so;
enc = (svc_opts >> 6) & 1;
alg_id = state->payload_algid;
key_id = (uint16_t)state->payload_keyid;
mi = state->payload_mi;
}
else
{
source_id = state->lastsrcR;
target_id = state->lasttgR;
subtype = state->dmrburstR;
mfid = state->dmr_fidR;
svc_opts = state->dmr_soR;
enc = (svc_opts >> 6) & 1;
alg_id = state->payload_algidR;
key_id = (uint16_t)state->payload_keyidR;
mi = state->payload_miR;
}
//if P25 (if not P25, then these will all be zero anyways)
sys_id1 = state->p2_wacn;
sys_id2 = state->p2_sysid;
if (state->nac != 0)
sys_id3 = state->nac; //same as state->p2_cc, but zeroes out when no signal or error
else sys_id3 = state->p2_cc;
sys_id4 = state->p2_rfssid;
sys_id5 = state->p2_siteid;
if (sys_id1)
sprintf (sysid_string, "P25_%05X%03X%03X_%d_%d", sys_id1, sys_id2, sys_id3, sys_id4, sys_id5);
else sprintf (sysid_string, "P25_%03X", sys_id3);
if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13 ||
state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 )
{
sys_id1 = state->dmr_t3_syscode;
sys_id2 = state->dmr_color_code;
if (sys_id1)
sprintf (sysid_string, "DMR_%X_CC_%d", sys_id1, sys_id2);
else sprintf (sysid_string, "DMR_CC_%d", sys_id2);
}
if (slot == 0) //BUGFIX: generic catch on FDMA systems so that we don't write duplicate data to slot 2 event history
{
//NXDN RID (TODO: Changeover to lastsrc and lasttg later on)
if (state->lastsynctype == 28 || state->lastsynctype == 29)
{
source_id = state->nxdn_last_rid;
target_id = state->nxdn_last_tg;
if (state->nxdn_cipher_type != 0)
enc = 1;
alg_id = state->nxdn_cipher_type;
key_id = state->nxdn_key;
sys_id1 = state->nxdn_location_site_code;
sys_id2 = state->nxdn_location_sys_code;
sys_id3 = state->nxdn_last_ran; //might be an issue on conventional systems that have a different RAN on the tx_rel or idle data bursts
if (sys_id1)
sprintf (sysid_string, "NXDN_%d_%d_RAN_%d", sys_id2, sys_id1, sys_id3);
else sprintf (sysid_string, "NXDN_RAN_%d", sys_id3);
}
if (state->lastsynctype == 30 || state->lastsynctype == 31) //YSF Fusion
{
source_id = 0;
if (strncmp(state->ysf_src, " ", 10) != 0) //if this field does not have ten spaces in it
{
for (uint8_t i = 0; i < 11; i++)
source_id += state->ysf_src[i]; //convert to sum value to make a distinct enough src value
}
//WIP: If Text, compile it here (still having issues with an empty txt string making a line break)
uint8_t k = 0; char ysf_emp[21][21]; memset(ysf_emp, 0, sizeof(ysf_emp));
if (memcmp(ysf_emp, state->ysf_txt, sizeof(state->ysf_txt)) != 0)
{
for (uint8_t i = 4; i < 8; i++)
{
for (uint8_t j = 0; j < 20; j++)
{
if (state->ysf_txt[i][j] != 0x2A)
event_struct->Event_History_Items[0].text_message[k++] = state->ysf_txt[i][j];
else event_struct->Event_History_Items[0].text_message[k++] = 0x20; //space
}
event_struct->Event_History_Items[0].text_message[k] = 0; //terminate
}
}
else sprintf (event_struct->Event_History_Items[0].text_message, "%s", "BUMBLEBEETUNA");
sprintf (sysid_string, "%s", "YSF");
char temp_str[20];
memset(temp_str, 0, sizeof(temp_str));
//set src string as a non-spaced non-garbo char string
for (uint8_t i = 0; i < 10; i++)
{
if (state->ysf_src[i] == 0x20) //spaces to underscore
temp_str[i] = 0x5F;
else if (state->ysf_src[i] > 0x20 && state->ysf_src[i] < 0x7F) //copy normal ascii range characters
temp_str[i] = state->ysf_src[i];
else if (state->ysf_src[i] == 0) break; //hit the terminator, so stop
else temp_str[i] = 0x5F; //unknown to underscore
}
sprintf (src_str, "%s", temp_str);
//same for tgt str
memset(temp_str, 0, sizeof(temp_str));
for (uint8_t i = 0; i < 10; i++)
{
if (state->ysf_tgt[i] == 0x20) //spaces to underscore
temp_str[i] = 0x5F;
else if (state->ysf_tgt[i] > 0x20 && state->ysf_tgt[i] < 0x7F) //copy normal ascii range characters
temp_str[i] = state->ysf_tgt[i];
else if (state->ysf_tgt[i] == 0) break; //hit the terminator, so stop
else temp_str[i] = 0x5F; //unknown to underscore
}
sprintf (tgt_str, "%s", temp_str);
}
if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17) //M17 STR
{
target_id = (uint32_t)state->m17_dst;
source_id = (uint32_t)state->m17_src;
sys_id1 = state->m17_can;
sprintf (sysid_string, "M17_CAN_%d", sys_id1);
sprintf (src_str, "%s", state->m17_src_csd);
sprintf (tgt_str, "%s", state->m17_dst_csd);
}
if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR
{
source_id = 0;
for (uint8_t i = 0; i < 12; i++)
source_id += state->dstar_src[i]; //convert to sum value to make a distinct enough src value
//need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history
if (strncmp(state->dstar_src, " ", 8) == 0)
source_id = 0;
sprintf (sysid_string, "%s", "DSTAR");
char temp_str[20];
memset(temp_str, 0, sizeof(temp_str));
//set src string as a non-spaced non-garbo char string
for (uint8_t i = 0; i < 12; i++)
{
if (state->dstar_src[i] == 0x20) //spaces to underscore
temp_str[i] = 0x5F;
else if (state->dstar_src[i] > 0x20 && state->dstar_src[i] < 0x7F) //copy normal ascii range characters
temp_str[i] = state->dstar_src[i];
else if (state->dstar_src[i] == 0) break; //hit the terminator, so stop
else temp_str[i] = 0x5F; //unknown to underscore
}
sprintf (src_str, "%s", temp_str);
//same for tgt str
memset(temp_str, 0, sizeof(temp_str));
for (uint8_t i = 0; i < 8; i++)
{
if (state->dstar_dst[i] == 0x20) //spaces to underscore
temp_str[i] = 0x5F;
else if (state->dstar_dst[i] > 0x20 && state->dstar_dst[i] < 0x7F) //copy normal ascii range characters
temp_str[i] = state->dstar_dst[i];
else if (state->dstar_dst[i] == 0) break; //hit the terminator, so stop
else temp_str[i] = 0x5F; //unknown to underscore
}
sprintf (tgt_str, "%s", temp_str);
}
if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR
{
source_id = 0;
for (uint8_t i = 0; i < 20; i++)
source_id += state->dpmr_caller_id[i]; //convert to sum value to make a distinct enough src value
//need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history
if (strncmp(state->dpmr_caller_id, " ", 6) == 0)
source_id = 0;
sprintf (sysid_string, "DPMR_CC_%d", state->dpmr_color_code);
sprintf (src_str, "%s", state->dpmr_caller_id);
sprintf (tgt_str, "%s", state->dpmr_target_id);
}
if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls
{
source_id = 0;
if (opts->p25_is_tuned == 1)
{
source_id = state->lastsrc;
channel = state->edacs_tuned_lcn;
}
sys_id1 = state->edacs_site_id;
sys_id2 = state->edacs_area_code;
sys_id3 = state->edacs_sys_id;
svc_opts = state->edacs_vc_call_type;
char sup_str[200]; memset (sup_str, 0, sizeof(sup_str));
sprintf (sup_str, "%s", "_");
if (svc_opts & 0x02)
strcat (sup_str, "Digital_");
else strcat (sup_str, "Analog_");
if (svc_opts & 0x04)
strcat (sup_str, "Emergency_");
if (svc_opts & 0x08)
strcat (sup_str, "Group_");
if (svc_opts & 0x10)
strcat (sup_str, "I_");
if (svc_opts & 0x20)
strcat (sup_str, "ALL_");
if (svc_opts & 0x40)
strcat (sup_str, "INTER_");
if (svc_opts & 0x80)
strcat (sup_str, "TEST_");
if (svc_opts & 0x100)
strcat (sup_str, "AGENCY_");
if (svc_opts & 0x200)
strcat (sup_str, "FLEET_");
if (svc_opts & 0x01)
strcat (sup_str, "Voice_");
strcat (sup_str, "Call");
sprintf (sysid_string, "EDACS_SITE_%03d", sys_id1);
strcat (sysid_string, sup_str);
if (state->ea_mode == 0)
{
int afs = state->lasttg;
sprintf(src_str, "%s", ""); sprintf(tgt_str, "%s", "");
int a = (afs >> state->edacs_a_shift) & state->edacs_a_mask;
int f = (afs >> state->edacs_f_shift) & state->edacs_f_mask;
int s = afs & state->edacs_s_mask;
sprintf (tgt_str, "%03d_AFS_%02d_%02d%01d", afs, a, f, s);
if (state->lastsrc != 0x800 && state->lastsrc != 0)
sprintf (src_str, "LID_%d", state->lastsrc);
else sprintf (src_str, "LID_UNK");
}
}
}
//if we have a group_array import, search and load it here
//will search and load both target values, and src values if available
uint8_t t_name_loaded = 0;
uint8_t s_name_loaded = 0;
if (target_id != 0)
{
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == target_id)
{
sprintf (t_name, "%s", state->group_array[i].groupName);
sprintf (t_mode, "%s", state->group_array[i].groupMode);
t_name_loaded = 1;
break;
}
}
}
if (source_id != 0) //&& state->gi[slot] == 1
{
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == source_id)
{
sprintf (s_name, "%s", state->group_array[i].groupName);
sprintf (s_mode, "%s", state->group_array[i].groupMode);
s_name_loaded = 1;
break;
}
}
}
//system type string (P25, DMR, etc)
const char * sys_string = "Digital";
if (state->lastsynctype != -1)
sys_string = SyncTypes[state->lastsynctype];
//date and time strings
char * timestr = getTimeN(time(NULL));
char * datestr = getDateN(time(NULL));
if (source_id != 0)
{
event_struct->Event_History_Items[0].write = 0;
state->event_history_s[slot].Event_History_Items[0].color_pair = color_pair;
if (state->lastsynctype != -1)
event_struct->Event_History_Items[0].systype = state->lastsynctype;
else event_struct->Event_History_Items[0].systype = 39; //generic digital call
event_struct->Event_History_Items[0].subtype = subtype; //voice
event_struct->Event_History_Items[0].gi = state->gi[slot]; //need this add this to link control messages
event_struct->Event_History_Items[0].sys_id1 = sys_id1;
event_struct->Event_History_Items[0].sys_id2 = sys_id2;
event_struct->Event_History_Items[0].sys_id3 = sys_id3;
event_struct->Event_History_Items[0].sys_id4 = sys_id4;
event_struct->Event_History_Items[0].sys_id5 = sys_id5;
event_struct->Event_History_Items[0].enc = enc;
event_struct->Event_History_Items[0].enc_alg = alg_id;
event_struct->Event_History_Items[0].enc_key = key_id;
event_struct->Event_History_Items[0].mi = mi;
event_struct->Event_History_Items[0].svc = svc_opts;
event_struct->Event_History_Items[0].source_id = source_id;
event_struct->Event_History_Items[0].target_id = target_id;
event_struct->Event_History_Items[0].channel = channel; //need to add this to trunking messages, if tuned from call grant
if (opts->playfiles == 0) //if playing back .mbe files with a time in it, don't set this
event_struct->Event_History_Items[0].event_time = time(NULL);
sprintf (event_struct->Event_History_Items[0].sysid_string, "%s", sysid_string);
sprintf (event_struct->Event_History_Items[0].src_str, "%s", src_str);
sprintf (event_struct->Event_History_Items[0].tgt_str, "%s", tgt_str);
sprintf (event_struct->Event_History_Items[0].t_name, "%s", t_name);
sprintf (event_struct->Event_History_Items[0].s_name, "%s", s_name);
sprintf (event_struct->Event_History_Items[0].t_mode, "%s", t_mode);
sprintf (event_struct->Event_History_Items[0].s_mode, "%s", s_mode);
}
//Craft an event string for ncurses event history, and a more complex string for logging
char event_string[2000]; memset(event_string, 0, sizeof(event_string));
//WIP: Seperate Voice Call Event Strings when SRC/TGT values are numerical,
//and a seperate one for when they are string values (M17, YSF, DSTAR, and dPMR, or use special formatting)
if (state->lastsynctype == 30 || state->lastsynctype == 31) //YSF Fusion //TODO: Data calls dumping a lot of events as VOICE
{
//TODO: See if we can add some decoded data as well in the future to an event string
sprintf (event_string, "%s %s %s TGT: %s SRC: %s ", datestr, timestr, sys_string, state->ysf_tgt, state->ysf_src);
}
else if (state->lastsynctype == 16 || state->lastsynctype == 17) //M17
{
//TODO: See if we can add some decoded data as well in the future to an event string
if (state->m17_dst == 0xFFFFFFFFFFFF)
sprintf (event_string, "%s %s %s TGT: %s SRC: %s CAN: %02d;", datestr, timestr, sys_string, "BROADCAST", state->m17_src_str, state->m17_can);
else
sprintf (event_string, "%s %s %s TGT: %s SRC: %s CAN: %02d;", datestr, timestr, sys_string, state->m17_dst_str, state->m17_src_str, state->m17_can);
}
else if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR
{
//TODO: See if we can add some decoded data as well in the future to an event string
sprintf (event_string, "%s %s %s TGT: %s SRC: %s ", datestr, timestr, sys_string, state->dstar_dst, state->dstar_src);
}
else if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR
{
//TODO: See if we can add some decoded data as well in the future to an event string
sprintf (event_string, "%s %s %s CC: %02d; TGT: %s; SRC: %s; ", datestr, timestr, sys_string, state->dpmr_color_code, state->dpmr_target_id, state->dpmr_caller_id);
if (state->dPMRVoiceFS2Frame.Version[0] == 3)
strcat (event_string, "Scrambler Enc; ");
}
else if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls
{
svc_opts = state->edacs_vc_call_type;
char sup_str[200]; memset (sup_str, 0, sizeof(sup_str));
sprintf (sup_str, "%s", "");
if (svc_opts & 0x02)
strcat (sup_str, "Digital ");
else strcat (sup_str, "Analog ");
if (svc_opts & 0x04)
strcat (sup_str, "Emergency ");
if (svc_opts & 0x08)
strcat (sup_str, "Group ");
if (svc_opts & 0x10)
strcat (sup_str, "I ");
if (svc_opts & 0x20)
strcat (sup_str, "ALL ");
if (svc_opts & 0x40)
strcat (sup_str, "INTER ");
if (svc_opts & 0x80)
strcat (sup_str, "TEST ");
if (svc_opts & 0x100)
strcat (sup_str, "AGENCY ");
if (svc_opts & 0x200)
strcat (sup_str, "FLEET ");
if (svc_opts & 0x01)
strcat (sup_str, "Voice ");
strcat (sup_str, "Call");
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);
}
else
{
int afs = state->lasttg;
int a = (afs >> state->edacs_a_shift) & state->edacs_a_mask;
int f = (afs >> state->edacs_f_shift) & state->edacs_f_mask;
int s = afs & state->edacs_s_mask;
char afs_str[8];
getAfsString(state, afs_str, a, f, s);
char lid_str[20]; memset(lid_str, 0, sizeof(lid_str));
sprintf(lid_str, "%s", "");
if (state->lastsrc != 0 && state->lastsrc != 0x800)
sprintf (lid_str, "LID: %05d;", state->lastsrc);
else sprintf (lid_str, "LID: __UNK;");
sprintf (event_string, "%s %s %s AFS: %s (%04d); %s LCN: %02d; Site: %d; %s; ", datestr, timestr, sys_string, afs_str, afs, lid_str, channel, sys_id1, sup_str);
}
}
else if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13 ||
state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 ) //DMR
{
if (sys_id1)
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; CC: %02d; SYS: %X; ", datestr, timestr, sys_string, target_id, source_id, sys_id2, sys_id1);
else
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; CC: %02d; ", datestr, timestr, sys_string, target_id, source_id, sys_id2);
if (enc)
strcat(event_string, "ENC; ");
if (alg_id != 0)
{
char ess_str[30];
sprintf (ess_str, "ALG: %02X; KID: %02X; ", alg_id, key_id);
strcat(event_string, ess_str);
}
//monitor for misc link control that may set a SO without having SO inside of it,
//those could cause misc issues here, will need to observe and make adjustments
if (svc_opts & 0x80)
strcat (event_string, "Emergency; ");
if (svc_opts & 0x08)
strcat (event_string, "Broadcast; ");
if (svc_opts & 0x04)
strcat (event_string, "OVCM; ");
if (state->gi[slot] == 0)
strcat (event_string, "Group; ");
else if (state->gi[slot] == 1)
strcat (event_string, "Private; ");
if (mfid == 0x10)
{
if (svc_opts & 0x20)
strcat (event_string, "TXI; ");
else if (svc_opts & 0x10)
strcat (event_string, "TXI; "); //this is the svc opt bit that tells you when the next VC6 will be pre-empted, but not helpful here
if (svc_opts & 0x03)
strcat (event_string, "PRIORITY; "); //need to break this apart into each one, but need to double check the decoded value is accurate
}
}
else if (state->lastsynctype == 0 || state->lastsynctype == 1 || state->lastsynctype == 35 || state->lastsynctype == 36)
{
if (sys_id1)
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; NAC: %03X; NET_STS: %05X:%03X:%d.%d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3, sys_id1, sys_id2, sys_id4, sys_id5);
else
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; NAC: %03X; ", datestr, timestr, sys_string, target_id, source_id, sys_id3);
if (alg_id != 0 && alg_id != 0x80)
{
char ess_str[30];
sprintf (ess_str, "ENC; ALG: %02X; KID: %04X; ", alg_id, key_id);
strcat(event_string, ess_str);
}
if (svc_opts & 0x80)
strcat (event_string, "Emergency; ");
if (state->gi[slot] == 0)
strcat (event_string, "Group; ");
else if (state->gi[slot] == 1)
strcat (event_string, "Private; ");
}
else if (state->lastsynctype == 28 || state->lastsynctype == 29)
{
if (sys_id1)
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; RAN: %02d; SYS: %d.%d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3, sys_id2, sys_id1);
else
sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; RAN: %02d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3);
if (enc)
strcat(event_string, "ENC; ");
if (alg_id != 0)
{
char ess_str[30];
sprintf (ess_str, "ALG: %d; KID: %02X; ", alg_id, key_id);
strcat(event_string, ess_str);
}
if (state->gi[slot] == 0)
strcat (event_string, "Group; ");
else if (state->gi[slot] == 1)
strcat (event_string, "Private; ");
}
if (t_name_loaded)
{
char group[420];
sprintf (group, "TName: %s; Mode: %s; ", t_name, t_mode);
strcat (event_string, group);
}
if (s_name_loaded)
{
char private[420];
sprintf (private, "SName: %s; Mode: %s; ", s_name, s_mode);
strcat (event_string, private);
}
sprintf (event_struct->Event_History_Items[0].event_string, "%s", event_string);
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
}
void watchdog_event_datacall (dsd_opts * opts, dsd_state * state, uint32_t src, uint32_t dst, char * data_string, uint8_t slot)
{
UNUSED(opts);
state->event_history_s[slot].Event_History_Items[0].write = 0;
if (state->event_history_s[slot].Event_History_Items[0].color_pair == 4) //if not set previously by specific decoder //don't touch this one
state->event_history_s[slot].Event_History_Items[0].color_pair = 4; //default data color //you can change this one
state->event_history_s[slot].Event_History_Items[0].systype = state->lastsynctype;
state->event_history_s[slot].Event_History_Items[0].subtype = 6; //data
state->event_history_s[slot].Event_History_Items[0].gi = state->gi[slot];
state->event_history_s[slot].Event_History_Items[0].enc = 0;
state->event_history_s[slot].Event_History_Items[0].enc_alg = 0;
state->event_history_s[slot].Event_History_Items[0].enc_key = 0;
state->event_history_s[slot].Event_History_Items[0].mi = 0;
state->event_history_s[slot].Event_History_Items[0].svc = 0;
state->event_history_s[slot].Event_History_Items[0].source_id = src;
state->event_history_s[slot].Event_History_Items[0].target_id = dst;
state->event_history_s[slot].Event_History_Items[0].channel = 0;
state->event_history_s[slot].Event_History_Items[0].event_time = time(NULL);
//date and time strings //getTimeN(time(NULL)); //getDateN(time(NULL));
char * timestr = getTimeN(time(NULL));
char * datestr = getDateN(time(NULL));
char event_string[2000]; memset (event_string, 0, sizeof(event_string));
sprintf (event_string, "%s %s ", datestr, timestr);
strcat (event_string, data_string); //may need to check for potential overflow of this
sprintf (state->event_history_s[slot].Event_History_Items[0].event_string, "%s", event_string); //could change this to a strncpy to prevent potential overflow
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
//call alert on data calls
if (opts->call_alert)
beeper (opts, state, slot, 80, 20, 3);
}

File diff suppressed because it is too large Load Diff

View File

@@ -20,36 +20,27 @@
#define NULL 0 #define NULL 0
#endif #endif
#include "p25p1_check_nid.h"
void void
printFrameInfo (dsd_opts * opts, dsd_state * state) printFrameInfo (dsd_opts * opts, dsd_state * state)
{ {
int level; UNUSED(opts);
level = (int) state->max / 164;
if (opts->verbose > 0)
{
// fprintf (stderr,"inlvl: %2i%% ", level);
UNUSED(level);
}
if (state->nac != 0)
{
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr,"nac: [%4X] ", state->nac); if (state->p2_wacn != 0)
fprintf (stderr, "%s", KNRM); fprintf (stderr, "WACN: %05llX; ", state->p2_wacn);
} if (state->p2_sysid != 0)
fprintf (stderr, "SYS: %03llX; ", state->p2_sysid);
if (state->p2_cc != 0)
fprintf (stderr, "NAC/CC: %03llX; ", state->p2_cc);
else fprintf (stderr, "NAC: %03X; ", state->nac);
if (opts->verbose > 1) if (state->p2_rfssid != 0)
{ fprintf (stderr, "RFSS: %03lld; ", state->p2_rfssid);
fprintf (stderr, "%s", KGRN); if (state->p2_siteid != 0)
fprintf (stderr,"src: [%8i] ", state->lastsrc); fprintf (stderr, "Site: %03lld; ", state->p2_siteid);
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "%s", KGRN);
fprintf (stderr,"tg: [%5i] ", state->lasttg);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
void void
@@ -269,21 +260,10 @@ processFrame (dsd_opts * opts, dsd_state * state)
processYSF(opts, state); processYSF(opts, state);
return; return;
} }
//M17 //M17 STR
else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) || else if (state->synctype == 16)
(state->synctype == 76) || (state->synctype == 77) || (state->synctype == 86) || (state->synctype == 87) ||
(state->synctype == 99) || (state->synctype == 98) )
{ {
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); 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);
return; return;
} }
//P25 P2 //P25 P2

View File

@@ -162,7 +162,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
char synctest20[21]; //YSF char synctest20[21]; //YSF
char synctest48[49]; //EDACS char synctest48[49]; //EDACS
char synctest8[9]; //M17 char synctest8[9]; //M17
char synctest16[17]; //M17 Preamble
char modulation[8]; char modulation[8];
char *synctest_p; char *synctest_p;
char synctest_buf[10240]; //what actually is assigned to this, can't find its use anywhere? char synctest_buf[10240]; //what actually is assigned to this, can't find its use anywhere?
@@ -208,9 +207,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
t = 0; t = 0;
synctest10[10] = 0; synctest10[10] = 0;
synctest[24] = 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; synctest12[12] = 0;
synctest16[16] = 0; //M17, wasn't initialized or terminated (source of much pain and frustration in Cygwin)
synctest48[48] = 0; synctest48[48] = 0;
synctest32[32] = 0; synctest32[32] = 0;
synctest20[20] = 0; synctest20[20] = 0;
@@ -224,11 +222,17 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
lastt = 0; lastt = 0;
state->numflips = 0; state->numflips = 0;
//move ncursesPrinter outside of the sync loop, causes weird lag inside the loop //run here as well
if (opts->use_ncurses_terminal == 1) if (opts->use_ncurses_terminal == 1)
{
ncursesPrinter(opts, state); ncursesPrinter(opts, state);
}
//slot 1
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
//slot 2 for TDMA systems
watchdog_event_history(opts, state, 1);
watchdog_event_current(opts, state, 1);
if ((opts->symboltiming == 1) && (state->carrier == 1)) if ((opts->symboltiming == 1) && (state->carrier == 1))
{ {
@@ -240,6 +244,11 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
t++; t++;
//run ncurses printer more frequently when no sync to speed up responsiveness of it during no sync period
//NOTE: Need to monitor and test this, if responsiveness issues arise, then disable this
if ( opts->use_ncurses_terminal == 1 && ((t % 300) == 0)) //t maxes out at 1800 (6 times each getFrameSync)
ncursesPrinter(opts, state);
symbol = getSymbol (opts, state, 0); symbol = getSymbol (opts, state, 0);
lbuf[lidx] = symbol; lbuf[lidx] = symbol;
@@ -432,7 +441,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
sprintf (state->ftype, "P25 Phase 1"); sprintf (state->ftype, "P25 Phase 1");
if (opts->errorbars == 1) if (opts->errorbars == 1)
{ {
printFrameSync (opts, state, "+P25p1 ", synctest_pos + 1, modulation); printFrameSync (opts, state, "+P25p1", synctest_pos + 1, modulation);
} }
state->lastsynctype = 0; state->lastsynctype = 0;
state->last_cc_sync_time = time(NULL); state->last_cc_sync_time = time(NULL);
@@ -555,84 +564,18 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
} }
//end YSF sync //end YSF sync
//M17 Sync -- Just STR and LSF for now //M17 Sync -- Just STR
strncpy(synctest16, (synctest_p - 15), 16);
strncpy(synctest8, (synctest_p - 7), 8); strncpy(synctest8, (synctest_p - 7), 8);
if(opts->frame_m17 == 1) if(opts->frame_m17 == 1)
{ {
//preambles will skip dibits in an attempt to prime the if (strcmp(synctest8, M17_STR) == 0)
//demodulator but not attempt any decoding
if (strcmp(synctest8, M17_PRE) == 0)
{
if (opts->inverted_m17 == 0)
{
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); printFrameSync (opts, state, "+M17 STR", synctest_pos + 1, modulation);
state->carrier = 1; state->carrier = 1;
state->offset = synctest_pos; state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2; state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2; state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 16 || state->lastsynctype == 8) if (state->lastsynctype == 16)
{ {
state->lastsynctype = 16; state->lastsynctype = 16;
return (16); return (16);
@@ -640,55 +583,6 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
state->lastsynctype = 16; state->lastsynctype = 16;
fprintf (stderr, "\n"); 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");
}
}
} }
//end M17 //end M17
@@ -708,13 +602,11 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
state->lastsynctype = 35; //35 state->lastsynctype = 35; //35
if (opts->errorbars == 1) if (opts->errorbars == 1)
{ {
printFrameSync (opts, state, "+P25p2 SISCH", synctest_pos + 1, modulation); printFrameSync (opts, state, "+P25p2", synctest_pos + 1, modulation);
} }
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0) if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0)
{ {
//fprintf (stderr, "%s", KCYN); printFrameInfo(opts, state);
fprintf (stderr, " WACN [%05llX] SYS [%03llX] NAC [%03llX] ", state->p2_wacn, state->p2_sysid, state->p2_cc);
//fprintf (stderr, "%s", KNRM);
} }
else else
{ {
@@ -741,13 +633,11 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
opts->inverted_p2 = 1; opts->inverted_p2 = 1;
if (opts->errorbars == 1) if (opts->errorbars == 1)
{ {
printFrameSync (opts, state, "-P25p2 SISCH", synctest_pos + 1, modulation); printFrameSync (opts, state, "-P25p2", synctest_pos + 1, modulation);
} }
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0) if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0)
{ {
//fprintf (stderr, "%s", KCYN); printFrameInfo(opts, state);
fprintf (stderr, " WACN [%05llX] SYS [%03llX] NAC [%03llX] ", state->p2_wacn, state->p2_sysid, state->p2_cc);
//fprintf (stderr, "%s", KNRM);
} }
else else
{ {

View File

@@ -15,6 +15,12 @@ void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input)
int slot = state->currentslot; int slot = state->currentslot;
//May need to set this in the UDT header, or pass it into this function to be sure
// uint32_t src = 0;
// if (slot == 0)
// src = state->lastsrc;
// else src = state->lastsrcR;
//NOTE: This format is pretty much the same as DMR EMB GPS, but has a few extra elements, //NOTE: This format is pretty much the same as DMR EMB GPS, but has a few extra elements,
//so I got lazy and just lifted most of the code from there, also assuming same lat/lon calcs //so I got lazy and just lifted most of the code from there, also assuming same lat/lon calcs
//since those have been tested to work in DMR EMB GPS and units are same in LIP //since those have been tested to work in DMR EMB GPS and units are same in LIP
@@ -89,7 +95,7 @@ void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input)
int dt = (int)dir; int dt = (int)dir;
//sanity check //sanity check
if (abs (latitude) < 90 && abs(longitude) < 180) if (fabs (latitude) < 90 && fabs(longitude) < 180)
{ {
fprintf (stderr, "Src(Hash); %03d; Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf); Spd: %d km/h; Dir: %d%s",add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude, vt, dt, deg_glyph); fprintf (stderr, "Src(Hash); %03d; Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf); Spd: %d km/h; Dir: %d%s",add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude, vt, dt, deg_glyph);
@@ -115,6 +121,9 @@ void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input)
} }
else sprintf (state->dmr_embedded_gps[slot], "%03d; LIP: %.5lf%s%s %.5lf%s%s Unknown Pos Err; Spd: %d km/h; Dir %d%s", add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, vt, dt, deg_glyph); else sprintf (state->dmr_embedded_gps[slot], "%03d; LIP: %.5lf%s%s %.5lf%s%s Unknown Pos Err; Spd: %d km/h; Dir %d%s", add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, vt, dt, deg_glyph);
//save to event history string
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 //save to LRRP report for mapping/logging
FILE * pFile; //file pointer FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1) if (opts->lrrp_file_output == 1)
@@ -184,7 +193,7 @@ void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint
uint8_t nmea_utc_hh = (uint8_t)ConvertBitIntoBytes(&input[66], 5); uint8_t nmea_utc_hh = (uint8_t)ConvertBitIntoBytes(&input[66], 5);
uint8_t nmea_utc_mm = (uint8_t)ConvertBitIntoBytes(&input[71], 6); uint8_t nmea_utc_mm = (uint8_t)ConvertBitIntoBytes(&input[71], 6);
//seconds and the addition of COG is the difference between short and long formats //seconds and the addition of COG is the difference between short and long formats
uint8_t nmea_utc_ss3 = (uint8_t)ConvertBitIntoBytes(&input[77], 3); //seconds in 10s uint8_t nmea_utc_ss3 = (uint8_t)ConvertBitIntoBytes(&input[77], 3) * 10; //seconds in 10s
uint8_t nmea_utc_ss6 = (uint8_t)ConvertBitIntoBytes(&input[77], 6); //seconds in 1s uint8_t nmea_utc_ss6 = (uint8_t)ConvertBitIntoBytes(&input[77], 6); //seconds in 1s
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[103], 9); //course over ground in degrees uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[103], 9); //course over ground in degrees
@@ -230,6 +239,9 @@ void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint
//save to ncurses string //save to ncurses string
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph); sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
//save to event history string
sprintf (state->event_history_s[slot].Event_History_Items[0].gps_s, "(%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
//save to LRRP report for mapping/logging //save to LRRP report for mapping/logging
FILE * pFile; //file pointer FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1) if (opts->lrrp_file_output == 1)
@@ -333,16 +345,24 @@ void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
fprintf (stderr, "\n"); //is this from LCW, or MAC?
uint16_t header = (uint16_t)ConvertBitIntoBytes(&input[0], 16);
fprintf (stderr, "\n");
if (header == 0x2AA4)
fprintf (stderr, " SRC: %08d;", src);
else
fprintf (stderr, " VCH: %d - SRC: %08d;", slot, src); fprintf (stderr, " VCH: %d - SRC: %08d;", slot, src);
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph); 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) //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 (nmea_speed > 126) 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, " SPD > 126 knots or %f MPH;", fmph); //using MPH here, its Harris, they're in the U.S.
else else
fprintf (stderr, " SPD: %d knots; %f MPH;", nmea_speed, fmph); fprintf (stderr, " SPD: %d knots; %f MPH;", nmea_speed, fmph);
}
//Course Over Ground (COG) //Course Over Ground (COG)
fprintf (stderr, " COG: %03d%s;", nmea_cog, deg_glyph); //%360 fprintf (stderr, " COG: %03d%s;", nmea_cog, deg_glyph); //%360
@@ -361,7 +381,11 @@ void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t
else fprintf (stderr, " Last Fix;"); else fprintf (stderr, " Last Fix;");
//save to ncurses string //save to ncurses string
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph); sprintf (state->dmr_embedded_gps[slot], "(%f%s, %f%s)", latitude, deg_glyph, longitude, 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 //save to LRRP report for mapping/logging
FILE * pFile; //file pointer FILE * pFile; //file pointer
@@ -601,7 +625,7 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
longitude = ((double)lon * lon_unit); longitude = ((double)lon * lon_unit);
//sanity check //sanity check
if (abs (latitude) < 90 && abs(longitude) < 180) if (fabs(latitude) < 90 && fabs(longitude) < 180)
{ {
fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf)", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude); fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf)", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude);
@@ -617,6 +641,14 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
} }
else sprintf (state->dmr_embedded_gps[slot], "GPS: %.5lf%s%s %.5lf%s%s Unknown Pos Err", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr); else sprintf (state->dmr_embedded_gps[slot], "GPS: %.5lf%s%s %.5lf%s%s Unknown Pos Err", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr);
uint32_t src = 0;
if (slot == 0) src = state->lasttg;
if (slot == 1) src = state->lasttgR;
//save to event history string
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 //save to LRRP report for mapping/logging
FILE * pFile; //file pointer FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1) if (opts->lrrp_file_output == 1)
@@ -625,10 +657,6 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
char * datestr = getDate(); char * datestr = getDate();
char * timestr = getTime(); char * timestr = getTime();
int src = 0;
if (slot == 0) src = state->lasttg;
if (slot == 1) src = state->lasttgR;
//open file by name that is supplied in the ncurses terminal, or cli //open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->lrrp_out_file, "a"); pFile = fopen (opts->lrrp_out_file, "a");
fprintf (pFile, "%s\t", datestr ); fprintf (pFile, "%s\t", datestr );
@@ -648,8 +676,8 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
//This Function has been redone and variables fixed to mirror SDRTrunk //This Function needs testing, is tested working for NW and NE lat and
//my earlier assumption that this was the same format as DMR Embedded GPS was incorrect //long coordinates, but not for SE and SW coordinates
void apx_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[])
{ {
@@ -668,18 +696,16 @@ void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23); uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23);
uint32_t lat_sign = lc_bits[24]; uint32_t lat_sign = lc_bits[24];
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[25], 23); uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[25], 23);
//todo: acquire more samples for additional validation
double lat_unit = 90.0f / 0x7FFFFF; double lat_unit = 90.0f / 0x7FFFFF;
double lon_unit = 180.0f / 0x7FFFFF; double lon_unit = 180.0f / 0x7FFFFF;
double lon_sf = 1.0f; //float value we can multiple longitude with
double lat_sf = 1.0f; //float value we can multiple latitude with
char latstr[3]; char latstr[3];
char lonstr[3]; char lonstr[3];
char valid[9]; char valid[12];
sprintf (latstr, "%s", "N"); sprintf (latstr, "%s", "N");
sprintf (lonstr, "%s", "E"); sprintf (lonstr, "%s", "E");
sprintf (valid, "%s", "Current"); sprintf (valid, "%s", "Current Fix");
double latitude = 0; double latitude = 0;
double longitude = 0; double longitude = 0;
@@ -687,32 +713,33 @@ void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
if (pf) fprintf (stderr, " Protected"); if (pf) fprintf (stderr, " Protected");
else else
{ {
latitude = ((double)lat * lat_unit);
if (lat_sign) if (lat_sign)
{ {
latitude -= 90.0f;
sprintf (latstr, "%s", "S"); sprintf (latstr, "%s", "S");
lat_sf = -1.0f;
} }
latitude = ((double)lat * lat_unit);
longitude = ((double)lon * lon_unit);
if (lon_sign) if (lon_sign)
{ {
longitude -= 180.0f;
sprintf (lonstr, "%s", "W"); sprintf (lonstr, "%s", "W");
lon_sf = -1.0f;
} }
longitude = ((double)lon * lon_unit);
//sanity check //sanity check
if (abs (latitude) < 90 && abs(longitude) < 180) if (fabs ((float)latitude) < 90 && fabs((float)longitude) < 180)
{ {
fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf) ", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, latitude * lat_sf, longitude * lon_sf); fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf) ", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, latitude, longitude);
if (expired) if (expired)
{ {
fprintf (stderr, "Expired; "); fprintf (stderr, "Last Fix; ");
sprintf (valid, "%s", "Expired"); sprintf (valid, "%s", "Last Fix");
} }
else if (!expired) else if (!expired)
fprintf (stderr, "Current; "); fprintf (stderr, "Current Fix; ");
if (res_a) if (res_a)
fprintf (stderr, "RES_A: %d; ", res_a); fprintf (stderr, "RES_A: %d; ", res_a);
@@ -720,17 +747,21 @@ void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
if (res_b) if (res_b)
fprintf (stderr, "RES_B: %02X; ", res_b); fprintf (stderr, "RES_B: %02X; ", res_b);
uint32_t src = 0;
if (slot == 0) src = state->lastsrc;
if (slot == 1) src = state->lastsrcR;
//save to array for ncurses //save to array for ncurses
sprintf (state->dmr_embedded_gps[slot], "GPS: %lf%s%s %lf%s%s %s", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, valid); sprintf (state->dmr_embedded_gps[slot], "GPS: %lf%s%s %lf%s%s (%lf, %lf) %s", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, latitude, longitude, valid);
//save to event history string
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 //save to LRRP report for mapping/logging
FILE * pFile; //file pointer FILE * pFile; //file pointer
if (opts->lrrp_file_output == 1) if (opts->lrrp_file_output == 1)
{ {
int src = 0;
if (slot == 0) src = state->lastsrc;
if (slot == 1) src = state->lastsrcR;
char * datestr = getDate(); char * datestr = getDate();
char * timestr = getTime(); char * timestr = getTime();
@@ -764,3 +795,43 @@ void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
void decode_cellocator(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
{
//TODO: This
UNUSED(opts);
UNUSED(state);
UNUSED(input);
UNUSED(len);
//UTF8 Text: MCGP (0x4D434750) followed by data values
//(0417D1050000F45FD1DD00010000000097BDD56C81000009AAAABF12864C)
utf8_to_text(state, 0, 4, input);
fprintf (stderr, " Cellocator:");
uint8_t type = input[4];
if (type == 1) fprintf (stderr, " Platform Manifest Data;");
else if (type == 2) fprintf (stderr, " CAN Data;");
else if (type == 3) fprintf (stderr, " CAN Trigger Data;");
else if (type == 4) fprintf (stderr, " Time and Location Data;");
else if (type == 5) fprintf (stderr, " Accelerometer Data;");
else if (type == 6) fprintf (stderr, " PSP Alarm System Data;");
else if (type == 7) fprintf (stderr, " Usage Counter Data;");
else if (type == 8) fprintf (stderr, " Command Authentication Table Data;");
else if (type == 9) fprintf (stderr, " GSM Neighbor List Data;");
else if (type == 10) fprintf (stderr, " Maintenance Server Platform Manifest Data;");
else fprintf (stderr, " Unknown Data;");
//Data afterwards appears to be an arbitrary len so has variable reporting data
//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
UNUSED(opts);
UNUSED(state);
UNUSED(input);
UNUSED(len);
}

View File

@@ -214,7 +214,7 @@ int csvKeyImportDec(dsd_opts * opts, dsd_state * state) //multi-key support
} }
//Hex Variant of Key Import //Hex Variant of Key Import
int csvKeyImportHex(dsd_opts * opts, dsd_state * state) //key import for rc4 keys int csvKeyImportHex(dsd_opts * opts, dsd_state * state) //key import for hex keys
{ {
char filename[1024] = "filename.csv"; char filename[1024] = "filename.csv";
sprintf (filename, "%s", opts->key_in_file); sprintf (filename, "%s", opts->key_in_file);
@@ -247,10 +247,31 @@ int csvKeyImportHex(dsd_opts * opts, dsd_state * state) //key import for rc4 key
sscanf (field, "%llX", &state->rkey_array[keynumber]); sscanf (field, "%llX", &state->rkey_array[keynumber]);
} }
//this could also theoretically nuke other keys that are at the same offset
if (field_count == 2)
{
sscanf (field, "%llX", &state->rkey_array[keynumber+0x101]);
}
if (field_count == 3)
{
sscanf (field, "%llX", &state->rkey_array[keynumber+0x201]);
}
if (field_count == 4)
{
sscanf (field, "%llX", &state->rkey_array[keynumber+0x301]);
}
field = strtok(NULL, ","); field = strtok(NULL, ",");
field_count++; field_count++;
} }
fprintf (stderr, "Key [%04llX] [%016llX]", keynumber, state->rkey_array[keynumber]); fprintf (stderr, "Key [%04llX] [%016llX]", keynumber, state->rkey_array[keynumber]);
//if longer key is loaded (or clash with the 0x101, 0x201, 0x301 offset, then print the full key listing)
if ( (state->rkey_array[keynumber+0x101] != 0) || (state->rkey_array[keynumber+0x201] != 0) || (state->rkey_array[keynumber+0x301] != 0) )
fprintf (stderr, " [%016llX] [%016llX] [%016llX]", state->rkey_array[keynumber+0x101], state->rkey_array[keynumber+0x201], state->rkey_array[keynumber+0x301]);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -123,9 +123,11 @@ void soft_mbe (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char amb
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 2) if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 2)
playSynthesizedVoiceFS(opts, state); playSynthesizedVoiceFS(opts, state);
if (opts->wav_out_f != NULL) if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1)
writeSynthesizedVoice (opts, state); writeSynthesizedVoice (opts, state);
//single wav file handled by playSynthesizedVoiceMS
if (opts->mbe_out_f != NULL) if (opts->mbe_out_f != NULL)
saveAmbe2450Data (opts, state, ambe_d); saveAmbe2450Data (opts, state, ambe_d);
} }

View File

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

File diff suppressed because it is too large Load Diff

925
src/dsd_ncurses_handler.c Normal file
View File

@@ -0,0 +1,925 @@
/*-------------------------------------------------------------------------------
* dsd_ncurses_handler.c
* DSD-FME ncurses terminal user input handler
*
* LWVMOBILE
* 2025-05 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
{
struct stat st_wav = {0};
// //debug char value
// if (c != -1)
// fprintf (stderr, "\n User Input: %i / %c ;;", c, c);
//TEST: Handle Escape Characters
//if issues arise, just go back to the way it was before
//this is primarly a fix for scroll wheel activatign the menu
//and Windows Powershell right-click doing a copy and paste
//and sendign tons of garbage getch chars here and changing things
if (c == '\033')
{
//TODO: Find way to getch all the chars immediately after, or just
//run getch in a loop in case of somebody copying and pasting a ton of things accidentally
for (int i = 0; i < 100000; i++) //wonder how slow this will be
getch();
c = -1;
return 1;
}
//keyboard shortcuts - codes same as ascii codes
if (c == 10) //Return / Enter key, open menu
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
{
if (state->tg_hold == 0)
state->tg_hold = state->lasttg;
else state->tg_hold = 0;
if ( (opts->frame_nxdn48 == 1 || opts->frame_nxdn96 == 1) && (state->tg_hold == 0) )
state->tg_hold = state->nxdn_last_tg;
else if (opts->frame_provoice == 1 && state->ea_mode == 0)
state->tg_hold = state->lastsrc;
}
if (c == 108) //'l' key, hold tg on slot 2 for trunking purposes, or toggle clear
{
if (state->tg_hold == 0)
state->tg_hold = state->lasttgR;
else state->tg_hold = 0;
}
//toggling when 48k/1 OSS still has some lag -- needed to clear out the buffer when switching
if (c == 49) // '1' key, toggle slot1 on
{
//switching, but want to control each seperately plz
if (opts->slot1_on == 1)
{
opts->slot1_on = 0; if (opts->slot_preference == 0) opts->slot_preference = 2;
// opts->slot_preference = 1; //slot 2
//clear any previously buffered audio
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
state->audio_out_buf_p = state->audio_out_buf + 100;
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
memset (state->audio_out_buf, 0, 100 * sizeof (short));
state->audio_out_idx2 = 0;
state->audio_out_idx = 0;
}
else if (opts->slot1_on == 0)
{
opts->slot1_on = 1;
if (opts->audio_out_type == 5) //OSS 48k/1
{
opts->slot_preference = 0; //slot 1
// opts->slot2_on = 0; //turn off slot 2
}
}
}
if (c == 50) // '2' key, toggle slot2 on
{
//switching, but want to control each seperately plz
if (opts->slot2_on == 1)
{
opts->slot2_on = 0;
opts->slot_preference = 0; //slot 1
//clear any previously buffered audio
state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100;
state->audio_out_buf_pR = state->audio_out_bufR + 100;
memset (state->audio_out_float_bufR, 0, 100 * sizeof (float));
memset (state->audio_out_bufR, 0, 100 * sizeof (short));
state->audio_out_idx2R = 0;
state->audio_out_idxR = 0;
}
else if (opts->slot2_on == 0)
{
opts->slot2_on = 1;
if (opts->audio_out_type == 5) //OSS 48k/1
{
opts->slot_preference = 1; //slot 2
// opts->slot1_on = 0; //turn off slot 1
}
}
}
// if (c == 51) //'3' key, toggle slot preference on 48k/1
// {
// if (opts->slot_preference == 1) opts->slot_preference = 0;
// else opts->slot_preference = 1;
// }
if (c == 51) //'3' key, cycle slot preference
{
if (opts->slot_preference == 0 || opts->slot_preference == 1) opts->slot_preference++;
else opts->slot_preference = 0;
}
if (c == 43) //+ key, increment audio_gain
{
if (opts->audio_gain < 50)
opts->audio_gain++;
state->aout_gain = opts->audio_gain;
state->aout_gainR = opts->audio_gain;
opts->audio_gainR = opts->audio_gain;
}
if (c == 45) //- key, decrement audio_gain
{
if (opts->audio_gain > 0)
opts->audio_gain--;
state->aout_gain = opts->audio_gain;
state->aout_gainR = opts->audio_gain;
//reset to default on 0 for auto
if (opts->audio_gain == 0)
{
state->aout_gain = 25;
state->aout_gainR = 25;
}
opts->audio_gainR = opts->audio_gain;
}
if (c == 42) // * key, increment audio_gainA
{
if (opts->audio_gainA < 100)
opts->audio_gainA++;
}
if (c == 47) // / key, decrement audio_gainA
{
if (opts->audio_gainA > 0)
opts->audio_gainA--;
}
if (c == 122) //'z' key, toggle payload to console
{
if (opts->payload == 1) opts->payload = 0;
else opts->payload = 1;
}
if (c == 99) //'c' key, toggle compact mode
{
if (opts->ncurses_compact == 1) opts->ncurses_compact = 0;
else opts->ncurses_compact = 1;
}
if (c == 116) //'t' key, toggle trunking
{
if (opts->p25_trunk == 1) opts->p25_trunk = 0;
else opts->p25_trunk = 1;
}
if (c == 121) //'y' key, toggle scanner mode
{
if (opts->scanner_mode == 1) opts->scanner_mode = 0;
else opts->scanner_mode = 1;
opts->p25_trunk = 0; //turn off trunking mode
}
if (c == 97) //'a' key, toggle call alert beep
{
if (opts->call_alert == 1) opts->call_alert = 0;
else opts->call_alert = 1;
}
if (c == 104) //'h' key, cycle history off, short, long
{
opts->ncurses_history++;
opts->ncurses_history %= 3;
}
if (c == 113) //'q' key, quit
{
exitflag = 1;
}
if (c == 52) // '4' key, toggle force privacy key over fid and svc (dmr)
{
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->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
{
//Set all signal for inversion or uninversion
if (opts->inverted_dmr == 0)
{
opts->inverted_dmr = 1;
opts->inverted_dpmr = 1;
opts->inverted_x2tdma = 1;
opts->inverted_ysf = 1;
}
else
{
opts->inverted_dmr = 0;
opts->inverted_dpmr = 0;
opts->inverted_x2tdma = 0;
opts->inverted_ysf = 0;
}
}
if (c == 109) //'m' key, toggle qpsk/c4fm - everything but phase 2
{
if (state->rf_mod == 0)
{
opts->mod_c4fm = 0;
opts->mod_qpsk = 1;
opts->mod_gfsk = 0;
state->rf_mod = 1;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
else
{
opts->mod_c4fm = 1;
opts->mod_qpsk = 0;
opts->mod_gfsk = 0;
state->rf_mod = 0;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
}
if (c == 77) //'M' key, toggle qpsk - phase 2 6000 sps
{
if (state->rf_mod == 0)
{
opts->mod_c4fm = 0;
opts->mod_qpsk = 1;
opts->mod_gfsk = 0;
state->rf_mod = 1;
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
else
{
opts->mod_c4fm = 1;
opts->mod_qpsk = 0;
opts->mod_gfsk = 0;
state->rf_mod = 0;
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
}
if (c == 82) //'R', save symbol capture bin with date/time string as name
{
//for filenames (no colons, etc)
char * timestr = getTime();
char * datestr = getDate();
sprintf (opts->symbol_out_file, "%s_%s_dibit_capture.bin", datestr, timestr);
openSymbolOutFile (opts, state);
//add a system event to echo in the event history
state->event_history_s[0].Event_History_Items[0].color_pair = 4;
char event_str[2000]; memset (event_str, 0, sizeof(event_str));
sprintf (event_str, "DSD-FME Dibit Capture File Started: %s;", opts->symbol_out_file);
watchdog_event_datacall (opts, state, 0xFFFFFF, 0xFFFFFF, event_str, 0);
state->lastsrc = 0; //this could wipe a call src if they hit 'R' while call in slot 1 in progress
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
//allocated memory pointer needs to be free'd
if (timestr != NULL)
{
free (timestr);
timestr = NULL;
}
if (datestr != NULL)
{
free (datestr);
datestr = NULL;
}
opts->symbol_out_file_creation_time = time(NULL);
opts->symbol_out_file_is_auto = 1;
}
if (c == 114) //'r' key, stop capturing symbol capture bin file
{
if (opts->symbol_out_f)
{
closeSymbolOutFile (opts, state);
sprintf (opts->audio_in_dev, "%s", opts->symbol_out_file);
//add a system event to echo in the event history
state->event_history_s[0].Event_History_Items[0].color_pair = 4;
char event_str[2000]; memset (event_str, 0, sizeof(event_str));
sprintf (event_str, "DSD-FME Dibit Capture File Closed: %s;", opts->symbol_out_file);
watchdog_event_datacall (opts, state, 0xFFFFFF, 0xFFFFFF, event_str, 0);
state->lastsrc = 0; //this could wipe a call src if they hit 'R' while call in slot 1 in progress
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
}
opts->symbol_out_file_is_auto = 0;
}
#ifdef __CYGWIN__
//do nothing
#else
if (c == 32) //'space bar' replay last bin file (rework to do wav files too?)
{
struct stat stat_buf;
if (stat(opts->audio_in_dev, &stat_buf) != 0)
{
fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev);
goto SKIPR;
}
if (S_ISREG(stat_buf.st_mode))
{
opts->symbolfile = fopen(opts->audio_in_dev, "r");
opts->audio_in_type = 4; //symbol capture bin files
}
SKIPR: ; //do nothing
}
#endif
if (c == 80) //'P' key - start per call wav files //TODO: Fix
{
char wav_file_directory[1024];
sprintf (wav_file_directory, "%s", opts->wav_out_dir);
wav_file_directory[1023] = '\0';
if (stat(wav_file_directory, &st_wav) == -1)
{
fprintf (stderr, "%s wav file directory does not exist\n", wav_file_directory);
fprintf (stderr, "Creating directory %s to save decoded wav files\n", wav_file_directory);
mkdir(wav_file_directory, 0700);
}
fprintf (stderr,"\n Per Call Wav File Enabled to Directory: %s;.\n", opts->wav_out_dir);
srand(time(NULL)); //seed random for filenames (so two filenames aren't the exact same datetime string on initailization)
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0);
opts->dmr_stereo_wav = 1;
}
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_file[0] = 0; //Bugfix for decoded wav file display after disabling
opts->wav_out_fileR[0] = 0;
opts->dmr_stereo_wav = 0;
}
//
#ifdef __CYGWIN__ //this might be okay on Aero as well, will need to look into and/or test
//
#else
if (c == 115) //'s' key, stop playing wav or symbol in files
{
if (opts->symbolfile != NULL)
{
if (opts->audio_in_type == 4)
{
fclose(opts->symbolfile);
}
}
if (opts->audio_in_type == 2) //wav input file
{
sf_close(opts->audio_in_file);
}
if (opts->audio_out_type == 0)
{
opts->audio_in_type = 0;
openPulseInput(opts);
}
else opts->audio_in_type = 5; //exitflag = 1;
}
#endif
//makes buzzing sound when locked out in new audio config and short, probably something to do with processaudio running or not running
if (state->lasttg != 0 && opts->frame_provoice != 1 && c == 33) //SHIFT+'1' key (exclamation point), lockout slot 1 or conventional tg from tuning/playback during session
{
state->group_array[state->group_tally].groupNumber = state->lasttg;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "B");
sprintf (state->group_array[state->group_tally].groupName, "%s", "LOCKOUT");
state->group_tally++;
sprintf (state->event_history_s[0].Event_History_Items[0].internal_str, "Target: %d; has been locked out; User Lock Out.", state->lasttg);
watchdog_event_current(opts, state, 0);
sprintf (state->call_string[0], "%s", " "); //21 spaces
//if we have an opened group file, let's write a group lock out into it to make it permanent
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,B,LOCKOUT,%02X\n", state->lasttg, state->payload_algid);
fclose (pFile);
}
//extra safeguards due to sync issues with NXDN
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));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1)
{
//drop all items (failsafe)
noCarrier(opts, state);
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
}
//rtl
#ifdef USE_RTLSDR
if (opts->p25_trunk == 1 && opts->audio_in_type == 3)
{
//drop all items (failsafe)
noCarrier(opts, state);
rtl_dev_tune (opts, state->p25_cc_freq);
}
#endif
state->last_cc_sync_time = time(NULL);
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
}
if (state->lasttgR != 0 && opts->frame_provoice != 1 && c == 64) //SHIFT+'2' key (@ at sign), lockout slot 2 tdma tgR from tuning/playback during session
{
state->group_array[state->group_tally].groupNumber = state->lasttgR;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "B");
sprintf (state->group_array[state->group_tally].groupName, "%s", "LOCKOUT");
state->group_tally++;
sprintf (state->event_history_s[1].Event_History_Items[0].internal_str, "Target: %d; has been locked out; User Lock Out.", state->lasttgR);
watchdog_event_current(opts, state, 1);
sprintf (state->call_string[1], "%s", " "); //21 spaces
//if we have an opened group file, let's write a group lock out into it to make it permanent
if (opts->group_in_file[0] != 0) //file is available
{
FILE * pFile; //file pointer
//open file by name that is supplied in the ncurses terminal, or cli
pFile = fopen (opts->group_in_file, "a");
fprintf (pFile, "%d,B,LOCKOUT,%02X\n", state->lasttgR, state->payload_algidR);
fclose (pFile);
}
//extra safeguards due to sync issues with NXDN
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));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1)
{
//drop all items (failsafe)
noCarrier(opts, state);
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
}
//rtl
#ifdef USE_RTLSDR
if (opts->p25_trunk == 1 && opts->audio_in_type == 3)
{
//drop all items (failsafe)
noCarrier(opts, state);
rtl_dev_tune (opts, state->p25_cc_freq);
}
#endif
state->last_cc_sync_time = time(NULL);
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
}
if (opts->p25_trunk == 1 && c == 119) //'w' key, toggle white list/black list mode
{
if (opts->trunk_use_allow_list == 1) opts->trunk_use_allow_list = 0;
else opts->trunk_use_allow_list = 1;
}
if (opts->p25_trunk == 1 && c == 117) //'u' key, toggle tune private calls
{
if (opts->trunk_tune_private_calls == 1) opts->trunk_tune_private_calls = 0;
else opts->trunk_tune_private_calls = 1;
}
if (opts->p25_trunk == 1 && c == 100) //'d' key, toggle tune data calls
{
if (opts->trunk_tune_data_calls == 1) opts->trunk_tune_data_calls = 0;
else opts->trunk_tune_data_calls = 1;
}
if (opts->p25_trunk == 1 && c == 101) //'e' key, toggle tune enc calls (P25 only on certain grants)
{
if (opts->trunk_tune_enc_calls == 1) opts->trunk_tune_enc_calls = 0;
else opts->trunk_tune_enc_calls = 1;
}
if (opts->p25_trunk == 1 && c == 103) //'g' 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;
}
if (c == 70) //'F' key - toggle agressive sync/crc failure/ras
{
if (opts->aggressive_framesync == 0) opts->aggressive_framesync = 1;
else opts->aggressive_framesync = 0;
}
if (c == 68) //'D' key - Reset DMR Site Parms/Call Strings, etc.
{
//dmr trunking/ncurses stuff
state->dmr_rest_channel = -1; //init on -1
state->dmr_mfid = -1; //
//dmr mfid branding and site parms
sprintf(state->dmr_branding_sub, "%s", "");
sprintf(state->dmr_branding, "%s", "");
sprintf (state->dmr_site_parms, "%s", "");
//DMR Location Area - DMRLA B***S***
opts->dmr_dmrla_is_set = 0;
opts->dmr_dmrla_n = 0;
//reset NXDN info
state->nxdn_location_site_code = 0;
state->nxdn_location_sys_code = 0;
sprintf (state->nxdn_location_category, "%s", " ");
state->nxdn_last_ran = -1; //0
state->nxdn_ran = 0; //0
state->nxdn_rcn = 0;
state->nxdn_base_freq = 0;
state->nxdn_step = 0;
state->nxdn_bw = 0;
}
//Debug/Troubleshooting Option
if (c == 90) //'Z' key - Simulate NoCarrier/No VC/CC sync to zero out more stuff (capital Z)
{
// opts->p25_is_tuned = 0;
state->last_cc_sync_time = 0;
state->last_vc_sync_time = 0;
noCarrier(opts, state);
}
if (c == 93) //']' key - increment event history indexer
{
state->eh_index++;
}
if (c == 91) //'[' key - decrement event history indexer
{
state->eh_index--;
}
if (c == 92) //'\' key - toggle events for slot displayed, and reset eh_index
{
state->eh_slot ^= 1;
state->eh_index = 0;
}
//attempt retry to TCP Audio server
if (c == 56) // '8' key, try audio in type 8 (TCP Audio Server connection) using defaults OR whatever the user last specified
{
opts->tcp_sockfd = Connect(opts->tcp_hostname, opts->tcp_portno);
if (opts->tcp_sockfd != 0)
{
//reset audio input stream
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
opts->audio_in_file_info->channels=1;
opts->audio_in_file_info->seekable=0;
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
if(opts->tcp_file_in == NULL)
{
fprintf(stderr, "Error, couldn't Connect to TCP with libsndfile: %s\n", sf_strerror(NULL));
}
else
{
//close pulse input if it is currently open
if (opts->audio_in_type == 0) closePulseInput(opts);
fprintf (stderr, "TCP Socket Connected Successfully.\n");
opts->audio_in_type = 8;
}
}
else fprintf (stderr, "TCP Socket Connection Error.\n");
}
if (c == 57) //'9' key, try rigctl connection with default values
{
//use same or last specified host for TCP audio sink for connection
memcpy (opts->rigctlhostname, opts->tcp_hostname, sizeof (opts->rigctlhostname) );
opts->rigctl_sockfd = Connect(opts->rigctlhostname, opts->rigctlportno);
if (opts->rigctl_sockfd != 0) opts->use_rigctl = 1;
else opts->use_rigctl = 0;
}
//if trunking and user wants to just go back to the control channel and skip this call
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && c == 67) //Capital C key - Return to CC
{
//extra safeguards due to sync issues with NXDN
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));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1)
{
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
}
//rtl
#ifdef USE_RTLSDR
if (opts->p25_trunk == 1 && opts->audio_in_type == 3) rtl_dev_tune (opts, state->p25_cc_freq);
#endif
state->last_cc_sync_time = time(NULL);
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
//if P25p1 Data Revert on P25p2 TDMA CC, flip symbolrate
if (state->p25_cc_is_tdma == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
fprintf (stderr, "\n User Activated Return to CC; \n ");
}
//if trunking or scanning, manually cycle forward through channels loaded (can be run without trunking or scanning enabled)
if ( (opts->use_rigctl == 1 || opts->audio_in_type == 3) && c == 76) //Capital L key - Cycle Channels Forward
{
//extra safeguards due to sync issues with NXDN
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));
memset (state->active_channel, 0, sizeof(state->active_channel));
//reset dmr blocks
dmr_reset_blocks (opts, state);
//zero out additional items
state->lasttg = 0;
state->lasttgR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->payload_algid = 0;
state->payload_algidR = 0;
state->payload_keyid = 0;
state->payload_keyidR = 0;
state->payload_mi = 0;
state->payload_miR = 0;
state->payload_miP = 0;
state->payload_miN = 0;
opts->p25_is_tuned = 0;
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
//just copy and pasted the cycle logic for CC/signal hunting on no sync
if (state->lcn_freq_roll >= state->lcn_freq_count) //fixed this to skip the extra wait out at the end of the list
{
state->lcn_freq_roll = 0; //reset to zero
}
//roll an extra value up if the current is the same as what's already loaded -- faster hunting on Cap+, etc
if (state->lcn_freq_roll != 0)
{
if (state->trunk_lcn_freq[state->lcn_freq_roll-1] == state->trunk_lcn_freq[state->lcn_freq_roll])
{
state->lcn_freq_roll++;
//check roll again if greater than expected, then go back to zero
if (state->lcn_freq_roll >= state->lcn_freq_count)
{
state->lcn_freq_roll = 0; //reset to zero
}
}
}
//check that we have a non zero value first, then tune next frequency
if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0)
{
//rigctl
if (opts->use_rigctl == 1)
{
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[state->lcn_freq_roll]);
}
//rtl
if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
rtl_dev_tune (opts, state->trunk_lcn_freq[state->lcn_freq_roll]);
#endif
}
fprintf (stderr, "\n User Activated Channel Cycle;");
fprintf (stderr, " Tuning to Frequency: %.06lf MHz\n",
(double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000);
}
state->lcn_freq_roll++;
state->last_cc_sync_time = time(NULL);
//may need to test to see if we want to do the conditional below or not for symbol rate flipping
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
//if P25p1 Data Revert on P25p2 TDMA CC, flip symbolrate
if (state->p25_cc_is_tdma == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
}
}
if (c == 118 && opts->audio_in_type == 3) //'v' key, cycle rtl volume multiplier, when active
{
if (opts->rtl_volume_multiplier == 1 || opts->rtl_volume_multiplier == 2) opts->rtl_volume_multiplier++;
else opts->rtl_volume_multiplier = 1;
}
if (c == 86) // 'V' Key, toggle LPF
{
if (opts->use_lpf == 0) opts->use_lpf = 1;
else opts->use_lpf = 0;
}
if (c == 66) // 'B' Key, toggle HPF
{
if (opts->use_hpf == 0) opts->use_hpf = 1;
else opts->use_hpf = 0;
}
if (c == 78) // 'N' Key, toggle PBF
{
if (opts->use_pbf == 0) opts->use_pbf = 1;
else opts->use_pbf = 0;
}
if (c == 72) // 'H' Key, toggle HPF on digital
{
if (opts->use_hpf_d == 0) opts->use_hpf_d = 1;
else opts->use_hpf_d = 0;
}
if(opts->frame_provoice == 1 && c == 65) //'A' Key, toggle ESK mask 0xA0
{
if (state->esk_mask == 0) state->esk_mask = 0xA0;
else state->esk_mask = 0;
}
if(opts->frame_provoice == 1 && c == 83) //'S' Key, toggle STD or EA mode and reset
{
if (state->ea_mode == -1) state->ea_mode = 0;
else if (state->ea_mode == 0) state->ea_mode = 1;
else state->ea_mode = 0;
//reset -- test to make sure these don't do weird things when reset
state->edacs_site_id = 0;
state->edacs_lcn_count = 0;
state->edacs_cc_lcn = 0;
state->edacs_vc_lcn = 0;
state->edacs_tuned_lcn = -1;
state->edacs_vc_call_type = 0;
state->p25_cc_freq = 0;
opts->p25_is_tuned = 0;
state->lasttg = 0;
state->lastsrc = 0;
}
//RTL PPM Manual Adjustment
if (c == 125)
opts->rtlsdr_ppm_error++;
if (c == 123)
opts->rtlsdr_ppm_error--;
//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
return 0;
} //end ncursesPrinter

1631
src/dsd_ncurses_menu.c Normal file

File diff suppressed because it is too large Load Diff

1712
src/dsd_ncurses_printer.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,6 @@ void error(char *msg) {
struct sockaddr_in address; struct sockaddr_in address;
struct sockaddr_in addressA; struct sockaddr_in addressA;
struct sockaddr_in addressM17;
// //
// Connect // 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 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 //Analog UDP port on +2 of normal open socket
void udp_socket_blasterA(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)
{ {
@@ -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 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) int udp_socket_connect(dsd_opts * opts, dsd_state * state)
{ {
UNUSED(state); UNUSED(state);
@@ -447,46 +420,6 @@ int udp_socket_connectA(dsd_opts * opts, dsd_state * state)
return 0; 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) void return_to_cc (dsd_opts * opts, dsd_state * state)
{ {
//extra safeguards due to sync issues with NXDN //extra safeguards due to sync issues with NXDN
@@ -503,6 +436,8 @@ void return_to_cc (dsd_opts * opts, dsd_state * state)
state->lasttgR = 0; state->lasttgR = 0;
state->lastsrc = 0; state->lastsrc = 0;
state->lastsrcR = 0; state->lastsrcR = 0;
state->gi[0] = -1;
state->gi[1] = -1;
state->payload_algid = 0; state->payload_algid = 0;
state->payload_algidR = 0; state->payload_algidR = 0;
state->payload_keyid = 0; state->payload_keyid = 0;

View File

@@ -141,7 +141,7 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
//tcp socket input from SDR++ -- now with 1 retry if connection is broken //tcp socket input from SDR++ -- now with 1 retry if connection is broken
else if (opts->audio_in_type == 8) else if (opts->audio_in_type == 8)
{ {
#ifdef AERO_BUILD #ifdef __CYGWIN__
result = sf_read_short(opts->tcp_file_in, &sample, 1); result = sf_read_short(opts->tcp_file_in, &sample, 1);
if(result == 0) { if(result == 0) {
fprintf (stderr, "\nConnection to TCP Server Interrupted. Trying again in 3 seconds.\n"); fprintf (stderr, "\nConnection to TCP Server Interrupted. Trying again in 3 seconds.\n");
@@ -627,13 +627,7 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
} }
#endif #endif
//test throttle on wav input files //read dibit capture bin files
if (opts->audio_in_type == 2)
{
if (state->use_throttle == 1) usleep(.003); //very environment specific, tuning to cygwin
}
//read op25/fme symbol bin files
if (opts->audio_in_type == 4) if (opts->audio_in_type == 4)
{ {
//use fopen and read in a symbol, check op25 for clues //use fopen and read in a symbol, check op25 for clues
@@ -645,13 +639,6 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
state->symbolc = fgetc(opts->symbolfile); state->symbolc = fgetc(opts->symbolfile);
//experimental throttle
if (state->use_throttle == 1)
{
// useconds_t stime = state->symbol_throttle;
// usleep(stime);
usleep(.003); //very environment specific, tuning to cygwin
}
//fprintf(stderr, "%d", state->symbolc); //fprintf(stderr, "%d", state->symbolc);
if( feof(opts->symbolfile) ) if( feof(opts->symbolfile) )
{ {
@@ -713,6 +700,16 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
} }
//.raw or .sym float symbol files
if (opts->audio_in_type == 44)
{
float float_symbol = 0.0f;
fread (&float_symbol, sizeof(float), 1, opts->symbolfile); //sizeof(float) is 4 (usually)
if ( feof(opts->symbolfile) ) exitflag = 1; //end of file, exit
// float_symbol = -float_symbol; //inversion
symbol = (int)float_symbol * (int)10000;
}
state->symbolcnt++; state->symbolcnt++;
return (symbol); return (symbol);
} }

View File

@@ -60,6 +60,16 @@ char * getDateH()
return curr; return curr;
} }
//get YYYY/MM/DD with forward slash (LRRP files)
char * getDateS()
{
char * curr = calloc(27, sizeof(char));
time_t t = time(NULL);
struct tm * ptm = localtime(& t);
sprintf(curr, "%04d/%02d/%02d", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday);
return curr;
}
//get YYYY-MM-DD with hyphen (Ncurses Call History) //get YYYY-MM-DD with hyphen (Ncurses Call History)
char * getDateN(time_t t) char * getDateN(time_t t)
{ {
@@ -69,3 +79,20 @@ char * getDateN(time_t t)
return curr; return curr;
} }
//get HHmmss timestamp no colon (file operations)
char * getTimeF(time_t t)
{
char * curr = calloc(7, sizeof(char));
struct tm * ptm = localtime(& t);
sprintf(curr,"%02d%02d%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
return curr;
}
//get YYYYMMDD without hyphen (file operations)
char * getDateF(time_t t)
{
char * curr = calloc(25, sizeof(char));
struct tm * ptm = localtime(& t);
sprintf(curr,"%04d%02d%02d", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday);
return curr;
}

View File

@@ -49,6 +49,10 @@ void processDSTAR(dsd_opts * opts, dsd_state * state)
if (opts->use_ncurses_terminal == 1) if (opts->use_ncurses_terminal == 1)
ncursesPrinter(opts, state); ncursesPrinter(opts, state);
//slot 1
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
} }
fprintf (stderr, "\n"); fprintf (stderr, "\n");
@@ -334,6 +338,8 @@ void processDSTAR_SD(dsd_opts * opts, dsd_state * state, uint8_t * sd)
//this seems to work okay with a few samples, but wouldn't be surprised if it broke down //this seems to work okay with a few samples, but wouldn't be surprised if it broke down
//randomly on different users, depending on location and what else in in $$CRC //randomly on different users, depending on location and what else in in $$CRC
sprintf (state->event_history_s[0].Event_History_Items[0].gps_s, "%s", state->dstar_gps);
} }
else else
{ {
@@ -351,6 +357,7 @@ void processDSTAR_SD(dsd_opts * opts, dsd_state * state, uint8_t * sd)
} }
strt[59] = '\0'; strt[59] = '\0';
memcpy (state->dstar_txt, strt, sizeof(strt)); memcpy (state->dstar_txt, strt, sizeof(strt));
sprintf (state->event_history_s[0].Event_History_Items[0].text_message, "%s", state->dstar_txt);
} }
} }
@@ -370,6 +377,7 @@ void processDSTAR_SD(dsd_opts * opts, dsd_state * state, uint8_t * sd)
} }
strt[59] = '\0'; strt[59] = '\0';
memcpy (state->dstar_txt, strt, sizeof(strt)); memcpy (state->dstar_txt, strt, sizeof(strt));
sprintf (state->event_history_s[0].Event_History_Items[0].text_message, "%s", state->dstar_txt);
} }
//anything else //anything else
else //print entire thing else //print entire thing

View File

@@ -21,6 +21,108 @@
*-----------------------------------------------------------------------------*/ *-----------------------------------------------------------------------------*/
#include "dsd.h" #include "dsd.h"
int isCustomAfsString(dsd_state * state) {
return state->edacs_a_bits != 4 || state->edacs_f_bits != 4 || state->edacs_s_bits != 3;
}
//Get the string length we need for an AFS string, math-style
int getAfsStringLength(dsd_state * state) {
if (!isCustomAfsString(state))
return 6;
int length = 0;
length += (state->edacs_a_bits + 2) / 3;
length += (state->edacs_f_bits + 2) / 3;
length += (state->edacs_s_bits + 2) / 3;
length += 2; //colon separators
// This will be either 6 or 7
return length;
}
//Format the AFS string, Florida-style
int getAfsString(dsd_state * state, char * buffer, int a, int f, int s) {
if (!isCustomAfsString(state))
{
sprintf(buffer, "%02d-%02d%01d", a, f, s);
return 6;
}
int printed_chars = 0;
switch (state->edacs_a_bits)
{
case 1:
case 2:
case 3:
sprintf(buffer, "%01d:", a);
printed_chars += 1;
break;
case 4:
case 5:
case 6:
sprintf(buffer, "%02d:", a);
printed_chars += 2;
break;
case 7:
case 8:
case 9:
sprintf(buffer, "%03d:", a);
printed_chars += 3;
break;
}
sprintf(buffer + printed_chars, ":");
printed_chars++;
switch (state->edacs_f_bits) {
case 1:
case 2:
case 3:
sprintf(buffer + printed_chars, "%01d", f);
printed_chars += 1;
break;
case 4:
case 5:
case 6:
sprintf(buffer + printed_chars, "%02d", f);
printed_chars += 2;
break;
case 7:
case 8:
case 9:
sprintf(buffer + printed_chars, "%03d", f);
printed_chars += 3;
break;
}
sprintf(buffer + printed_chars, ":");
printed_chars++;
switch (state->edacs_s_bits) {
case 1:
case 2:
case 3:
sprintf(buffer + printed_chars, "%01d", s);
printed_chars += 1;
break;
case 4:
case 5:
case 6:
sprintf(buffer + printed_chars, "%02d", s);
printed_chars += 2;
break;
case 7:
case 8:
case 9:
sprintf(buffer + printed_chars, "%03d", s);
printed_chars += 3;
break;
}
return printed_chars;
}
char * getLcnStatusString(int lcn) char * getLcnStatusString(int lcn)
{ {
if (lcn == 26 || lcn == 27) if (lcn == 26 || lcn == 27)
@@ -73,22 +175,6 @@ unsigned long long int edacsVoteFr(unsigned long long int fr_1_4, unsigned long
return msg_result & 0xFFFFFFFFFF; return msg_result & 0xFFFFFFFFFF;
} }
void openWavOutFile48k (dsd_opts * opts, dsd_state * state)
{
UNUSED(state);
SF_INFO info;
info.samplerate = 48000; //48k for analog output (has to match input)
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info);
if (opts->wav_out_f == NULL)
{
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
return;
}
}
//listening to and playing back analog audio //listening to and playing back analog audio
void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn) void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn)
{ {
@@ -375,12 +461,38 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
ncursesPrinter(opts, state); ncursesPrinter(opts, state);
//write to wav file if opened //write to wav file if opened
if (opts->wav_out_f != NULL) if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1) //Per Call
{ {
sf_write_short(opts->wav_out_f, analog1, 960); sf_write_short(opts->wav_out_f, analog1, 960);
sf_write_short(opts->wav_out_f, analog2, 960); sf_write_short(opts->wav_out_f, analog2, 960);
sf_write_short(opts->wav_out_f, analog3, 960); sf_write_short(opts->wav_out_f, analog3, 960);
} }
else if (opts->wav_out_f != NULL && opts->static_wav_file == 1) //Static Wav File
{
//downsample and stereo out (960 / 6 = 160; 160*2 = 320;)
short ss[320];
memset(ss, 0, sizeof(ss));
for (i = 0; i < 160; i++)
{
ss[(i*2)+0] = analog1[i*6]; //grab every 6th sample to downsample
ss[(i*2)+1] = analog1[i*6]; //grab every 6th sample to downsample
}
sf_write_short(opts->wav_out_f, ss, 320);
memset(ss, 0, sizeof(ss));
for (i = 0; i < 160; i++)
{
ss[(i*2)+0] = analog2[i*6]; //grab every 6th sample to downsample
ss[(i*2)+1] = analog2[i*6]; //grab every 6th sample to downsample
}
sf_write_short(opts->wav_out_f, ss, 320);
memset(ss, 0, sizeof(ss));
for (i = 0; i < 160; i++)
{
ss[(i*2)+0] = analog3[i*6]; //grab every 6th sample to downsample
ss[(i*2)+1] = analog3[i*6]; //grab every 6th sample to downsample
}
sf_write_short(opts->wav_out_f, ss, 320);
}
//debug //debug
// fprintf (stderr, " SR: %016llX", sr); // fprintf (stderr, " SR: %016llX", sr);
@@ -417,6 +529,7 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn
if (count > 0) fprintf (stderr, "\n"); if (count > 0) fprintf (stderr, "\n");
} }
} }
void edacs(dsd_opts * opts, dsd_state * state) void edacs(dsd_opts * opts, dsd_state * state)
@@ -455,6 +568,11 @@ void edacs(dsd_opts * opts, dsd_state * state)
edacs_bit[i] = getDibit (opts, state); //getDibit returns binary 0 or 1 on GFSK signal (Edacs and PV) edacs_bit[i] = getDibit (opts, state); //getDibit returns binary 0 or 1 on GFSK signal (Edacs and PV)
} }
//if we have executed a tune to a channel, then we will forego decoding any more edacs until we return from the voice channel
//this is a simple quick and dirty solution to fix setting the lastsrc value to something that we don't want in event history
if (opts->p25_is_tuned == 1)
goto EDACS_END;
//Each EDACS outbound frame consists of two 40-bit (28-bit data, 12-bit BCH) messages. Each message is sent three //Each EDACS outbound frame consists of two 40-bit (28-bit data, 12-bit BCH) messages. Each message is sent three
//times, with the middle message bitwise-inverted. We use unsigned long long int here to be safe in 32-bit cygwin (not //times, with the middle message bitwise-inverted. We use unsigned long long int here to be safe in 32-bit cygwin (not
//sure if this was actually an issue). //sure if this was actually an issue).
@@ -645,6 +763,9 @@ void edacs(dsd_opts * opts, dsd_state * state)
} }
fprintf (stderr, " :: System ID [%04X] CC LCN [%02d]%s", system, state->edacs_cc_lcn, getLcnStatusString(lcn)); fprintf (stderr, " :: System ID [%04X] CC LCN [%02d]%s", system, state->edacs_cc_lcn, getLcnStatusString(lcn));
if (system != 0)
state->edacs_sys_id = system;
//check for control channel lcn frequency if not provided in channel map or in the lcn list //check for control channel lcn frequency if not provided in channel map or in the lcn list
if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0) if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0)
{ {
@@ -681,6 +802,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
fprintf (stderr, " Extended Addressing :: Site ID [%02llX][%03lld] Area [%02X][%03d]", site_id, site_id, area, area); fprintf (stderr, " Extended Addressing :: Site ID [%02llX][%03lld] Area [%02X][%03d]", site_id, site_id, area, area);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
state->edacs_site_id = site_id; state->edacs_site_id = site_id;
state->edacs_area_code = area;
} }
//System Dynamic Regroup Plan Bitmap //System Dynamic Regroup Plan Bitmap
else if (mt2 == 0xB) else if (mt2 == 0xB)
@@ -958,6 +1080,9 @@ void edacs(dsd_opts * opts, dsd_state * state)
} }
} }
//debug for analog testing
// is_digital = 0;
//TG hold on EDACS EA -- block non-matching target, allow matching group //TG hold on EDACS EA -- block non-matching target, allow matching group
if (state->tg_hold != 0 && state->tg_hold != group) sprintf (mode, "%s", "B"); if (state->tg_hold != 0 && state->tg_hold != group) sprintf (mode, "%s", "B");
if (state->tg_hold != 0 && state->tg_hold == group) sprintf (mode, "%s", "A"); if (state->tg_hold != 0 && state->tg_hold == group) sprintf (mode, "%s", "A");
@@ -970,11 +1095,12 @@ void edacs(dsd_opts * opts, dsd_state * state)
//openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) //openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl)
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
{ {
sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TG %d SRC %d.wav", datestr, timestr, state->edacs_site_id, group, source); if (is_digital == 1) {} //just write to already open temp file to be renamed later
if (is_digital == 1) else //close the temp 8k wav file and open as 48k
openWavOutFile (opts, state); {
else 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]);
openWavOutFile48k (opts, state); opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
} }
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
@@ -1085,11 +1211,12 @@ void edacs(dsd_opts * opts, dsd_state * state)
//openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) //openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl)
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
{ {
sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TGT %d SRC %d I-Call.wav", datestr, timestr, state->edacs_site_id, target, source); if (is_digital == 1) {} //just write to already open temp file to be renamed later
if (is_digital == 1) else //close the temp 8k wav file and open as 48k
openWavOutFile (opts, state); {
else 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]);
openWavOutFile48k (opts, state); opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
} }
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
@@ -1191,11 +1318,12 @@ void edacs(dsd_opts * opts, dsd_state * state)
//openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) //openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl)
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
{ {
sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld SRC %d All-Call.wav", datestr, timestr, state->edacs_site_id, source); if (is_digital == 1) {} //just write to already open temp file to be renamed later
if (is_digital == 1) else //close the temp 8k wav file and open as 48k
openWavOutFile (opts, state); {
else 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]);
openWavOutFile48k (opts, state); opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
} }
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
@@ -1370,9 +1498,12 @@ void edacs(dsd_opts * opts, dsd_state * state)
//openwav file and do per call right here //openwav file and do per call right here
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
{ {
sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TG %04d SRC %05d.wav", datestr, timestr, state->edacs_site_id, group, lid); if (is_digital == 1) {} //just write to already open temp file to be renamed later
if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k else //close the temp 8k wav file and open as 48k
else openWavOutFile (opts, state); //digital {
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 = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
} }
if (opts->use_rigctl == 1) if (opts->use_rigctl == 1)
@@ -1436,7 +1567,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
//Call info for state //Call info for state
if (lcn != 0){state->edacs_vc_lcn = lcn;} if (lcn != 0){state->edacs_vc_lcn = lcn;}
state->lasttg = target; state->lasttg = target;
state->lastsrc = 0; state->lastsrc = 0x800;
//Call type for state //Call type for state
if (is_individual_call == 0) state->edacs_vc_call_type = EDACS_IS_GROUP; if (is_individual_call == 0) state->edacs_vc_call_type = EDACS_IS_GROUP;
@@ -1570,7 +1701,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (lcn != 0){state->edacs_vc_lcn = lcn;} if (lcn != 0){state->edacs_vc_lcn = lcn;}
state->lasttg = target; state->lasttg = target;
//Alas, EDACS standard does not provide a source LID on channel updates - try to work around this on the display end instead //Alas, EDACS standard does not provide a source LID on channel updates - try to work around this on the display end instead
state->lastsrc = 0; state->lastsrc = 0x800;
//Call type for state //Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE; state->edacs_vc_call_type = EDACS_IS_VOICE;
@@ -1623,10 +1754,12 @@ void edacs(dsd_opts * opts, dsd_state * state)
//openwav file and do per call right here //openwav file and do per call right here
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
{ {
if (is_individual == 0) sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TG %04d SRC %05d.wav", datestr, timestr, state->edacs_site_id, target, state->lastsrc); if (is_digital == 1) {} //just write to already open temp file to be renamed later
else sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TGT %05d SRC %05d I-Call.wav", datestr, timestr, state->edacs_site_id, target, state->lastsrc); else //close the temp 8k wav file and open as 48k
if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k {
else openWavOutFile (opts, state); //digital 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 = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
} }
if (opts->use_rigctl == 1) if (opts->use_rigctl == 1)
@@ -1734,9 +1867,12 @@ void edacs(dsd_opts * opts, dsd_state * state)
//openwav file and do per call right here //openwav file and do per call right here
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
{ {
sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TGT %05d SRC %05d I-Call.wav", datestr, timestr, state->edacs_site_id, target, state->lastsrc); if (is_digital == 1) {} //just write to already open temp file to be renamed later
if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k else //close the temp 8k wav file and open as 48k
else openWavOutFile (opts, state); //digital {
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 = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
} }
if (opts->use_rigctl == 1) if (opts->use_rigctl == 1)
@@ -2027,9 +2163,12 @@ void edacs(dsd_opts * opts, dsd_state * state)
//openwav file and do per call right here //openwav file and do per call right here
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3)) if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
{ {
sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld SRC %05d All-Call.wav", datestr, timestr, state->edacs_site_id, state->lastsrc); if (is_digital == 1) {} //just write to already open temp file to be renamed later
if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k else //close the temp 8k wav file and open as 48k
else openWavOutFile (opts, state); //digital {
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 = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
} }
if (opts->use_rigctl == 1) if (opts->use_rigctl == 1)
@@ -2135,6 +2274,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
} }
EDACS_END:
if (timestr != NULL) if (timestr != NULL)
{ {
@@ -2149,6 +2289,9 @@ void edacs(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "\n"); fprintf (stderr, "\n");
//when on a CC, rotate the symbol out file every hour, if enabled
rotate_symbol_out_file(opts, state);
} }
void eot_cc(dsd_opts * opts, dsd_state * state) void eot_cc(dsd_opts * opts, dsd_state * state)
@@ -2156,6 +2299,23 @@ void eot_cc(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "EOT; \n"); 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);
watchdog_event_current(opts, state, 0);
//close and rename wav file here, then open a new one
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 = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
}
//set here so that when returning to the CC, it doesn't go into an immediate hunt if not immediately acquired //set here so that when returning to the CC, it doesn't go into an immediate hunt if not immediately acquired
state->last_cc_sync_time = time(NULL); state->last_cc_sync_time = time(NULL);
state->last_vc_sync_time = time(NULL); state->last_vc_sync_time = time(NULL);

View File

@@ -85,7 +85,7 @@ int ez_rs28_facch (int payload[156], int parity[114])
int i, j, k, b; int i, j, k, b;
//init HB //init HB
for (i = 0; i < 64; i++) for (i = 0; i < (int)HB.size(); i++)
{ {
HB[i] = 0; HB[i] = 0;
} }
@@ -136,7 +136,7 @@ int ez_rs28_sacch (int payload[180], int parity[132])
int i, j, k, b; int i, j, k, b;
//init HBS //init HBS
for (i = 0; i < 64; i++) for (i = 0; i < (int)HBS.size(); i++)
{ {
HBS[i] = 0; HBS[i] = 0;
} }

3958
src/m17.c

File diff suppressed because it is too large Load Diff

View File

@@ -25,26 +25,45 @@
#include "dsd.h" #include "dsd.h"
#include "nxdn_const.h" #include "nxdn_const.h"
//NOTE: Descrambling was having an issue without a value inside the brackets, but only when void nxdn_pn95_dibit_scrambler(dsd_state * state, uint8_t * dibits, int len)
//the PARITY table was disabled, was this due to a memory issue or overflow?
static const uint8_t scramble_t[182] = { //values are the position values we need to invert in the descramble
2, 5, 6, 7, 10, 12, 14, 16, 17, 22, 23, 25, 26, 27, 28, 30, 33, 34, 36, 37, 38, 41, 45, 47,
52, 54, 56, 57, 59, 62, 63, 64, 65, 66, 67, 69, 70, 73, 76, 79, 81, 82, 84, 85, 86, 87, 88,
89, 92, 95, 96, 98, 100, 103, 104, 107, 108, 116, 117, 121, 122, 125, 127, 131, 132, 134,
137, 139, 140, 141, 142, 143, 144, 145, 147, 151, 153, 154, 158, 159, 160, 162, 164, 165,
168, 170, 171, 174, 175, 176, 177, 181
};
//decoding functions here
void nxdn_descramble(uint8_t dibits[], int len)
{ {
for (int i=0; i<len; i++)
uint16_t lfsr = state->nxdn_pn95_seed; //default value is 228 / 0xE4
uint16_t bit = 0;
uint8_t pN95[182]; memset (pN95, 0, sizeof(pN95));
for (int i = 0; i < len; i++)
{ {
if (scramble_t[i] >= len) //before feedback, take the bit
break; pN95[i] = lfsr & 1;
dibits[scramble_t[i]] ^= 0x2; // invert sign of scrambled dibits
//since this is right shift, the taps are 0 and 4, and not 8 and 4 (9,5)
bit = ((lfsr >> 4) ^ (lfsr >> 0)) & 1;
lfsr >>= 1;
lfsr |= (bit << 8);
} }
//convert the pN sequence to a scramble table
uint8_t scramble_table[182]; memset(scramble_table, 0, sizeof(scramble_table));
int k = 0;
for (int i = 0; i < len; i++)
{
if (pN95[i] == 1)
scramble_table[k++] = i;
}
//the entries into the scramble_table are the dibits (symbols) that are inverted
for (int i = 0; i < k; i++)
{
dibits[scramble_table[i]] ^= 0x2;
dibits[scramble_table[i]] &= 0x3;
}
//debug, make sure we don't overflow dibits array
// fprintf (stderr, " PN95: K: %d; Len: %d; ", k, len);
} }
void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144]) void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
@@ -104,11 +123,11 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1; trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
} }
crc = crc12f (trellis_buf, 84); //80 crc = crc12f (trellis_buf, 80);
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
{ {
check = check << 1; check = check << 1;
check = check | trellis_buf[84+i]; //80 check = check | trellis_buf[80+i];
} }
//debug //debug
@@ -127,11 +146,11 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
//fill m_data bytes with trellis_buf //fill m_data bytes with trellis_buf
for(int i = 0; i < 12; i++) for(int i = 0; i < 12; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8); m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
crc = crc12f (trellis_buf, 84); crc = crc12f (trellis_buf, 80);
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
{ {
check = check << 1; check = check << 1;
check = check | trellis_buf[i+84]; check = check | trellis_buf[i+80];
} }
} }
@@ -266,6 +285,9 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran); if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
else fprintf (stderr, " "); else fprintf (stderr, " ");
//needed for DES and AES
state->nxdn_part_of_frame = 0;
uint8_t nsf_sacch[26]; uint8_t nsf_sacch[26];
memset (nsf_sacch, 0, sizeof(nsf_sacch)); memset (nsf_sacch, 0, sizeof(nsf_sacch));
for (int i = 0; i < 26; i++) for (int i = 0; i < 26; i++)
@@ -281,7 +303,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
fprintf (stderr, "PF 1/1"); fprintf (stderr, "PF 1/1");
if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed
else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch); if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch);
// else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, nsf_sacch); // else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, nsf_sacch);
@@ -327,6 +349,9 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
else if (sf == 0) part_of_frame = 3; else if (sf == 0) part_of_frame = 3;
else part_of_frame = 0; else part_of_frame = 0;
//needed for DES and AES
state->nxdn_part_of_frame = part_of_frame;
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran); if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
else fprintf (stderr, " "); else fprintf (stderr, " ");
@@ -336,7 +361,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
if (part_of_frame == 0) if (part_of_frame == 0)
{ {
if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed
else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed
} }
// if (crc != check) // if (crc != check)
@@ -353,13 +378,13 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1)
{ {
if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed
else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed
} }
//this seems to work much better now //this seems to work much better now
else if (part_of_frame != 0 && state->nxdn_cipher_type == 0x1) else if (part_of_frame != 0 && state->nxdn_cipher_type == 0x1)
{ {
if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed
else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed
//advance seed by required number of turns depending on the current pf value //advance seed by required number of turns depending on the current pf value
int start = 0; int end = part_of_frame; int start = 0; int end = part_of_frame;
@@ -415,6 +440,457 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
} }
//sacch2 (JPN DCR)
//SEE: https://web.archive.org/web/20150417175725/http://arib.or.jp/english/html/overview/doc/1-STD-T98v1_4.pdf
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
{
//see about initializing these variables
uint8_t deperm[60]; //60
uint8_t depunc[72]; //72
uint8_t trellis_buf[32]; //32
memset (deperm, 0, sizeof(deperm));
memset (depunc, 0, sizeof(depunc));
memset (trellis_buf, 0, sizeof(trellis_buf));
int o = 0;
uint8_t crc = 1; //value computed by crc6 on payload
uint8_t check = 0; //value pulled from last 6 bits
for (int i=0; i<60; i++)
deperm[PERM_12_5[i]] = bits[i];
for (int p=0; p<60; p+= 10) {
depunc[o++] = deperm[p+0];
depunc[o++] = deperm[p+1];
depunc[o++] = deperm[p+2];
depunc[o++] = deperm[p+3];
depunc[o++] = deperm[p+4];
depunc[o++] = 0;
depunc[o++] = deperm[p+5];
depunc[o++] = deperm[p+6];
depunc[o++] = deperm[p+7];
depunc[o++] = deperm[p+8];
depunc[o++] = deperm[p+9];
depunc[o++] = 0;
}
//switch to the convolutional decoder
uint8_t temp[80];
uint8_t s0;
uint8_t s1;
uint8_t m_data[5]; //5
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
memset (trellis_buf, 0, sizeof(trellis_buf));
for (int i = 0; i < 72; i++)
temp[i] = depunc[i] << 1;
CNXDNConvolution_start();
for (int i = 0; i < 36; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
//stored as 4 bytes, will need to convert to trellis_buf after running
CNXDNConvolution_chainback(m_data, 32);
for(int i = 0; i < 4; i++)
{
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
}
crc = crc6(trellis_buf, 26);
check = (uint8_t) convert_bits_into_output(trellis_buf+26, 6);
//debug
// if (crc == check)
// fprintf (stderr, " Pass 1 ");
//if the crc fails, attempt again with the other trellis decoder
if (crc != check)
{
//debug
// fprintf (stderr, " Pass 2 ");
crc = 1; check = 0;
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (m_data, 0, sizeof(m_data));
trellis_decode(trellis_buf, depunc, 32);
//fill m_data bytes with trellis_buf
for(int i = 0; i < 4; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
crc = crc6(trellis_buf, 26); //32
check = (uint8_t) convert_bits_into_output(trellis_buf+26, 6);
}
//Configuration of Single Message or Multi Part Message
uint8_t sf_fb = trellis_buf[0];
uint8_t sf_num = (uint8_t) convert_bits_into_output(trellis_buf+1, 2);
uint8_t sf_mes = (uint8_t) convert_bits_into_output(trellis_buf+3, 5);
uint8_t sf_pof = 3-sf_num;
if (crc == check)
{
if (sf_fb && sf_pof) //single message, single unit
fprintf (stderr, "PF: %d/1; ", sf_num+1);
else //multiple unit message
fprintf (stderr, "PF: %d/4; ", sf_pof+1);
if (sf_mes == 0x01)
fprintf (stderr, "Call; ");
else if (sf_mes == 0x02)
fprintf (stderr, "PDU; ");
else if (sf_mes == 0x1E)
fprintf (stderr, "End; ");
else if (sf_mes == 0x00)
fprintf (stderr, "Idle; ");
else fprintf (stderr, "Res: %02X; ", sf_mes);
}
else if (crc != check)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "SACCH (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
if (crc == check)
state->nxdn_sacch_frame_segcrc[sf_num] = 0;
else state->nxdn_sacch_frame_segcrc[sf_num] = 1;
//entire superframe has good crc
uint8_t crc_sf_check = 0;
for (int i = 0; i < 4; i++)
crc_sf_check += state->nxdn_sacch_frame_segcrc[i];
//values for storage and which parts to store
int sf_full = 26; //full size of a sacch frame, minus CRC6
int sf_size = 18; //size of superframe portion (18 bits)
int sf_end = 0; //end of sf
int sf_idx = sf_size*sf_pof; //index position for this frame compared to super frame
int bf_idx = sf_full-sf_size; //index position for buffer to superframe
if (sf_fb && sf_pof) //single unit message
{
memset (state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
memcpy(state->dmr_pdu_sf[0]+0, trellis_buf+bf_idx, sf_size*sizeof(uint8_t));
}
else //multiple unit message
memcpy(state->dmr_pdu_sf[0]+sf_idx, trellis_buf+bf_idx, sf_size*sizeof(uint8_t));
//if force application of scrambler key, then let's reset, regardless of CRC check
if (sf_fb && state->forced_alg_id == 1)
state->payload_miN = 0;
//currently using static values so event log will log something, and do wav files, etc
//disable this is random false positive for this lich code triggers this often enough
if (crc == check)
{
state->gi[0] = 0;
state->nxdn_last_ran = 7;
state->nxdn_last_tg = 777;
state->nxdn_last_rid = 777;
//disabled with CSM going into Alias Value
// sprintf (state->generic_talker_alias[0], "%s", "JPN DCR");
// sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", "JPN DCR");
//sf_fb is the head message in a multi part, or the only message in a single part message
if (sf_fb)
state->payload_miN = 0;
}
//check for valid crc on single, or on all received
if ( (sf_fb && sf_pof && crc == check) || //single frame
(sf_num == sf_end && crc_sf_check == 0) ) //multi part frame
{
uint8_t cipher = (uint16_t) convert_bits_into_output(state->dmr_pdu_sf[0]+0, 2);
uint16_t user_code = (uint16_t) convert_bits_into_output(state->dmr_pdu_sf[0]+2, 9);
fprintf (stderr, "UC: %03d; ", user_code);
if (cipher == 0x01)
{
fprintf (stderr, "Scrambler; ");
state->nxdn_cipher_type = 1;
if (state->R != 0)
fprintf (stderr, "Key: %lld; ", state->R);
}
else if (cipher != 0x00)
{
fprintf (stderr, "Reserved Comms: %d; ", cipher);
}
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
if (state->nxdn_cipher_type != 0)
state->dmr_encL = 1;
if (state->nxdn_cipher_type == 0 || state->R != 0)
state->dmr_encL = 0;
//this always appears to be 0, but could be other values
uint8_t mfid = (uint16_t) convert_bits_into_output(state->dmr_pdu_sf[0]+11, 7);
if (mfid != 0)
fprintf (stderr, "MFID: %02X; ", mfid);
//multi-part message, continue decoding
if (sf_fb == 0 && sf_num == 0)
{
fprintf (stderr, "\n");
//can't find definitions for these elements, even when MT == 1 and MFID == 0
unsigned long long int mes_hex = (unsigned long long int ) convert_bits_into_output(state->dmr_pdu_sf[0]+18, 54);
fprintf (stderr, " Message: %014llX; ", mes_hex << 0);
}
}
if (opts->payload == 1)
{
fprintf (stderr, "\n DCR SACCH ");
for (int i = 0; i < 4; i++)
fprintf (stderr, "[%02X]", m_data[i]);
if (sf_num == sf_end)
{
fprintf (stderr, "\n DCR SFULL ");
for (int i = 0; i < 9; i++)
fprintf (stderr, "[%02X]", (uint8_t)convert_bits_into_output(state->dmr_pdu_sf[0]+(i*8), 8));
}
}
//clear out if run, or crc error
if (sf_num == sf_end)
{
memset (state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
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));
}
else if (sf_fb && sf_pof) //single
{
memset (state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
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));
}
}
//PICH or TCH 144 bit (JPN DCR)
//SEE: https://web.archive.org/web/20150417175725/http://arib.or.jp/english/html/overview/doc/1-STD-T98v1_4.pdf
void nxdn_deperm_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t bits[144], uint8_t lich)
{
uint8_t deperm[144]; //144
uint8_t depunc[192]; //192
uint8_t trellis_buf[96]; //96
uint16_t crc = 1; //crc calculated by function
uint16_t check = 0; //crc from payload for comparison
int out;
memset (deperm, 0, sizeof(deperm));
memset (depunc, 0, sizeof(depunc));
for (int i=0; i<144; i++)
deperm[PERM_16_9[i]] = bits[i];
out = 0;
for (int i=0; i<144; i+=3) {
depunc[out++] = deperm[i+0];
depunc[out++] = 0;
depunc[out++] = deperm[i+1];
depunc[out++] = deperm[i+2];
}
//switch to the convolutional decoder
uint8_t temp[200];
uint8_t s0;
uint8_t s1;
uint8_t m_data[20]; //13
memset (temp, 0, sizeof(temp));
memset (m_data, 0, sizeof(m_data));
memset (trellis_buf, 0, sizeof(trellis_buf));
for (int i = 0; i < 192; i++)
temp[i] = depunc[i] << 1;
CNXDNConvolution_start();
for (int i = 0; i < 96; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 92);
for(int i = 0; i < 12; i++)
{
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
}
crc = crc12f (trellis_buf, 80);
for (int i = 0; i < 12; i++)
{
check = check << 1;
check = check | trellis_buf[80+i];
}
//debug
// if (crc == check)
// fprintf (stderr, " Pass 1 ");
//if the crc fails, attempt again with the other trellis decoder
if (crc != check)
{
//debug
// fprintf (stderr, " Pass 2 ");
crc = 1; check = 0;
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (m_data, 0, sizeof(m_data));
trellis_decode(trellis_buf, depunc, 92);
//fill m_data bytes with trellis_buf
for(int i = 0; i < 12; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
crc = crc12f (trellis_buf, 80);
for (int i = 0; i < 12; i++)
{
check = check << 1;
check = check | trellis_buf[i+80];
}
}
//STD-T98 DCR suggests TCH1 has data, and TCH2 will be zero fill
//could vary by PDU, but limited data points suggests the same
if (crc == check)
{
uint8_t opcode = (uint8_t)ConvertBitIntoBytes(&trellis_buf[0], 8);
uint8_t gi = trellis_buf[16];
uint16_t source = (uint16_t)ConvertBitIntoBytes(&trellis_buf[24], 16);
uint16_t target = (uint16_t)ConvertBitIntoBytes(&trellis_buf[40], 16);
//SB0 with CSM
if (lich == 0x08)
{
unsigned long long int csm = 0;
for (int i = 0; i < 9; i++)
{
csm <<= 4;
uint8_t bcd = (uint8_t)ConvertBitIntoBytes(&trellis_buf[0+(i*4)], 4);
// if (bcd < 10)
// csm |= bcd;
// else csm |= 0;
csm |= bcd;
}
fprintf (stderr, "\n ");
fprintf (stderr, "Call Sign Memory: %09llX; ", csm);
//Assigning this to talker alias, see notes below on decimal value
sprintf (state->generic_talker_alias[0], "CSM %09llX", csm);
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "CSM %09llX", csm);
//convert from hex to string to decimal w/ sscanf
// char csm_str[32]; memset(csm_str, 0, sizeof(csm_str));
// sprintf (csm_str, "%llX", csm);
// unsigned long long int csm_dec = 0;
// sscanf(csm_str, "%lld", &csm_dec);
//the issue is that this value will exceed the bit allotment for nxdn src value (needs long long at 36-bit)
//even values in range 100000000 - 200000000 are 33-bits minimum
// fprintf (stderr, "Call Sign Memory: %09lld; ", csm_dec);
}
//anything else
else
{
//may only be relevant on MFID 0x30 "F.R.C." Radios
if (opcode == 0x0F)
{
fprintf (stderr, "\n ");
fprintf (stderr, "Source: %d; Target: %d; ", source, target);
if (gi)
fprintf (stderr, "Private; ");
else fprintf (stderr, "Group; ");
fprintf (stderr, "Data Preamble; ");
uint8_t countdown = (uint8_t)ConvertBitIntoBytes(&trellis_buf[64], 8);
fprintf (stderr, "Countdown: %d; ", countdown);
}
//may only be relevant on MFID 0x30 "F.R.C." Radios
if (opcode == 0x32)
{
fprintf (stderr, "\n ");
fprintf (stderr, "Source: %d; Target: %d; ", source, target);
if (gi)
fprintf (stderr, "Private; ");
else fprintf (stderr, "Group; ");
fprintf (stderr, "Precoded Message; ");
uint8_t idx = (uint8_t)ConvertBitIntoBytes(&trellis_buf[64], 8);
fprintf (stderr, "Index#: %d;", idx);
}
}
// if (opcode == 0x00)
// {
// fprintf (stderr, "\n NULL TCH; ");
// }
}
else if (opts->payload == 0)
{
fprintf (stderr, "\n ");
fprintf (stderr, "%s", KRED);
if (lich == 0x08)
fprintf (stderr, "PICH (CRC ERR)");
else fprintf (stderr, "TCH (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
if (opts->payload == 1)
{
fprintf (stderr, "\n");
if (lich == 0x08)
fprintf (stderr, " PICH Payload ");
else fprintf (stderr, " TCH Payload ");
for (int i = 0; i < 12; i++)
{
fprintf (stderr, "[%02X]", m_data[i]);
}
if (crc != check && opts->payload == 1)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
}
UNUSED(state);
}
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type) void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type)
{ {
uint8_t deperm[348]; //348 uint8_t deperm[348]; //348
@@ -635,7 +1111,7 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
} }
//switch to the convolutional decoder //switch to the convolutional decoder
uint8_t temp[179]; uint8_t temp[350];
uint8_t s0; uint8_t s0;
uint8_t s1; uint8_t s1;
uint8_t m_data[22]; //26 uint8_t m_data[22]; //26
@@ -776,6 +1252,9 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
} }
//when on a CC, rotate the symbol out file every hour, if enabled
rotate_symbol_out_file(opts, state);
} }
//Type-D "IDAS" //Type-D "IDAS"
@@ -876,7 +1355,7 @@ void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint
for(int i = 0; i < 4; i++) for(int i = 0; i < 4; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8); m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
crc = crc7_scch(trellis_buf, 25); crc = crc7_scch(trellis_buf, 25);
for (int i = 0; i < 6; i++) for (int i = 0; i < 7; i++)
{ {
check = check << 1; check = check << 1;
check = check | trellis_buf[i+25]; check = check | trellis_buf[i+25];
@@ -898,7 +1377,7 @@ void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1)
{ {
if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed if (state->nxdn_cipher_type == 1 && state->R != 0) state->payload_miN = state->R; //reset scrambler seed
else if (state->M == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed else if (state->forced_alg_id == 1 && state->R != 0) state->payload_miN = state->R; //force reset scrambler seed
} }
/* /*
@@ -1010,11 +1489,11 @@ void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[2
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1; trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
} }
crc[j] = crc12f (trellis_buf, 84); //84 crc[j] = crc12f (trellis_buf, 80);
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
{ {
check[j] = check[j] << 1; check[j] = check[j] << 1;
check[j] = check[j] | trellis_buf[84+i]; //84 check[j] = check[j] | trellis_buf[80+i];
} }
@@ -1034,11 +1513,11 @@ void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[2
//fill m_data bytes with trellis_buf //fill m_data bytes with trellis_buf
for(int i = 0; i < 12; i++) for(int i = 0; i < 12; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8); m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
crc[j] = crc12f (trellis_buf, 84); crc[j] = crc12f (trellis_buf, 80);
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
{ {
check[j] = check[j] << 1; check[j] = check[j] << 1;
check[j] = check[j] | trellis_buf[i+84]; check[j] = check[j] | trellis_buf[i+80];
} }
} }
@@ -1145,7 +1624,7 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
//RTCH Outbound will take precedent when differences may occur (except CALL_ASSGN) //RTCH Outbound will take precedent when differences may occur (except CALL_ASSGN)
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
if (MessageType == 0x10) fprintf(stderr, " IDLE"); if (MessageType == 0x10) fprintf(stderr, " IDLE");
else if (MessageType == 0x00) fprintf(stderr, " CALL_RESP"); // else if (MessageType == 0x00) fprintf(stderr, " CALL_RESP");
else if (MessageType == 0x01) fprintf(stderr, " VCALL"); else if (MessageType == 0x01) fprintf(stderr, " VCALL");
else if (MessageType == 0x02) fprintf(stderr, " VCALL_REC_REQ"); else if (MessageType == 0x02) fprintf(stderr, " VCALL_REC_REQ");
else if (MessageType == 0x03) fprintf(stderr, " VCALL_IV"); else if (MessageType == 0x03) fprintf(stderr, " VCALL_IV");
@@ -1176,7 +1655,12 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA"); else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP"); else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS"); else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
else fprintf(stderr, " Unknown M-%02X", MessageType); //observed from #318 and found in ARIB STD-B54
else if (MessageType == 0x21) fprintf(stderr, " VCALL_ARIB_STD_B54");
else if (MessageType == 0x28) fprintf(stderr, " TX_REL_ARIB_STD_B54");
else if (MessageType == 0x27) fprintf(stderr, " ALIAS_ARIB_STD_B54");
//TODO: Add rest of ARIB STD-B54 Message Types (Also, need to add a flag F1 and F2 check here, or earlier and make a seperate one for this)
else fprintf(stderr, " Unknown Message Type: %02X;", MessageType);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
//Zero out stale values on DISC or TX_REL only (IDLE messaages occur often on NXDN96 VCH, and randomly on Type-C FACCH1 steals for some reason) //Zero out stale values on DISC or TX_REL only (IDLE messaages occur often on NXDN96 VCH, and randomly on Type-C FACCH1 steals for some reason)
@@ -1330,3 +1814,43 @@ uint8_t crc7_scch(uint8_t bits[], int len)
} }
return load_i(s, 7); return load_i(s, 7);
} }
void LFSR128n(dsd_state * state)
{
//generate a 128-bit IV from a 64-bit IV for AES blocks
unsigned long long int lfsr = state->payload_miN;
//start packing aes_iv
state->aes_iv[0] = (lfsr >> 56) & 0xFF;
state->aes_iv[1] = (lfsr >> 48) & 0xFF;
state->aes_iv[2] = (lfsr >> 40) & 0xFF;
state->aes_iv[3] = (lfsr >> 32) & 0xFF;
state->aes_iv[4] = (lfsr >> 24) & 0xFF;
state->aes_iv[5] = (lfsr >> 16) & 0xFF;
state->aes_iv[6] = (lfsr >> 8 ) & 0xFF;
state->aes_iv[7] = (lfsr >> 0 ) & 0xFF;
int cnt = 0; int x = 64;
unsigned long long int bit;
//polynomial P(x) = 1 + X15 + X27 + X38 + X46 + X62 + X64
for(cnt=0;cnt<64;cnt++)
{
//63,61,45,37,27,14
// Polynomial is C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
lfsr = (lfsr << 1) | bit;
//continue packing aes_iv
state->aes_iv[x/8] = (state->aes_iv[x/8] << 1) + bit;
x++;
}
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "\n");
fprintf (stderr, " IV(128): 0x");
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", state->aes_iv[x]);
fprintf (stderr, "%s", KNRM);
}

View File

@@ -97,6 +97,21 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
*/ */
//Debug: Disable DUP messages if they cause random issues with Type-C trunking (i.e. changing SRC ang TGT IDs, hopping in the middle of calls, etc) //Debug: Disable DUP messages if they cause random issues with Type-C trunking (i.e. changing SRC ang TGT IDs, hopping in the middle of calls, etc)
//observed new messages in #318, should also be noted that F1 and F2 are both set on these messages
//VCALL and TX_REL custom to certain radios, but they have different elements in them
case 0x21:
case 0x28:
NXDN_decode_VCALL_ARIB(opts, state, ElementsContent);
break;
//Shift-JIS Talker Alias
case 0x27:
NXDN_decode_ALIAS_ARIB(opts, state, ElementsContent);
break;
//end observations from #318
//VCALL_ASSGN_DUP //VCALL_ASSGN_DUP
case 0x05: case 0x05:
@@ -139,9 +154,11 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
break; break;
//VCALL, TX_REL_EXT and TX_REL //VCALL, TX_REL_EXT and TX_REL
case 0x01: case 0x07: //TX_REL_EXT
case 0x07: case 0x08: //TX_REL
case 0x08: sprintf (state->call_string[0], "%s", "");
sprintf (state->nxdn_call_type, "%s", "");
case 0x01: //VCALL
NXDN_decode_VCALL(opts, state, ElementsContent); NXDN_decode_VCALL(opts, state, ElementsContent);
break; break;
@@ -149,6 +166,8 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
case 0x11: case 0x11:
NXDN_decode_VCALL(opts, state, ElementsContent); NXDN_decode_VCALL(opts, state, ElementsContent);
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment)); memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
sprintf (state->call_string[0], "%s", "");
sprintf (state->nxdn_call_type, "%s", "");
// #ifdef LIMAZULUTWEAKS // #ifdef LIMAZULUTWEAKS
// ; //do nothing -- testing errors on CAC messages when returning quickly from RTCH // ; //do nothing -- testing errors on CAC messages when returning quickly from RTCH
@@ -170,7 +189,7 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
state->nxdn_last_rid = 0; state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0; state->nxdn_last_tg = 0;
if (state->M == 0) if (state->forced_alg_id == 0)
state->nxdn_cipher_type = 0; state->nxdn_cipher_type = 0;
sprintf (state->nxdn_call_type, "%s", ""); sprintf (state->nxdn_call_type, "%s", "");
@@ -188,7 +207,7 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
state->nxdn_last_rid = 0; state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0; state->nxdn_last_tg = 0;
if (state->M == 0) if (state->forced_alg_id == 0)
state->nxdn_cipher_type = 0; state->nxdn_cipher_type = 0;
sprintf (state->nxdn_call_type, "%s", ""); sprintf (state->nxdn_call_type, "%s", "");
#endif #endif
@@ -647,6 +666,16 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
state->nxdn_last_tg = DestinationID; state->nxdn_last_tg = DestinationID;
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType)); sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
if (CallType == 3)
{
state->gi[0] = 1; //Private Call
//unassign these, sometimes, when trunking, these may be reversed by the time listened,
//and this will plant an extra private call in the event_history
state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0;
}
else state->gi[0] = 0; //Group Call
//Call String for Per Call WAV File //Call String for Per Call WAV File
sprintf (state->call_string[0], "%s", NXDN_Call_Type_To_Str(CallType)); sprintf (state->call_string[0], "%s", NXDN_Call_Type_To_Str(CallType));
if (CCOption & 0x80) strcat (state->call_string[0], " Emergency"); if (CCOption & 0x80) strcat (state->call_string[0], " Emergency");
@@ -660,7 +689,7 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
fprintf (stderr, " Key Loaded: %lld", state->rkey_array[DestinationID]); fprintf (stderr, " Key Loaded: %lld", state->rkey_array[DestinationID]);
state->payload_miN = state->R; //should be okay to load here, will test state->payload_miN = state->R; //should be okay to load here, will test
} }
if (state->M == 1) state->nxdn_cipher_type = 0x1; if (state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1;
} }
//rtl //rtl
else if (opts->audio_in_type == 3) else if (opts->audio_in_type == 3)
@@ -685,6 +714,16 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
state->nxdn_last_tg = DestinationID; state->nxdn_last_tg = DestinationID;
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType)); sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
if (CallType == 3)
{
state->gi[0] = 1; //Private Call
//unassign these, sometimes, when trunking, these may be reversed by the time listened,
//and this will plant an extra private call in the event_history
state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0;
}
else state->gi[0] = 0; //Group Call
//Call String for Per Call WAV File //Call String for Per Call WAV File
sprintf (state->call_string[0], "%s", NXDN_Call_Type_To_Str(CallType)); sprintf (state->call_string[0], "%s", NXDN_Call_Type_To_Str(CallType));
if (CCOption & 0x80) strcat (state->call_string[0], " Emergency"); if (CCOption & 0x80) strcat (state->call_string[0], " Emergency");
@@ -698,7 +737,7 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
fprintf (stderr, " Key Loaded: %lld", state->rkey_array[DestinationID]); fprintf (stderr, " Key Loaded: %lld", state->rkey_array[DestinationID]);
state->payload_miN = state->R; //should be okay to load here, will test state->payload_miN = state->R; //should be okay to load here, will test
} }
if (state->M == 1) state->nxdn_cipher_type = 0x1; if (state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1;
#endif #endif
} }
@@ -745,9 +784,11 @@ void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
char str_a[120]; char str_b[50]; char str_a[120]; char str_b[50];
//TODO: Revisit the debug here and see if anythign comes out of it,
//maybe go on the assumptiont his works very similar to DMR talker alias
//debug/test //debug/test
// fprintf (stderr, " U1: %02X U2: %02X;", unk1, unk2); // fprintf (stderr, " U1: %02X U2: %02X;", unk1, unk2); //these always appear to be the same two byte values, even across different systems and languages
// fprintf (stderr, " A:%d/%d; ", blocknumber, total); // fprintf (stderr, " A:%d/%d; ", blocknumber, total); //this is accurate info here
//sanity check to prevent OOB array assignment //sanity check to prevent OOB array assignment
if (blocknumber > 0 && blocknumber < 4) //last 'block' may have been assigning garbage name values -- I'm honestly not sure block'4' contains Alias data, but other data or something if (blocknumber > 0 && blocknumber < 4) //last 'block' may have been assigning garbage name values -- I'm honestly not sure block'4' contains Alias data, but other data or something
@@ -779,12 +820,8 @@ void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
//juggle strings here so we don't get compiler warnings on assignment size //juggle strings here so we don't get compiler warnings on assignment size
memcpy (str_b, str_a, 48); str_b[49] = '\0'; memcpy (str_b, str_a, 48); str_b[49] = '\0';
//one noteable issue is that on conventional, its possible to decode an alias before decong the src id sprintf (state->generic_talker_alias[0], "%s", str_b);
//just depends on reception and if VCALL is decoded before Alias, this will lead to alias being assigned sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", str_b);
//to incorrect src values or placement in the ncurses terminal's call history section
if (state->nxdn_last_rid != 0)
sprintf (state->str50a, "%s", str_b);
} }
@@ -1258,14 +1295,64 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
//check the rkey array for a scrambler key value //if using the keyloader, then check for a key value first by the key id,
//check by keyid first, then by tgt id //and then if not available, check by the destination (TG) id value
//TGT ID and Key ID could clash though if csv or system has both with different keys //also, for DES and AES, set the nxdn_key varialbe to the DestID for IV and KS gen
if (state->keyloader == 1)
{
//if Scrambler Key (and not running NXDN96 since that has VCALL in the non-voice frames)
//NOTE: The scrambler seed carries on the state->R variable so that will reset incorrectly on NXDN96
//NOTE: Observed on system with scrambler and AES keys on same TG, disabling loading DES and AES key by DestID
if (CipherType == 1 && opts->frame_nxdn48 == 1 && opts->frame_nxdn96 == 0)
{
if (state->rkey_array[KeyID] != 0) state->R = state->rkey_array[KeyID]; if (state->rkey_array[KeyID] != 0) state->R = state->rkey_array[KeyID];
else if (state->rkey_array[DestinationID] != 0) state->R = state->rkey_array[DestinationID]; else if (state->rkey_array[DestinationID] != 0) state->R = state->rkey_array[DestinationID];
}
//Don't zero key if no keyloader //if DES Key
if (CipherType != 0x1 && state->keyloader == 1) state->R = 0; else if (CipherType == 2)
{
if (state->rkey_array[KeyID] != 0) state->R = state->rkey_array[KeyID];
// else if (state->rkey_array[DestinationID] != 0)
// {
// state->R = state->rkey_array[DestinationID];
// state->nxdn_key = DestinationID;
// }
}
//if AES Key
else if (CipherType == 3)
{
uint32_t kidx = 0;
if (state->rkey_array[KeyID] != 0) kidx = KeyID;
// else if (state->rkey_array[DestinationID] != 0)
// {
// kidx = DestinationID;
// state->nxdn_key = DestinationID;
// }
state->A1[0] = state->rkey_array[kidx+0x000];
state->A2[0] = state->rkey_array[kidx+0x101];
state->A3[0] = state->rkey_array[kidx+0x201];
state->A4[0] = state->rkey_array[kidx+0x301];
//check to see if there is a value loaded or not
if (state->A1[0] == 0 && state->A2[0] == 0 && state->A3[0] == 0 && state->A4[0] == 0)
state->aes_key_loaded[0] = 0;
else state->aes_key_loaded[0] = 1;
for (int i = 0; i < 8; i++)
{
state->aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
}
state->R = state->A1[0]; //display KS stub
}
} //end state->keyloader == 1
/* Print the "Cipher Type" */ /* Print the "Cipher Type" */
if(CipherType != 0 && MessageType == 0x1) if(CipherType != 0 && MessageType == 0x1)
@@ -1289,21 +1376,41 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
if (CipherType == 0x02 && state->R > 0) //DES key value
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, "Value: %016llX", state->R);
fprintf (stderr, "%s", KNRM);
}
if (CipherType == 0x03 && state->R > 0) //AES key stub
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, "KS: %016llX", state->R);
fprintf (stderr, "%s", KNRM);
}
//only grab if VCALL //only grab if VCALL
if(MessageType == 0x1) if(MessageType == 0x1)
{ {
//only assign rid if not spare and not reserved (happens on private calls, unsure of its significance) //only assign rid if not spare and not reserved (happens on private calls, unsure of its significance)
if ( (VoiceCallOption & 0xF) < 4) //ideally, only want 0, 2, or 3 if ( (VoiceCallOption & 0xF) < 4) //ideally, only want 0, 1, 2, 3, or 4 (or 6, 7 on telephone calls)
state->nxdn_last_rid = SourceUnitID; state->nxdn_last_rid = SourceUnitID;
state->nxdn_last_tg = DestinationID; state->nxdn_last_tg = DestinationID;
state->nxdn_key = KeyID; state->nxdn_key = KeyID;
if (CallType == 0 || CallType == 1) //broadcast and group
state->gi[0] = 0;
else if (CallType == 4) //private
state->gi[0] = 1;
else state->gi[0] = -1; //unassigned on any other values
state->nxdn_cipher_type = CipherType; state->nxdn_cipher_type = CipherType;
} }
else else
{ {
state->nxdn_last_rid = 0; state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0; state->nxdn_last_tg = 0;
sprintf (state->str50a, "%s", ""); state->gi[0] = -1;
sprintf (state->generic_talker_alias[0], "%s", "");
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment)); memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
} }
@@ -1348,6 +1455,13 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
state->group_tally++; state->group_tally++;
} }
//run a watchdog here so we can update this with the crypto variables and ENC LO
if (DestinationID != 0 && lo == 0)
{
sprintf (state->event_history_s[0].Event_History_Items[0].internal_str, "Target: %d; has been locked out; Encryption Lock Out Enabled.", DestinationID);
watchdog_event_current(opts, state, 0);
}
//Craft a fake DISC Message send it to return to CC //Craft a fake DISC Message send it to return to CC
uint8_t dbits[96]; memset (dbits, 0, sizeof(dbits)); dbits[3] = 1; dbits[7] = 1; //DISC = 0x11; uint8_t dbits[96]; memset (dbits, 0, sizeof(dbits)); dbits[3] = 1; dbits[7] = 1; //DISC = 0x11;
if ( (strcmp(gm, "DE") == 0) && (strcmp(gn, "ENC LO") == 0) ) if ( (strcmp(gm, "DE") == 0) && (strcmp(gn, "ENC LO") == 0) )
@@ -1373,11 +1487,55 @@ void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message)
//At this point, I would assume an LFSR function is needed to expand the 22-bit IV collected here into a 64-bit IV, or 128-bit IV //At this point, I would assume an LFSR function is needed to expand the 22-bit IV collected here into a 64-bit IV, or 128-bit IV
state->payload_miN = IV; state->payload_miN = IV;
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 2;
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
fprintf (stderr, "\n VCALL_IV: %016llX", state->payload_miN); fprintf (stderr, "\n VCALL_IV: %016llX", state->payload_miN);
if ( ((state->nxdn_cipher_type == 0x02) || (state->nxdn_cipher_type == 0x03)) )
{
if (state->nxdn_cipher_type == 0x03 && state->keyloader == 1)
{
state->A1[0] = state->rkey_array[state->nxdn_key+0x000];
state->A2[0] = state->rkey_array[state->nxdn_key+0x101];
state->A3[0] = state->rkey_array[state->nxdn_key+0x201];
state->A4[0] = state->rkey_array[state->nxdn_key+0x301];
//check to see if there is a value loaded or not
if (state->A1[0] == 0 && state->A2[0] == 0 && state->A3[0] == 0 && state->A4[0] == 0)
state->aes_key_loaded[0] = 0;
else state->aes_key_loaded[0] = 1;
for (int i = 0; i < 8; i++)
{
state->aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
}
state->R = state->A1[0]; //display KS stub
}
//expand the IV into 128-bit
if (state->nxdn_cipher_type == 0x03)
LFSR128n(state);
if (state->nxdn_cipher_type == 0x02 && state->keyloader == 1)
{
if (state->rkey_array[state->nxdn_key] != 0)
state->R = state->rkey_array[state->nxdn_key];
}
//signal a new IV is available, but only when the other checks pass
if (state->nxdn_cipher_type == 0x02 && state->R != 0)
state->nxdn_new_iv = 1;
if (state->nxdn_cipher_type == 0x03 && state->aes_key_loaded[0] == 1)
state->nxdn_new_iv = 1;
}
} /* End NXDN_decode_VCALL_IV() */ } /* End NXDN_decode_VCALL_IV() */
//SCCH messages have a unique format that can be used in a super frame, but each 'unit' //SCCH messages have a unique format that can be used in a super frame, but each 'unit'
@@ -1602,7 +1760,7 @@ void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uin
//check the rkey array for a scrambler key value //check the rkey array for a scrambler key value
//TGT ID and Key ID could clash though if csv or system has both with different keys //TGT ID and Key ID could clash though if csv or system has both with different keys
if (state->rkey_array[id] != 0) state->R = state->rkey_array[id]; if (state->rkey_array[id] != 0) state->R = state->rkey_array[id];
if (state->M == 1) state->nxdn_cipher_type = 0x1; if (state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1;
} }
//rtl //rtl
else if (opts->audio_in_type == 3) else if (opts->audio_in_type == 3)
@@ -1621,7 +1779,7 @@ void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uin
//check the rkey array for a scrambler key value //check the rkey array for a scrambler key value
//TGT ID and Key ID could clash though if csv or system has both with different keys //TGT ID and Key ID could clash though if csv or system has both with different keys
if (state->rkey_array[id] != 0) state->R = state->rkey_array[id]; if (state->rkey_array[id] != 0) state->R = state->rkey_array[id];
if (state->M == 1) state->nxdn_cipher_type = 0x1; if (state->forced_alg_id == 1) state->nxdn_cipher_type = 0x1;
#endif #endif
} }
@@ -1716,6 +1874,335 @@ void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uin
} }
//ARIB STD-B54 Messages
//ARIB STD-B54 Page 22 - 選択呼出音声通信 and 選択呼出終話 (Selective Call Info and Call End)
void NXDN_decode_VCALL_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
//MFID is the only difference, this is added, the rest are shifted 8 bits down
uint8_t mfid = 0;
uint8_t CCOption = 0;
uint8_t CallType = 0;
uint8_t VoiceCallOption = 0;
uint16_t SourceUnitID = 0;
uint16_t DestinationID = 0;
uint8_t CipherType = 0;
uint8_t KeyID = 0;
uint8_t DuplexMode[32] = {0};
uint8_t TransmissionMode[32] = {0};
uint8_t MessageType;
/* Get the "Message Type" field */
MessageType = (Message[2] & 1) << 5;
MessageType |= (Message[3] & 1) << 4;
MessageType |= (Message[4] & 1) << 3;
MessageType |= (Message[5] & 1) << 2;
MessageType |= (Message[6] & 1) << 1;
MessageType |= (Message[7] & 1) << 0;
if (MessageType == 0x21) fprintf (stderr, "%s", KGRN); //VCALL
else if (MessageType == 0x28) fprintf (stderr, "%s", KYEL); //TX_REL
mfid = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
/* Decode "CC Option" */
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[16], 8);
state->NxdnElementsContent.CCOption = CCOption;
/* Decode "Call Type" */
CallType = (uint8_t)ConvertBitIntoBytes(&Message[24], 3);
state->NxdnElementsContent.CallType = CallType;
/* Decode "Voice Call Option" */
VoiceCallOption = (uint8_t)ConvertBitIntoBytes(&Message[27], 5);
state->NxdnElementsContent.VoiceCallOption = VoiceCallOption;
/* Decode "Source Unit ID" */
SourceUnitID = (uint16_t)ConvertBitIntoBytes(&Message[32], 16);
state->NxdnElementsContent.SourceUnitID = SourceUnitID;
/* Decode "Destination ID" */
DestinationID = (uint16_t)ConvertBitIntoBytes(&Message[48], 16);
state->NxdnElementsContent.DestinationID = DestinationID;
/* Decode the "Cipher Type" */
CipherType = (uint8_t)ConvertBitIntoBytes(&Message[64], 2);
state->NxdnElementsContent.CipherType = CipherType;
/* Decode the "Key ID" */
KeyID = (uint8_t)ConvertBitIntoBytes(&Message[66], 6);
state->NxdnElementsContent.KeyID = KeyID;
fprintf (stderr, "\n MFID: %02X; ", mfid);
/* Print the "CC Option" */
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
if(CCOption & 0x20) fprintf(stderr, "Priority Paging ");
//Call String for Per Call WAV File
sprintf (state->call_string[0], "%s", NXDN_Call_Type_To_Str(CallType));
if (CCOption & 0x80) strcat (state->call_string[0], " Emergency");
if (CipherType) strcat (state->call_string[0], " Enc");
if((CipherType == 2) || (CipherType == 3))
{
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 1;
state->NxdnElementsContent.PartOfNextEncryptedFrame = 2;
}
else
{
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 1;
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
}
/* Print the "Call Type" */
fprintf (stderr, "%s - ", NXDN_Call_Type_To_Str(CallType));
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
/* Print the "Voice Call Option" */
if (MessageType == 0x21) NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
if (MessageType == 0x21) fprintf(stderr, "%s %s (%02X) - ", DuplexMode, TransmissionMode, VoiceCallOption);
else if (MessageType == 0x28) fprintf (stderr, " Transmission Release - "); //TX_REL
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
fprintf (stderr, "%s", KNRM);
//if using the keyloader, then check for a key value first by the key id,
//and then if not available, check by the destination (TG) id value
//also, for DES and AES, set the nxdn_key varialbe to the DestID for IV and KS gen
if (state->keyloader == 1)
{
//if Scrambler Key (and not running NXDN96 since that has VCALL in the non-voice frames)
//NOTE: The scrambler seed carries on the state->R variable so that will reset incorrectly on NXDN96
//NOTE: Observed on system with scrambler and AES keys on same TG, disabling loading DES and AES key by DestID
if (CipherType == 1 && opts->frame_nxdn48 == 1 && opts->frame_nxdn96 == 0)
{
if (state->rkey_array[KeyID] != 0) state->R = state->rkey_array[KeyID];
else if (state->rkey_array[DestinationID] != 0) state->R = state->rkey_array[DestinationID];
}
//if DES Key
else if (CipherType == 2)
{
if (state->rkey_array[KeyID] != 0) state->R = state->rkey_array[KeyID];
// else if (state->rkey_array[DestinationID] != 0)
// {
// state->R = state->rkey_array[DestinationID];
// state->nxdn_key = DestinationID;
// }
}
//if AES Key
else if (CipherType == 3)
{
uint32_t kidx = 0;
if (state->rkey_array[KeyID] != 0) kidx = KeyID;
// else if (state->rkey_array[DestinationID] != 0)
// {
// kidx = DestinationID;
// state->nxdn_key = DestinationID;
// }
state->A1[0] = state->rkey_array[kidx+0x000];
state->A2[0] = state->rkey_array[kidx+0x101];
state->A3[0] = state->rkey_array[kidx+0x201];
state->A4[0] = state->rkey_array[kidx+0x301];
//check to see if there is a value loaded or not
if (state->A1[0] == 0 && state->A2[0] == 0 && state->A3[0] == 0 && state->A4[0] == 0)
state->aes_key_loaded[0] = 0;
else state->aes_key_loaded[0] = 1;
for (int i = 0; i < 8; i++)
{
state->aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
state->aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
}
state->R = state->A1[0]; //display KS stub
}
} //end state->keyloader == 1
/* Print the "Cipher Type" */
if(CipherType != 0 && MessageType == 0x21)
{
fprintf (stderr, "\n %s", KYEL);
fprintf(stderr, "%s - ", NXDN_Cipher_Type_To_Str(CipherType));
}
/* Print the Key ID */
if(CipherType != 0 && MessageType == 0x21)
{
fprintf(stderr, "Key ID %u - ", KeyID & 0xFF);
fprintf (stderr, "%s", KNRM);
}
if (CipherType == 0x01 && state->R > 0) //scrambler key value
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, "Value: %05lld", state->R);
fprintf (stderr, "%s", KNRM);
}
if (CipherType == 0x02 && state->R > 0) //DES key value
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, "Value: %016llX", state->R);
fprintf (stderr, "%s", KNRM);
}
if (CipherType == 0x03 && state->R > 0) //AES key stub
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, "KS: %016llX", state->R);
fprintf (stderr, "%s", KNRM);
}
//only grab if VCALL
if(MessageType == 0x21)
{
//only assign rid if not spare and not reserved (happens on private calls, unsure of its significance)
if ( (VoiceCallOption & 0xF) < 4) //ideally, only want 0, 1, 2, 3, or 4 (or 6, 7 on telephone calls)
state->nxdn_last_rid = SourceUnitID;
state->nxdn_last_tg = DestinationID;
state->nxdn_key = KeyID;
if (CallType == 0 || CallType == 1) //broadcast and group
state->gi[0] = 0;
else if (CallType == 4) //private
state->gi[0] = 1;
else state->gi[0] = -1; //unassigned on any other values
state->nxdn_cipher_type = CipherType;
}
else
{
state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0;
state->gi[0] = -1;
sprintf (state->generic_talker_alias[0], "%s", "");
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
}
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
if (state->nxdn_cipher_type != 0)
{
state->dmr_encL = 1;
}
if (state->nxdn_cipher_type == 0 || state->R != 0)
{
state->dmr_encL = 0;
}
} /* End NXDN_decode_VCALL_ARIB() */
void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint8_t mfid = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
//ARIB STD-B54 (p171-172) describes this as always having 3 segments
//but this is also signalled, so perhaps it can be variable
uint8_t seg_num = (uint8_t)ConvertBitIntoBytes(&Message[16], 4);
uint8_t seg_len = (uint8_t)ConvertBitIntoBytes(&Message[20], 4);
uint8_t seg_bytes[12]; memset(seg_bytes, 0, sizeof(seg_bytes));
int seg_byte_num = 6;
for (int i = 0; i < seg_byte_num; i++)
seg_bytes[i] = (uint8_t)ConvertBitIntoBytes(&Message[(i*8)+24], 8);
if (opts->payload == 1)
{
fprintf (stderr, "\n Multi Segment Alias %d/%d; ", seg_num, seg_len);
for (int i = 0; i < seg_byte_num; i++)
fprintf (stderr, "%02X", seg_bytes[i]);
}
else fprintf (stderr, " %d/%d; ", seg_num, seg_len);
//copy to PDU superframe
memcpy(state->dmr_pdu_sf[0]+(seg_num-1)*seg_byte_num, seg_bytes, seg_byte_num*sizeof(uint8_t));
int len = seg_byte_num * seg_len;
//TODO: Do a CRC32 Check, currently relying on each SACCH frame CRC instead
//extract CRC32
uint32_t crc_ext = (state->dmr_pdu_sf[0][len-4] << 24) | (state->dmr_pdu_sf[0][len-3] << 16) |
(state->dmr_pdu_sf[0][len-2] << 8) | state->dmr_pdu_sf[0][len-1];
//remove CRC32 bytes
if (len > 4)
len -= 4;
if (seg_num == seg_len)
{
if (opts->payload == 1)
{
fprintf (stderr, "\n Completed Alias Encoded: ");
for (int i = 0; i < (seg_len*seg_byte_num); i++)
fprintf (stderr, "%02X", state->dmr_pdu_sf[0][i]);
}
//Alias String
char alias[500]; memset(alias, 0, sizeof(alias));
int ptr = 0; //pointer to position in alias buffer
int k = 0; //pointer to position in completed message
uint16_t sjis = 0; //SHIFT-JIS character
uint8_t utf8 = 0; //UTF-8 Character
uint32_t c16 = 0; //Unicode Character returned from sjis_char_to_unicode
//SHIFT-JIS to UNICODE Conversion
setlocale(LC_ALL, ""); //needed when encoded alias contains Japanese (or probably any non-roman charset that isn't default on users terminal)
if (opts->payload == 1)
fprintf (stderr, "\n MFID: %02X; Encoded Len: %d; CRC: %04X; Alias: ", mfid, len, crc_ext);
for (int i = 0; i < len; i++)
{
c16 = 0;
utf8 = state->dmr_pdu_sf[0][k];
sjis = (state->dmr_pdu_sf[0][k] << 8) | state->dmr_pdu_sf[0][k+1];
if (utf8 >= 0x20 && utf8 < 0x7F)
{
c16 = sjis_char_to_unicode(utf8);
fprintf (stderr, "%lc", c16);
k++;
}
else if (sjis != 0)
{
c16 = sjis_char_to_unicode(sjis);
fprintf (stderr, "%lc", c16);
k+=2;
}
else if (utf8 == 0 && sjis == 0)
break;
//Encode Unicode to UTF-8 if not a pass-through ASCII control character or 0
if (c16 >= 0x0020)
ptr += utf8_encode(c16, alias+ptr);
}
//terminate string failsafe
alias[ptr++] = 0x00;
alias[ptr++] = 0x00;
sprintf (state->generic_talker_alias[0], "%s", alias);
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", alias);
//reset PDU superframe afterwards
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
}
} /* End NXDN_decode_ALIAS_ARIB() */
char * NXDN_Call_Type_To_Str(uint8_t CallType) char * NXDN_Call_Type_To_Str(uint8_t CallType)
{ {

View File

@@ -25,7 +25,8 @@
#include "dsd.h" #include "dsd.h"
#include "nxdn_const.h" #include "nxdn_const.h"
#include <assert.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)
void nxdn_frame (dsd_opts * opts, dsd_state * state) void nxdn_frame (dsd_opts * opts, dsd_state * state)
{ {
@@ -49,6 +50,10 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
int facch3 = 0; int facch3 = 0;
int udch2 = 0; int udch2 = 0;
//DCR Mode Specific Things
int sacch2 = 0;
int pich_tch = 0;
//new breakdown of lich codes //new breakdown of lich codes
uint8_t lich_rf = 0; //RF Channel Type uint8_t lich_rf = 0; //RF Channel Type
uint8_t lich_fc = 0; //Functional Channel Type uint8_t lich_fc = 0; //Functional Channel Type
@@ -85,18 +90,54 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//collect lich bits first, if they are good, then we can collect the rest of them //collect lich bits first, if they are good, then we can collect the rest of them
for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state); for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state);
nxdn_descramble (lich_dibits, 8); nxdn_pn95_dibit_scrambler (state, lich_dibits, 8);
lich = 0; lich = 0;
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i); for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
//debug lich as a 16-bit value (with encoding "dividing")
uint8_t lich_bits[16]; memset(lich_bits, 0, sizeof(lich_bits));
for (int i=0; i<8; i++)
{
lich_bits[(i*2)+0] = (lich_dibits[i] >> 1) & 1;
lich_bits[(i*2)+1] = (lich_dibits[i] >> 0) & 1;
}
uint16_t lich_bits_hex = (uint16_t)ConvertBitIntoBytes(lich_bits, 16);
UNUSED(lich_bits_hex);
//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;
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
{
#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
uint8_t lich_full = lich;
lich_parity_received = lich & 1; lich_parity_received = lich & 1;
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1; lich_parity_computed = ((lich_full >> 7) + (lich_full >> 6) + (lich_full >> 5) + (lich_full >> 4)) & 1;
lich = lich >> 1; lich = lich_full >> 1;
//special cases on DCR where parity is computed over 7 bits, and not 4 bits
if (lich == 0x08 || lich == 0x4A || lich == 0x48 || lich == 0x46)
lich_parity_computed = ((lich_full >> 7) + (lich_full >> 6) + (lich_full >> 5) + (lich_full >> 4) + (lich_full >> 3) + (lich_full >> 2) + (lich_full >> 1)) & 1;
if (lich_parity_received != lich_parity_computed) if (lich_parity_received != lich_parity_computed)
{ {
if (opts->payload == 1) fprintf(stderr, " Lich Parity Error %02X\n", lich); #ifdef NXDN_DEBUG_LICH
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly if (opts->payload == 1)
fprintf(stderr, " Lich Parity Error %02X / %04X\n", lich_full, lich_bits_hex);
#endif
state->lastsynctype = -1;
goto END; goto END;
} }
@@ -110,7 +151,10 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//all inbound lich are even value (lsb is set to 0 for inbound direction) //all inbound lich are even value (lsb is set to 0 for inbound direction)
if (lich % 2 == 0 && opts->p25_trunk == 1) if (lich % 2 == 0 && opts->p25_trunk == 1)
{ {
if (opts->payload == 1) fprintf(stderr, " Simplex/Inbound NXDN lich on trunking system - type 0x%02X\n", lich); #ifdef NXDN_DEBUG_LICH
if (opts->payload == 1)
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 state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
goto END; goto END;
} }
@@ -122,7 +166,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
break; break;
case 0x28: //facch2 types case 0x28: //facch2 types
case 0x29: case 0x29:
case 0x48: // case 0x48: //removing from here, moving to DCR as pich_tch
case 0x49: case 0x49:
facch2 = 1; facch2 = 1;
break; break;
@@ -173,6 +217,23 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
sacch = 1; sacch = 1;
break; break;
//DCR Voice
case 0x46:
voice = 3;
sacch2 = 1;
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;
//NXDN "Type-D" or "IDAS" Specific Lich Codes //NXDN "Type-D" or "IDAS" Specific Lich Codes
case 0x76: //normal vch voice (in one and two) case 0x76: //normal vch voice (in one and two)
case 0x77: case 0x77:
@@ -180,8 +241,8 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
scch = 1; scch = 1;
voice = 3; voice = 3;
break; break;
case 0x74: //vch in 1, facch1 in 2 (facch 2 steal) // case 0x74: //False Positive on DCR, keep disabled, or revert this line
case 0x75: case 0x75: //vch in 1, facch1 in 2 (facch 2 steal)
idas = 1; idas = 1;
scch = 1; scch = 1;
voice = 1; voice = 1;
@@ -226,7 +287,10 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
break; break;
default: default:
if (opts->payload == 1) fprintf(stderr, " false sync or unsupported NXDN lich type 0x%02X\n", lich); #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 //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_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
@@ -247,8 +311,21 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
{ {
printFrameSync (opts, state, "IDAS D ", 0, "-"); printFrameSync (opts, state, "IDAS D ", 0, "-");
} }
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1) if (opts->payload == 1)
fprintf (stderr, "L%02X - ", lich); fprintf (stderr, "L: %02X / LH: %04X; ", lich, lich_bits_hex);
#endif
}
else if (sacch2)
{
if (opts->frame_nxdn48 == 1)
{
printFrameSync (opts, state, "JPN DCR", 0, "-");
}
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1)
fprintf (stderr, "L: %02X / LH: %04X; ", lich, lich_bits_hex);
#endif
} }
else if (voice || facch || sacch || facch2 || udch || cac) else if (voice || facch || sacch || facch2 || udch || cac)
{ {
@@ -257,8 +334,10 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
printFrameSync (opts, state, "NXDN48 ", 0, "-"); printFrameSync (opts, state, "NXDN48 ", 0, "-");
} }
else printFrameSync (opts, state, "NXDN96 ", 0, "-"); else printFrameSync (opts, state, "NXDN96 ", 0, "-");
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1) if (opts->payload == 1)
fprintf (stderr, "L%02X - ", lich); fprintf (stderr, "L: %02X / LH: %04X; ", lich, lich_bits_hex);
#endif
} }
//now that we have a good LICH, we can collect all of our dibits //now that we have a good LICH, we can collect all of our dibits
@@ -268,7 +347,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
dbuf[i+8] = getDibit(opts, state); 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 //seperate our dbuf (dibit_buffer) into individual bit array
for (int i = 0; i < 182; i++) for (int i = 0; i < 182; i++)
@@ -319,6 +398,9 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
else direction = 1; else direction = 1;
// RF Channel Type // RF Channel Type
if (sacch2 == 0)
{
if (lich_rf == 0) fprintf (stderr, "RCCH "); if (lich_rf == 0) fprintf (stderr, "RCCH ");
else if (lich_rf == 1) fprintf (stderr, "RTCH "); else if (lich_rf == 1) fprintf (stderr, "RTCH ");
else if (lich_rf == 2) fprintf (stderr, "RDCH "); else if (lich_rf == 2) fprintf (stderr, "RDCH ");
@@ -328,6 +410,8 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
else fprintf (stderr, "RTCH2 "); else fprintf (stderr, "RTCH2 ");
} }
}
// Functional Channel Type -- things start to get really convoluted here // Functional Channel Type -- things start to get really convoluted here
// These will echo when handled, either with the decoded message type, or relevant crc err // These will echo when handled, either with the decoded message type, or relevant crc err
// if (lich_rf == 0) //CAC Type // if (lich_rf == 0) //CAC Type
@@ -385,7 +469,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (state->rkey_array[limazulu] != 0) if (state->rkey_array[limazulu] != 0)
state->R = state->rkey_array[limazulu]; 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 //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 //a little longer without affecting normal scan time on trunk_hangtime variable
@@ -401,19 +485,19 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (voice && !facch) //voice only, no facch steal if (voice && !facch) //voice only, no facch steal
{ {
fprintf (stderr, "%s", KGRN); fprintf (stderr, "%s", KGRN);
fprintf (stderr, " Voice "); fprintf (stderr, "Voice ");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
else if (voice && facch) //voice with facch1 steal else if (voice && facch) //voice with facch1 steal
{ {
fprintf (stderr, "%s", KGRN); fprintf (stderr, "%s", KGRN);
fprintf (stderr, " V%d+F%d ", 3 - facch, facch); //print which position on each fprintf (stderr, "V%d+F%d ", 3 - facch, facch); //print which position on each
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
else //Covers FACCH1 in both, FACCH2, UDCH, UDCH2, CAC else //Covers FACCH1 in both, FACCH2, UDCH, UDCH2, CAC
{ {
fprintf (stderr, "%s", KCYN); fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Data "); fprintf (stderr, "Data ");
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
//roll the voice scrambler LFSR here if key available to advance seed (usually just needed on NXDN96) //roll the voice scrambler LFSR here if key available to advance seed (usually just needed on NXDN96)
@@ -432,12 +516,16 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
} }
} }
//correct the bit counter if NXDN96 Data Frames (or double FACCH1 steal)
if (state->nxdn_cipher_type == 0x2 || state->nxdn_cipher_type == 0x3)
state->bit_counterL += (49*4);
} }
if (voice && facch == 1) //facch steal 1 -- before voice if (voice && facch == 1) //facch steal 1 -- before voice
{ {
//force scrambler here, but with unspecified key (just use what's loaded) //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 //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) if (state->nxdn_cipher_type == 0x1 && state->R != 0)
{ {
@@ -453,6 +541,10 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
LFSRN(ambe_temp, ambe_d, state); LFSRN(ambe_temp, ambe_d, state);
} }
} }
//correct the bit counter if FACCH1 steal)
if (state->nxdn_cipher_type == 0x2 || state->nxdn_cipher_type == 0x3)
state->bit_counterL += 49*2;
} }
if (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) state->nxdn_sacch_non_superframe = TRUE; if (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) state->nxdn_sacch_non_superframe = TRUE;
@@ -471,9 +563,10 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0); if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1); if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
//SHOULD be okay to run facch1's again on steal frames, will need testing //DCR
// if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a); if (sacch2) nxdn_deperm_sacch2(opts, state, sacch_bits);
// if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b); if (pich_tch & 1) nxdn_deperm_pich_tch(opts, state, facch_bits_a, lich);
if (pich_tch & 2) nxdn_deperm_pich_tch(opts, state, facch_bits_b, lich);
//only run facch in second slot if its not equal to the first one //only run facch in second slot if its not equal to the first one
//ideally, this would work better AFTER decoding/FEC //ideally, this would work better AFTER decoding/FEC
@@ -491,7 +584,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//update last voice sync time //update last voice sync time
state->last_vc_sync_time = time(NULL); state->last_vc_sync_time = time(NULL);
//turn on scrambler if forced by user option //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 //process voice frame
nxdn_voice (opts, state, voice, dbuf); nxdn_voice (opts, state, voice, dbuf);
} }
@@ -525,10 +618,21 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
LFSRN(ambe_temp, ambe_d, state); LFSRN(ambe_temp, ambe_d, state);
} }
} }
//correct the bit counter if FACCH1 steal)
if (state->nxdn_cipher_type == 0x2 || state->nxdn_cipher_type == 0x3)
state->bit_counterL += 49*2;
} }
if (opts->payload == 1 && !voice) fprintf (stderr, "\n"); if (opts->payload == 1 && !voice) fprintf (stderr, "\n");
else if (opts->payload == 0) fprintf (stderr, "\n"); else if (opts->payload == 0) fprintf (stderr, "\n");
END: ; //do nothing END:
//if rejected sync, reset carrier and synctype as well
if (state->lastsynctype == -1)
{
state->carrier = 0;
state->synctype = -1;
}
} }

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 //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; if (channel == 0xFFFF) return 0;
//Note: Base Frequency is calculated as (Base Frequency) x (0.000005 MHz) from the IDEN_UP message. //Note: Base Frequency is calculated as (Base Frequency) x (0.000005 MHz) from the IDEN_UP message.

View File

@@ -64,10 +64,14 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24); uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
fprintf (stderr, " - Group %d Source %d", group, source); fprintf (stderr, " - Group %d Source %d", group, source);
UNUSED2(res, explicit); UNUSED2(res, explicit);
state->gi[0] = 0;
state->dmr_so = lc_svcopt; //test to make sure no random issues
//don't set this when zero, annoying blink occurs in ncurses //don't set this when zero, annoying blink occurs in ncurses
if (group != 0) state->lasttg = group; if (group != 0)
if (source != 0) state->lastsrc = source; state->lasttg = group;
// if (source != 0) //disable now with new event history, if same src next ptt, then it will capture all of them as individual event items
state->lastsrc = source;
sprintf (state->call_string[0], " Group "); sprintf (state->call_string[0], " Group ");
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency "); if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
@@ -83,8 +87,12 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
fprintf (stderr, " - Target %d Source %d", target, source); fprintf (stderr, " - Target %d Source %d", target, source);
//don't set this when zero, annoying blink occurs in ncurses //don't set this when zero, annoying blink occurs in ncurses
if (target != 0) state->lasttg = target; if (target != 0)
if (source != 0) state->lastsrc = source; state->lasttg = target;
// if (source != 0) //disable now with new event history, if same src next ptt, then it will capture all of them as individual event items
state->lastsrc = source;
state->gi[0] = 1;
state->dmr_so = lc_svcopt;
sprintf (state->call_string[0], " Private "); sprintf (state->call_string[0], " Private ");
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency "); if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
@@ -160,6 +168,7 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 24); uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 24);
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 24); uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 24);
fprintf (stderr, "TGT: %d; SRC: %d; ", target, src); fprintf (stderr, "TGT: %d; SRC: %d; ", target, src);
state->gi[0] = 1;
} }
else if (lc_format == 0x50) else if (lc_format == 0x50)
@@ -297,9 +306,22 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
{ {
uint32_t tgt = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24); //can be individual, or all units (0xFFFFFF) uint32_t tgt = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24); //can be individual, or all units (0xFFFFFF)
fprintf (stderr, " Call Termination; TGT: %d;", tgt); fprintf (stderr, " Call Termination; TGT: %d;", tgt);
memset (state->dmr_pdu_sf[0], 0, sizeof (state->dmr_pdu_sf[0])); //reset storage for any talker alias (or other items)
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1) 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 //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? //revert back to a phase 1 traffic channel or carry a phase 1 traffic channel?
if (state->p25_cc_is_tdma == 1) if (state->p25_cc_is_tdma == 1)
@@ -325,6 +347,7 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
{ {
state->lasttg = 0; state->lasttg = 0;
state->lastsrc = 0; state->lastsrc = 0;
state->gi[0] = -1;
state->payload_algid = 0; state->payload_algid = 0;
state->payload_keyid = 0; state->payload_keyid = 0;
// state->payload_miP = 0; // state->payload_miP = 0;
@@ -344,6 +367,7 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
#ifdef USE_RTLSDR #ifdef USE_RTLSDR
state->lasttg = 0; state->lasttg = 0;
state->lastsrc = 0; state->lastsrc = 0;
state->gi[0] = -1;
state->payload_algid = 0; state->payload_algid = 0;
state->payload_keyid = 0; state->payload_keyid = 0;
// state->payload_miP = 0; // state->payload_miP = 0;
@@ -431,6 +455,7 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
if (LCW_bits[31] == 1) fprintf (stderr, " EXT;"); //Full SUID next LC (external) (octet 3) if (LCW_bits[31] == 1) fprintf (stderr, " EXT;"); //Full SUID next LC (external) (octet 3)
state->lasttg = sg; state->lasttg = sg;
state->lastsrc = src; state->lastsrc = src;
state->gi[0] = 0;
} }
else if (lc_mfid == 0x90 && lc_opcode == 0x1) else if (lc_mfid == 0x90 && lc_opcode == 0x1)
@@ -441,6 +466,7 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
fprintf (stderr, " SG: %d; CH: %04X;", sg, ch); fprintf (stderr, " SG: %d; CH: %04X;", sg, ch);
if (LCW_bits[16] == 1) fprintf (stderr, " Res;"); //res bit (octet 2) if (LCW_bits[16] == 1) fprintf (stderr, " Res;"); //res bit (octet 2)
if (LCW_bits[17] == 1) fprintf (stderr, " ENC;"); //P-bit (octet 2) if (LCW_bits[17] == 1) fprintf (stderr, " ENC;"); //P-bit (octet 2)
state->gi[0] = 0;
} }
else if (lc_mfid == 0x90 && lc_opcode == 0x3) else if (lc_mfid == 0x90 && lc_opcode == 0x3)
@@ -455,6 +481,45 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
fprintf (stderr, " MFID90 (Moto) Talker EOT; SRC: %d;", src); fprintf (stderr, " MFID90 (Moto) Talker EOT; SRC: %d;", src);
} }
//look for these in logs
else if (lc_mfid == 0x90 && lc_opcode == 0x15)
{
fprintf (stderr, " MFID90 (Moto) Talker Alias Header");
apx_embedded_alias_header_phase1 (opts, state, 0, LCW_bits);
}
else if (lc_mfid == 0x90 && lc_opcode == 0x17)
{
fprintf (stderr, " MFID90 (Moto) Talker Alias Blocks");
apx_embedded_alias_blocks_phase1 (opts, state, 0, LCW_bits);
}
else if (lc_mfid == 0xA4 && lc_opcode > 0x31 && lc_opcode < 0x36)
{
fprintf (stderr, " MFIDA4 (Harris) Talker Alias Blocks");
l3h_embedded_alias_blocks_phase1 (opts, state, 0, LCW_bits);
}
//Harris GPS on Phase 1 tested and working
else if (lc_mfid == 0xA4 && lc_opcode == 0x2A)
{
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
}
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
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);
else fprintf (stderr, " Missing GPS Block 1");
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
}
//observed format value on Harris SNDCP data channel (Phase 2 CC to Phase 1 MPDU channel) //observed format value on Harris SNDCP data channel (Phase 2 CC to Phase 1 MPDU channel)
else if (lc_mfid == 0xA4 && lc_format == 0x0A) else if (lc_mfid == 0xA4 && lc_format == 0x0A)
{ {
@@ -466,12 +531,20 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
fprintf (stderr, " MFIDA4 (Harris) Data Channel; SRC: %d; TGT: %d;", src, tgt); fprintf (stderr, " MFIDA4 (Harris) Data Channel; SRC: %d; TGT: %d;", src, tgt);
} }
//observed on Tait conventional / uplink (TODO: Move all this to dsd_alias.c)
else if (lc_mfid == 0xD8 && lc_format == 0x00)
{
fprintf (stderr, " MFIDD8 (Tait) Talker Alias: ");
tait_iso7_embedded_alias_decode(opts, state, 0, 8, LCW_bits);
}
//not a duplicate, this one will print if not MFID 0 or 1 //not a duplicate, this one will print if not MFID 0 or 1
else else
{ {
fprintf (stderr, " Unknown Format %02X MFID %02X ", lc_format, lc_mfid); fprintf (stderr, " Unknown Format %02X MFID %02X ", lc_format, lc_mfid);
if (lc_mfid == 0x90) fprintf (stderr, "(Moto)"); if (lc_mfid == 0x90) fprintf (stderr, "(Moto)");
else if (lc_mfid == 0xA4) fprintf (stderr, "(Harris)"); else if (lc_mfid == 0xA4) fprintf (stderr, "(Harris)");
else if (lc_mfid == 0xD8) fprintf (stderr, "(Tait)");
} }
} }

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 SynError = 0; CantDecode = 0;
// 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.
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 * 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) unsigned char get_index(unsigned char x, unsigned char y)
{ {
return (x<<2) + y; return (x << 2) + y;
} }
public: public:
ParityTable() ParityTable()
{ {
for (unsigned int i=0; i<sizeof(table); i++) { for (unsigned int i = 0; i < sizeof(table); i++) {
table[i] = 0; table[i] = 0;
} }
table[get_index(1,1)] = 1; table[get_index(1,1)] = 1;
@@ -39,9 +160,8 @@ public:
unsigned char get_value(unsigned char x, unsigned char y) unsigned char get_value(unsigned char x, unsigned char y)
{ {
return table[get_index(x,y)]; return table[get_index(x, y)];
} }
} parity_table; } parity_table;
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity) 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; int result;
// Fill up with the given input // Fill up with the given input
bvec input(63); bit_vector codeword(63);
for(unsigned int i=0; i<63; i++) { for (unsigned int i = 0; i < 63; i++) {
input[i] = bch_code[i]; codeword[i] = bch_code[62-i];
} }
// Decode it // Decode it
bvec decoded, cw_isvalid; int errors = bchDec(codeword);
bool ok = bch.decode(input, decoded, cw_isvalid); bool ok = (errors >= 0);
if (!ok) { if (!ok) {
// Decode failed // Decode failed
// fprintf (stderr, "NID ERR: %d; ", errors);
result = 0; result = 0;
} else { } 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. // 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; int nac = 0;
for (int i=0; i<12; i++) { for (int i = 0; i < 12; i++) {
nac <<= 1; nac <<= 1;
nac |= (int)decoded[i]; nac |= (int)codeword[i];
} }
*new_nac = nac; *new_nac = nac;
// Take the fixed DUID from the encoded output. 4 bit value starting at position 12. // 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_0 = (((int)codeword[12]) << 1) + ((int)codeword[13]);
unsigned char new_duid_1 = (((int)decoded[14])<<1) + ((int)decoded[15]); unsigned char new_duid_1 = (((int)codeword[14]) << 1) + ((int)codeword[15]);
new_duid[0] = new_duid_0 + '0'; new_duid[0] = new_duid_0 + '0';
new_duid[1] = new_duid_1 + '0'; new_duid[1] = new_duid_1 + '0';
new_duid[2] = 0; // Null terminate new_duid[2] = 0; // Null terminate
@@ -82,8 +209,8 @@ 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); unsigned char expected_parity = parity_table.get_value(new_duid_0, new_duid_1);
if (expected_parity != parity) { if (expected_parity != parity) {
// Ignore, not sure what to do // Parity mismatch – optional handling
//fprintf (stderr, "Error in parity detected?"); // fprintf(stderr, "Error in parity detected?\n");
} }
result = 1; result = 1;

View File

@@ -215,6 +215,11 @@ correct_golay_dibits_6(char* corrected_hex_data, int hex_count, AnalogSignal* an
void void
processHDU(dsd_opts* opts, dsd_state* state) processHDU(dsd_opts* opts, dsd_state* state)
{ {
//push current slot to 0, just in case swapping p2 to p1
//or stale slot value from p2 and then decoding a pdu
state->currentslot = 0;
uint8_t mi[73]; uint8_t mi[73];
char mfid[9], algid[9], kid[17], tgid[17]; char mfid[9], algid[9], kid[17], tgid[17];
int i, j; int i, j;
@@ -473,11 +478,32 @@ processHDU(dsd_opts* opts, dsd_state* state)
fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%04X MI: 0x%08llX%08llX", algidhex, kidhex, mihex1, mihex2); fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%04X MI: 0x%08llX%08llX", algidhex, kidhex, mihex1, mihex2);
state->payload_algid = algidhex; state->payload_algid = algidhex;
state->payload_keyid = kidhex; state->payload_keyid = kidhex;
if (mihex3) fprintf (stderr, "-%02llX", mihex3);
if (state->R != 0 && state->payload_algid == 0xAA) if (state->R != 0 && state->payload_algid == 0xAA)
{ {
fprintf (stderr, " Key: %010llX", state->R); fprintf (stderr, " Key: %010llX", state->R);
opts->unmute_encrypted_p25 = 1; opts->unmute_encrypted_p25 = 1;
} }
else if (state->R != 0 && state->payload_algid == 0x81)
{
fprintf (stderr, " Key: %010llX", state->R);
opts->unmute_encrypted_p25 = 1;
}
else if (state->R != 0 && state->payload_algid == 0x9F)
{
fprintf (stderr, " Key: %010llX", state->R);
opts->unmute_encrypted_p25 = 1;
}
else if ( (state->payload_algid == 0x84 || state->payload_algid == 0x89) && state->aes_key_loaded[0] == 1)
{
fprintf (stderr, "\n ");
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "Key: %016llX %016llX ", state->A1[0], state->A2[0]);
if (state->payload_algid == 0x84)
fprintf (stderr, "%016llX %016llX", state->A3[0], state->A4[0]);
fprintf (stderr, "%s ", KNRM);
opts->unmute_encrypted_p25 = 1;
}
else if (state->payload_algid != 0 && state->payload_algid != 0x80) else if (state->payload_algid != 0 && state->payload_algid != 0x80)
{ {
//may want to mute this again, or may not want to //may want to mute this again, or may not want to
@@ -495,6 +521,17 @@ processHDU(dsd_opts* opts, dsd_state* state)
} }
fprintf (stderr, "\n"); fprintf (stderr, "\n");
//expand 64-bit MI to 128-bit for AES
if (state->payload_algid == 0x84 || state->payload_algid == 0x89)
{
LFSR128(state);
fprintf (stderr, "\n");
}
//xl, we need to know if the ESS is from HDU, or LDU2
state->xl_is_hdu = 1;
} }
else else
{ {

View File

@@ -26,6 +26,11 @@
void void
processLDU1 (dsd_opts* opts, dsd_state* state) processLDU1 (dsd_opts* opts, dsd_state* state)
{ {
//push current slot to 0, just in case swapping p2 to p1
//or stale slot value from p2 and then decoding a pdu
state->currentslot = 0;
// extracts IMBE frames from LDU frame // extracts IMBE frames from LDU frame
int i; int i;
uint8_t lcformat[9], mfid[9], lcinfo[57]; uint8_t lcformat[9], mfid[9], lcinfo[57];
@@ -49,6 +54,9 @@ processLDU1 (dsd_opts* opts, dsd_state* state)
// so we start counter at 36-14-1 = 21 // so we start counter at 36-14-1 = 21
status_count = 21; status_count = 21;
//set vc counter to 0 -- just playing it safe, shouldn't matter since rotating MIs are only in the LDU2 frame
state->p25vc = 0;
if (opts->errorbars == 1) if (opts->errorbars == 1)
{ {
//fprintf (stderr, "e:"); //fprintf (stderr, "e:");
@@ -253,6 +261,8 @@ processLDU1 (dsd_opts* opts, dsd_state* state)
// TODO: do something useful with the LSD bytes... <--THIS! // TODO: do something useful with the LSD bytes... <--THIS!
state->dropL += 2; //need to skip 2 here for the LSD bytes state->dropL += 2; //need to skip 2 here for the LSD bytes
//same for octet counter
state->octet_counter += 2;
} }
// IMBE 9 // IMBE 9
@@ -508,11 +518,11 @@ processLDU1 (dsd_opts* opts, dsd_state* state)
{ {
state->dmr_alias_format[0] = 0x02; state->dmr_alias_format[0] = 0x02;
if (lsd_hex2 > 8) lsd_hex2 = 8; //sanity check if (lsd_hex2 > 8) lsd_hex2 = 8; //sanity check
state->dmr_alias_len[0] = lsd_hex2; state->dmr_alias_block_len[0] = lsd_hex2;
state->data_block_counter[0] = 0; state->data_block_counter[0] = 0;
} }
if ( (k >= state->dmr_alias_len[0]) && (state->dmr_alias_format[0] == 0x02) ) if ( (k >= state->dmr_alias_block_len[0]) && (state->dmr_alias_format[0] == 0x02) )
{ {
//storage for completed string //storage for completed string
char str[16]; int wr = 0; int tsrc = state->lastsrc; int z = 0; k = 0; char str[16]; int wr = 0; int tsrc = state->lastsrc; int z = 0; k = 0;

View File

@@ -26,6 +26,11 @@
void void
processLDU2 (dsd_opts * opts, dsd_state * state) processLDU2 (dsd_opts * opts, dsd_state * state)
{ {
//push current slot to 0, just in case swapping p2 to p1
//or stale slot value from p2 and then decoding a pdu
state->currentslot = 0;
// extracts IMBE frames from LDU frame // extracts IMBE frames from LDU frame
int i; int i;
uint8_t mi[73]; uint8_t mi[73];
@@ -256,6 +261,9 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
// TODO: do something useful with the LSD bytes... <--THIS! // TODO: do something useful with the LSD bytes... <--THIS!
state->dropL += 2; //need to skip 2 here for the LSD bytes state->dropL += 2; //need to skip 2 here for the LSD bytes
//same for octet counter
state->octet_counter += 2;
} }
// IMBE 9 // IMBE 9
@@ -277,6 +285,8 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
//reset dropbytes -- skip first 11 for next LCW //reset dropbytes -- skip first 11 for next LCW
state->dropL = 267; state->dropL = 267;
//reset octet counter
state->octet_counter = 0;
if (opts->errorbars == 1) if (opts->errorbars == 1)
{ {
@@ -476,11 +486,38 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%04X MI: 0x%08llX%08llX", algidhex, kidhex, mihex1, mihex2); fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%04X MI: 0x%08llX%08llX", algidhex, kidhex, mihex1, mihex2);
state->payload_algid = algidhex; state->payload_algid = algidhex;
state->payload_keyid = kidhex; state->payload_keyid = kidhex;
if (mihex3) fprintf (stderr, "-%02llX", mihex3);
if (state->R != 0 && state->payload_algid == 0xAA) if (state->R != 0 && state->payload_algid == 0xAA)
{ {
fprintf (stderr, " Key: %010llX", state->R); fprintf (stderr, " Key: %010llX", state->R);
opts->unmute_encrypted_p25 = 1; opts->unmute_encrypted_p25 = 1;
} }
else if (state->R != 0 && state->payload_algid == 0x81)
{
fprintf (stderr, " Key: %010llX", state->R);
opts->unmute_encrypted_p25 = 1;
}
else if (state->R != 0 && state->payload_algid == 0x9F)
{
fprintf (stderr, " Key: %010llX", state->R);
opts->unmute_encrypted_p25 = 1;
}
/* //going to want to update this for OFB, CBC, and ECB
{ 0x84, "AES-256-OFB" },
{ 0x85, "AES-128-ECB"},
{ 0x88, "AES-CBC"},
{ 0x89, "AES-128-OFB"},
*/
else if ( (state->payload_algid == 0x84 || state->payload_algid == 0x89) && state->aes_key_loaded[0] == 1)
{
fprintf (stderr, "\n ");
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "Key: %016llX %016llX ", state->A1[0], state->A2[0]);
if (state->payload_algid == 0x84)
fprintf (stderr, "%016llX %016llX", state->A3[0], state->A4[0]);
fprintf (stderr, "%s ", KNRM);
opts->unmute_encrypted_p25 = 1;
}
else if (state->payload_algid != 0 && state->payload_algid != 0x80) else if (state->payload_algid != 0 && state->payload_algid != 0x80)
{ {
//may want to mute this again, or may not want to //may want to mute this again, or may not want to
@@ -542,11 +579,11 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
{ {
state->dmr_alias_format[0] = 0x02; state->dmr_alias_format[0] = 0x02;
if (lsd_hex2 > 8) lsd_hex2 = 8; //sanity check if (lsd_hex2 > 8) lsd_hex2 = 8; //sanity check
state->dmr_alias_len[0] = lsd_hex2; state->dmr_alias_block_len[0] = lsd_hex2;
state->data_block_counter[0] = 0; state->data_block_counter[0] = 0;
} }
if ( (k >= state->dmr_alias_len[0]) && (state->dmr_alias_format[0] == 0x02) ) if ( (k >= state->dmr_alias_block_len[0]) && (state->dmr_alias_format[0] == 0x02) )
{ {
//storage for completed string //storage for completed string
char str[16]; int wr = 0; int tsrc = state->lastsrc; int z = 0; k = 0; char str[16]; int wr = 0; int tsrc = state->lastsrc; int z = 0; k = 0;
@@ -611,7 +648,7 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
//reset values //reset values
state->dmr_alias_format[0] = 0; state->dmr_alias_format[0] = 0;
state->data_block_counter[0] = 0; state->data_block_counter[0] = 0;
state->dmr_alias_len[0] = 0; state->dmr_alias_block_len[0] = 0;
// memset (state->dmr_alias_block_segment, 0, sizeof(state->dmr_alias_block_segment)); // memset (state->dmr_alias_block_segment, 0, sizeof(state->dmr_alias_block_segment));
} }
#else #else
@@ -627,6 +664,15 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
fprintf (stderr, "\n"); fprintf (stderr, "\n");
} }
//expand 64-bit MI to 128-bit for AES
if (state->payload_algid == 0x84 || state->payload_algid == 0x89)
{
LFSR128(state);
fprintf (stderr, "\n");
}
//xl, we need to know if the ESS is from HDU, or LDU2
state->xl_is_hdu = 0;
#define P25p1_ENC_LO //disable if this behavior is detremental #define P25p1_ENC_LO //disable if this behavior is detremental
#ifdef P25p1_ENC_LO #ifdef P25p1_ENC_LO
@@ -661,6 +707,13 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
state->group_tally++; state->group_tally++;
} }
//run a watchdog here so we can update this with the crypto variables and ENC LO
if (ttg != 0 && enc_wr == 0) //
{
sprintf (state->event_history_s[0].Event_History_Items[0].internal_str, "Target: %d; has been locked out; Encryption Lock Out Enabled.", ttg);
watchdog_event_current(opts, state, 0);
}
//return to the control channel //return to the control channel
fprintf (stderr, " No Enc Following on P25p1 Trunking; Return to CC; \n"); fprintf (stderr, " No Enc Following on P25p1 Trunking; Return to CC; \n");
return_to_cc (opts, state); return_to_cc (opts, state);
@@ -698,8 +751,91 @@ void LFSRP(dsd_state * state)
//print current ENC identifiers already known and new calculated MI //print current ENC identifiers already known and new calculated MI
fprintf (stderr, "%s", KYEL); fprintf (stderr, "%s", KYEL);
if (state->currentslot == 0) if (state->currentslot == 0)
fprintf (stderr, " LDU2/ESS_B FEC ERR - ALG: 0x%02X KEY ID: 0x%04X LFSR MI: 0x%016llX", state->payload_algid, state->payload_keyid, state->payload_miP); fprintf (stderr, "\n LDU2/ESS_B FEC ERR - ALG: 0x%02X KEY ID: 0x%04X LFSR MI: 0x%016llX", state->payload_algid, state->payload_keyid, state->payload_miP);
if (state->currentslot == 1) if (state->currentslot == 1)
fprintf (stderr, " LDU2/ESS_B FEC ERR - ALG: 0x%02X KEY ID: 0x%04X LFSR MI: 0x%016llX", state->payload_algidR, state->payload_keyidR, state->payload_miN); fprintf (stderr, "\n LDU2/ESS_B FEC ERR - ALG: 0x%02X KEY ID: 0x%04X LFSR MI: 0x%016llX", state->payload_algidR, state->payload_keyidR, state->payload_miN);
fprintf (stderr, "%s", KNRM); fprintf (stderr, "%s", KNRM);
} }
void LFSR128(dsd_state * state)
{
//generate a 128-bit IV from a 64-bit IV for AES blocks
unsigned long long int lfsr = 0;
int slot = state->currentslot;
if (state->currentslot == 0)
lfsr = state->payload_miP;
else lfsr = state->payload_miN;
//start packing aes_iv
if (slot == 0)
{
state->aes_iv[0] = (lfsr >> 56) & 0xFF;
state->aes_iv[1] = (lfsr >> 48) & 0xFF;
state->aes_iv[2] = (lfsr >> 40) & 0xFF;
state->aes_iv[3] = (lfsr >> 32) & 0xFF;
state->aes_iv[4] = (lfsr >> 24) & 0xFF;
state->aes_iv[5] = (lfsr >> 16) & 0xFF;
state->aes_iv[6] = (lfsr >> 8 ) & 0xFF;
state->aes_iv[7] = (lfsr >> 0 ) & 0xFF;
}
if (slot == 1)
{
state->aes_ivR[0] = (lfsr >> 56) & 0xFF;
state->aes_ivR[1] = (lfsr >> 48) & 0xFF;
state->aes_ivR[2] = (lfsr >> 40) & 0xFF;
state->aes_ivR[3] = (lfsr >> 32) & 0xFF;
state->aes_ivR[4] = (lfsr >> 24) & 0xFF;
state->aes_ivR[5] = (lfsr >> 16) & 0xFF;
state->aes_ivR[6] = (lfsr >> 8 ) & 0xFF;
state->aes_ivR[7] = (lfsr >> 0 ) & 0xFF;
}
int cnt = 0; int x = 64;
unsigned long long int bit;
//polynomial P(x) = 1 + X15 + X27 + X38 + X46 + X62 + X64
for(cnt=0;cnt<64;cnt++)
{
//63,61,45,37,27,14
// Polynomial is C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
lfsr = (lfsr << 1) | bit;
//continue packing aes_iv
if (slot == 0)
state->aes_iv[x/8] = (state->aes_iv[x/8] << 1) + bit;
if (slot == 1)
state->aes_ivR[x/8] = (state->aes_ivR[x/8] << 1) + bit;
x++;
}
if (state->currentslot == 0)
{
fprintf (stderr, "%s", KYEL);
if (state->dmrburstL != 27) //if not LDU2
fprintf (stderr, "\n");
fprintf (stderr, " ");
fprintf (stderr, " ALG ID: 0x%02X KEY ID: 0x%04X MI(128): 0x", state->payload_algid, state->payload_keyid);
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", state->aes_iv[x]);
fprintf (stderr, "%s", KNRM);
// fprintf (stderr, "\n");
}
if (state->currentslot == 1)
{
fprintf (stderr, "%s", KYEL);
if (state->dmrburstL != 27) //if not LDU2, shouldn't matter on P25p2 for the second slot
fprintf (stderr, "\n");
fprintf (stderr, " ");
fprintf (stderr, " ALG ID: 0x%02X KEY ID: 0x%04X MI(128): 0x", state->payload_algidR, state->payload_keyidR);
for (x = 0; x < 16; x++)
fprintf (stderr, "%02X", state->aes_ivR[x]);
fprintf (stderr, "%s", KNRM);
// fprintf (stderr, "\n");
}
}

File diff suppressed because it is too large Load Diff

482
src/p25p1_pdu_data.c Normal file
View File

@@ -0,0 +1,482 @@
/*-------------------------------------------------------------------------------
* p25p1_pdu_data.c
* P25p1 PDU Data Decoding
*
* LWVMOBILE
* 2025-03 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
void p25_decode_rsp(uint8_t C, uint8_t T, uint8_t S, char * rsp_string)
{
if (C == 0) sprintf (rsp_string, " ACK (Success);");
else if (C == 2) sprintf (rsp_string, " SACK (Retry);");
else if (C == 1)
{
if (T == 0) sprintf (rsp_string, " NACK (Illegal Format);");
else if (T == 1) sprintf (rsp_string, " NACK (CRC32 Failure);");
else if (T == 2) sprintf (rsp_string, " NACK (Memory Full);");
else if (T == 3) sprintf (rsp_string, " NACK (FSN Sequence Error);");
else if (T == 4) sprintf (rsp_string, " NACK (Undeliverable);");
else if (T == 5) sprintf (rsp_string, " NACK (NS/VR Sequence Error);"); //depreciated
else if (T == 6) sprintf (rsp_string, " NACK (Invalid User on System);");
}
//catch all for everything else
else sprintf (rsp_string, " Unknown RSP;");
fprintf (stderr, " Response Packet:%s C: %X; T: %X; S: %X; ", rsp_string, C, T, S);
}
void p25_decode_sap(uint8_t SAP, char * sap_string)
{
if (SAP == 0) sprintf (sap_string, " User Data;");
else if (SAP == 1) sprintf (sap_string, " Encrypted User Data;");
else if (SAP == 2) sprintf (sap_string, " Circuit Data;");
else if (SAP == 3) sprintf (sap_string, " Circuit Data Control;");
else if (SAP == 4) sprintf (sap_string, " Packet Data;");
else if (SAP == 5) sprintf (sap_string, " Address Resolution Protocol;");
else if (SAP == 6) sprintf (sap_string, " SNDCP Packet Data Control;");
else if (SAP == 15) sprintf (sap_string, " Packet Data Scan Preamble;");
else if (SAP == 29) sprintf (sap_string, " Packet Data Encryption Support;");
else if (SAP == 31) sprintf (sap_string, " Extended Address;");
else if (SAP == 32) sprintf (sap_string, " Registration and Authorization;");
else if (SAP == 33) sprintf (sap_string, " Channel Reassignment;");
else if (SAP == 34) sprintf (sap_string, " System Configuration;");
else if (SAP == 35) sprintf (sap_string, " Mobile Radio Loopback;");
else if (SAP == 36) sprintf (sap_string, " Mobile Radio Statistics;");
else if (SAP == 37) sprintf (sap_string, " Mobile Radio Out of Service;");
else if (SAP == 38) sprintf (sap_string, " Mobile Radio Paging;");
else if (SAP == 39) sprintf (sap_string, " Mobile Radio Configuration;");
else if (SAP == 40) sprintf (sap_string, " Unencrypted Key Management;");
else if (SAP == 41) sprintf (sap_string, " Encrypted Key Management;");
else if (SAP == 48) sprintf (sap_string, " Location Service;");
else if (SAP == 61) sprintf (sap_string, " Trunking Control;");
else if (SAP == 63) sprintf (sap_string, " Encrypted Trunking Control;");
//catch all for everything else
else sprintf (sap_string, " Unknown SAP;");
fprintf (stderr, "SAP: 0x%02X;%s ", SAP, sap_string);
}
void lfsr_64_to_128(uint8_t * iv)
{
uint64_t lfsr = 0, bit = 0;
lfsr = ((uint64_t)iv[0] << 56ULL) + ((uint64_t)iv[1] << 48ULL) + ((uint64_t)iv[2] << 40ULL) + ((uint64_t)iv[3] << 32ULL) +
((uint64_t)iv[4] << 24ULL) + ((uint64_t)iv[5] << 16ULL) + ((uint64_t)iv[6] << 8ULL) + ((uint64_t)iv[7] << 0ULL);
uint8_t cnt = 0, x = 64;
for(cnt = 0;cnt < 64; cnt++)
{
//63,61,45,37,27,14
// Polynomial is C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
lfsr = (lfsr << 1) | bit;
//continue packing iv
iv[x/8] = (iv[x/8] << 1) + bit;
x++;
}
}
uint8_t p25_decrypt_pdu(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t alg_id, uint16_t key_id, unsigned long long int mi, int len)
{
UNUSED(opts);
uint8_t encrypted = 1;
int i = 0;
int ks_idx = 0;
uint8_t ks_bytes[3096]; memset (ks_bytes, 0, sizeof(ks_bytes));
//create keystream
if (alg_id == 0x84 || alg_id == 0x89)
{
//aes specific arrays and things
uint8_t aes_iv[16]; memset (aes_iv, 0, sizeof(aes_iv));
uint8_t aes_key[32]; memset (aes_key, 0, sizeof(aes_key));
uint8_t empt[64]; memset (empt, 0, sizeof(empt));
uint8_t akl = 0; //aes key loaded into array flag
unsigned long long int a1 = state->rkey_array[key_id+0x000];
unsigned long long int a2 = state->rkey_array[key_id+0x101];
unsigned long long int a3 = state->rkey_array[key_id+0x201];
unsigned long long int a4 = state->rkey_array[key_id+0x301];
//checkdown to see if anything in a1-a4
if ( (a1 == 0) && (a2 == 0) && (a3 == 0) && (a4 == 0) )
{
//try loading from state->H instead (could clash if keys loaded that trigger any a1-a4 above)
a1 = state->K1;
a2 = state->K2;
a3 = state->K3;
a4 = state->K4;
}
//loader for aes keys
for (uint64_t i = 0; i < 8; i++)
{
aes_key[i+0] = (a1 >> (56ULL-(i*8))) & 0xFF;
aes_key[i+8] = (a2 >> (56ULL-(i*8))) & 0xFF;
aes_key[i+16] = (a3 >> (56ULL-(i*8))) & 0xFF;
aes_key[i+24] = (a4 >> (56ULL-(i*8))) & 0xFF;
}
//check to see if a key is loaded into any part of the array
if (memcmp(aes_key, empt, sizeof(aes_key)) != 0) akl = 1;
else akl = 0;
//convert mi to aes_iv and expand it
aes_iv[0] = ((mi & 0xFF00000000000000) >> 56);
aes_iv[1] = ((mi & 0xFF000000000000) >> 48);
aes_iv[2] = ((mi & 0xFF0000000000) >> 40);
aes_iv[3] = ((mi & 0xFF00000000) >> 32);
aes_iv[4] = ((mi & 0xFF000000) >> 24);
aes_iv[5] = ((mi & 0xFF0000) >> 16);
aes_iv[6] = ((mi & 0xFF00) >> 8);
aes_iv[7] = ((mi & 0xFF) >> 0);
lfsr_64_to_128(aes_iv);
ks_idx = 16; //offset for OFB discard round
if (akl == 1)
{
int nblocks = (len / 16) + 1;
if (alg_id == 0x84) //AES256
aes_ofb_keystream_output (aes_iv, aes_key, ks_bytes, 2, nblocks);
else aes_ofb_keystream_output (aes_iv, aes_key, ks_bytes, 0, nblocks);
fprintf (stderr, "\n Key: ");
for (i = 0; i < 32; i++)
{
if ( (i != 0) && ((i%8) == 0) )
fprintf (stderr, " ");
fprintf (stderr, "%02X", aes_key[i]);
}
encrypted = 0;
}
fprintf (stderr, "\n IV(128): ");
for (i = 0; i < 16; i++)
fprintf (stderr, "%02X", aes_iv[i]);
}
if (alg_id == 0x81) //DES56
{
int nblocks = (len / 8) + 1;
unsigned long long int des_key = 0;
des_key = state->rkey_array[key_id];
//if no key loaded from loader, check state->R for key
if (des_key == 0) des_key = state->R;
ks_idx = 8; //offset for OFB discard round
if (des_key)
des_multi_keystream_output (mi, des_key, ks_bytes, 1, nblocks);
encrypted = 0;
//debug, print key, iv, and keystream stuff
fprintf (stderr, "\n Key: %16llX", des_key);
}
if (alg_id == 0xAA) //RC4, or 'ADP'
{
unsigned long long int rc4_key = 0;
rc4_key = state->rkey_array[key_id];
//if no key loaded from loader, check state->R for key
if (rc4_key == 0) rc4_key = state->R;
ks_idx = 0; //offset
uint8_t rc4_kiv[13]; memset (rc4_kiv, 0, sizeof(rc4_key));
rc4_kiv[0] = ((rc4_key & 0xFF00000000) >> 32);
rc4_kiv[1] = ((rc4_key & 0xFF000000) >> 24);
rc4_kiv[2] = ((rc4_key & 0xFF0000) >> 16);
rc4_kiv[3] = ((rc4_key & 0xFF00) >> 8);
rc4_kiv[4] = ((rc4_key & 0xFF) >> 0);
rc4_kiv[5] = ((mi & 0xFF00000000000000) >> 56);
rc4_kiv[6] = ((mi & 0xFF000000000000) >> 48);
rc4_kiv[7] = ((mi & 0xFF0000000000) >> 40);
rc4_kiv[8] = ((mi & 0xFF00000000) >> 32);
rc4_kiv[9] = ((mi & 0xFF000000) >> 24);
rc4_kiv[10] = ((mi & 0xFF0000) >> 16);
rc4_kiv[11] = ((mi & 0xFF00) >> 8);
rc4_kiv[12] = ((mi & 0xFF) >> 0);
if (rc4_key)
rc4_block_output (256, 13, len, rc4_kiv, ks_bytes);
encrypted = 0;
//debug, print key, iv, and keystream stuff
fprintf (stderr, "\n Key: %16llX", rc4_key);
}
//debug input offset
// fprintf (stderr, "\n INPUT: ");
// for (i = 0; i < 16; i++)
// fprintf (stderr, "%02X", input[i]);
// fprintf (stderr, "\n KS: ");
// for (i = 0; i < 16; i++)
// fprintf (stderr, "%02X", ks_bytes[i]);
//apply keystream
for (i = 0; i < len; i++) //need to subtract pad bytes and crc bytes from keystream application
input[i] ^= ks_bytes[i+ks_idx];
if (alg_id == 0x80) encrypted = 0;
return encrypted;
}
//SAP 1
uint8_t p25_decode_es_header(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t * sap, int * ptr, int len)
{
uint8_t encrypted = 0;
uint8_t bits[13*8]; memset (bits, 0, sizeof(bits));
unpack_byte_array_into_bit_array(input, bits, 13);
fprintf (stderr, "%s",KYEL);
unsigned long long int mi = (unsigned long long int)ConvertBitIntoBytes(bits, 64);
uint8_t mi_res = (uint8_t)ConvertBitIntoBytes(bits+64, 8);
uint8_t alg_id = (uint8_t)ConvertBitIntoBytes(bits+72, 8);
uint16_t key_id = (uint16_t)ConvertBitIntoBytes(bits+80, 16);
fprintf (stderr, "\n ES Aux Encryption Header; ALG: %02X; KEY ID: %04X; MI: %016llX; ", alg_id, key_id, mi);
if (mi_res != 0)
fprintf (stderr, " RES: %02X;", mi_res);
//The Auxiliary Header signals the actual SAP value of the encrypted message (this byte is not encrypted)
uint8_t aux_res = (uint8_t)ConvertBitIntoBytes(&bits[96], 2); //these two bits should always be signalled as 1's, so 0b11, and if combined with the 2ndary SAP, 0xC0 if SAP == 0x00
uint8_t aux_sap = (uint8_t)ConvertBitIntoBytes(&bits[98], 6); //the SAP of the message that is encrypted immediately after
char aux_sap_string[99];
p25_decode_sap (aux_sap, aux_sap_string);
fprintf (stderr, "%s",KNRM);
UNUSED(aux_res);
//Decrypt PDU
if (alg_id != 0x80)
encrypted = p25_decrypt_pdu(opts, state, input+13, alg_id, key_id, mi, len-13);
*sap = aux_sap;
*ptr += 13;
//append enc at this point
if (encrypted)
{
char ess_str[200]; memset(ess_str, 0, sizeof(ess_str));
sprintf (ess_str, "ALG: %02X; KID: %04X; SAP:%02X;%s", alg_id, key_id, aux_sap, aux_sap_string);
strcat (state->dmr_lrrp_gps[0], ess_str);
}
return encrypted;
}
//alternate configuration for this (no Aux SAP)
uint8_t p25_decode_es_header_2(dsd_opts * opts, dsd_state * state, uint8_t * input, int * ptr, int len)
{
uint8_t encrypted = 0;
uint8_t bits[12*8]; memset (bits, 0, sizeof(bits));
unpack_byte_array_into_bit_array(input, bits, 12);
fprintf (stderr, "%s",KYEL);
uint8_t alg_id = (uint8_t)ConvertBitIntoBytes(bits+0, 8);
uint16_t key_id = (uint16_t)ConvertBitIntoBytes(bits+8, 16);
unsigned long long int mi = (unsigned long long int)ConvertBitIntoBytes(bits+24, 64);
uint8_t mi_res = (uint8_t)ConvertBitIntoBytes(bits+88, 8);
fprintf (stderr, "\n ES Aux Encryption Header 2; ALG: %02X; KEY ID: %04X; MI: %016llX;", alg_id, key_id, mi);
if (mi_res != 0)
fprintf (stderr, " RES: %02X;", mi_res);
fprintf (stderr, "%s",KNRM);
//Decrypt PDU
if (alg_id != 0x80)
encrypted = p25_decrypt_pdu(opts, state, input+12, alg_id, key_id, mi, len-12);
*ptr += 12;
return encrypted;
}
//SAP 31 //Extended Addressing
void p25_decode_extended_address(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t * sap, int * ptr)
{
UNUSED(opts);
uint8_t bits[12*8]; memset (bits, 0, sizeof(bits));
unpack_byte_array_into_bit_array(input, bits, 12);
uint8_t ea_sap = (uint8_t)ConvertBitIntoBytes(bits+10, 6);
uint8_t ea_mfid = (uint8_t)ConvertBitIntoBytes(bits+16, 6);
uint32_t ea_llid = (uint32_t)ConvertBitIntoBytes(bits+24, 24);
uint32_t ea_res = (uint32_t)ConvertBitIntoBytes(bits+48, 32);
uint16_t ea_crc = (uint16_t)ConvertBitIntoBytes(bits+80, 16);
fprintf (stderr, "\n Extended Addressing Header; MFID: %02X; SRC LLID: %d; RES: %08X; CRC: %04X; ", ea_mfid, ea_llid, ea_res, ea_crc);
char ea_sap_string[99];
p25_decode_sap (ea_sap, ea_sap_string);
UNUSED(ea_sap_string);
//Print to Data Call String for Ncurses Terminal
state->lastsrc = ea_llid;
char ea_str[200]; memset(ea_str, 0, sizeof(ea_str));
sprintf (ea_str, "EXT ADD SRC: %d; SAP:%02X;%s", ea_llid, ea_sap, ea_sap_string);
strcat (state->dmr_lrrp_gps[0], ea_str);
*sap = ea_sap;
*ptr += 12;
}
//PDU Format Header Decode
void p25_decode_pdu_header(dsd_opts * opts, dsd_state * state, uint8_t * input)
{
UNUSED(opts);
uint8_t an = (input[0] >> 6) & 0x1;
uint8_t io = (input[0] >> 5) & 0x1;
uint8_t fmt = input[0] & 0x1F;
uint8_t sap = input[1] & 0x3F;
uint8_t MFID = input[2];
uint32_t address = (input[3] << 16) | (input[4] << 8) | input[5];
uint8_t blks = input[6] & 0x7F;
uint8_t fmf = (input[6] >> 7) & 0x1;
uint8_t pad = input[7] & 0x1F;
uint8_t ns = (input[8] >> 4) & 0x7;
uint8_t fsnf = input[8] & 0xF;
uint8_t offset = input[9] & 0x3F;
//response packet
uint8_t class = (input[1] >> 6) & 0x3;
uint8_t type = (input[1] >> 3) & 0x7;
uint8_t status = (input[1] >> 0) & 0x7;
fprintf (stderr, "%s",KGRN);
fprintf (stderr, " P25 Data - AN: %d; IO: %d; FMT: %02X; ", an, io, fmt);
char sap_string[40]; sprintf (sap_string, "%s", " ");
char rsp_string[40]; sprintf (rsp_string, "%s", " ");
if (fmt != 3) p25_decode_sap (sap, sap_string); //decode SAP to see what kind of data we are dealing with
else p25_decode_rsp (class, type, status, rsp_string); //decode the response type (ack, nack, sack)
if (sap != 61 && sap != 63) //Not too interested in viewing these on trunking control, just data packets mostly
fprintf (stderr, "\n F: %d; Blocks: %02X; Pad: %d; NS: %d; FSNF: %d; Offset: %d; MFID: %02X;", fmf, blks, pad, ns, fsnf, offset, MFID);
if (io == 1 && sap != 61 && sap != 63) //destination address if IO bit set
fprintf (stderr, " DST LLID: %d;", address);
else if (io == 0 && sap != 61 && sap != 63) //Source address if IO bit not set
fprintf (stderr, " SRC LLID: %d;", address);
//Print to Data Call String for Ncurses Terminal
if (sap != 61 && sap != 63 && fmt != 3)
sprintf (state->dmr_lrrp_gps[0], "Data Call:%s SAP:%02X; LLID: %d; ", sap_string, sap, address);
else if (sap != 61 && sap != 63 && fmt == 3)
{
//watchdog the data call and make it push to event history
sprintf (state->dmr_lrrp_gps[0], "Data Call Response:%s LLID: %d; ", rsp_string, address);
state->lastsrc = 0xFFFFFF;
watchdog_event_datacall (opts, state, state->lastsrc, state->lasttg, state->dmr_lrrp_gps[0], 0);
state->lastsrc = 0;
state->lasttg = 0;
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
}
//following is for a continued PDU and not a response nor a trunking message
if (sap != 61 && sap != 63) //trunking blocks, don't set address (LID)
{
state->lasttg = address;
state->lastsrc = 0xFFFFFF; //none given, unless extended, so put any here for now
}
}
//user or other data delivered via PDU format
void p25_decode_pdu_data(dsd_opts * opts, dsd_state * state, uint8_t * input, int len)
{
uint8_t sap = input[1] & 0x3F;
uint8_t pad = input[7] & 0x1F;
uint8_t offset = input[9] & 0x3F; UNUSED(offset); //determine the best way to use this
uint8_t encrypted = 0;
int ptr = 12; //initial ptr index value past the first header
//may need a sanity check on this value to make sure it doesn't go negative
if (len > (12 + 4 + pad))
len -= (12 + 4 + pad); //substract the header, crc, and padding bytes from total len value
//debug
fprintf (stderr, " PDU Len: %d;", len);
//check for additional headers
if (sap == 31) //extended address header
p25_decode_extended_address(opts, state, input+ptr, &sap, &ptr);
//test shows this occurs after an extended address header
if (sap == 1) //encryption sync header
encrypted = p25_decode_es_header(opts, state, input+ptr, &sap, &ptr, len);
if (!encrypted)
{
//test if an offset value set, then take the difference between it and the ptr and and add that to the ptr
//or perhaps, just assign the ptr to that value + 12?
if (offset) ptr = 12 + offset;
//now start checking for the actual message
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
// else //default catch all (debug only)
// {
// if (len > ptr)
// utf8_to_text(state, 0, len-ptr+1, input+ptr);
// else utf8_to_text(state, 0, len+1, input+ptr);
// }
}
else
{
fprintf (stderr, " Encrypted PDU;");
}
//watchdog the data call and make it push to event history
watchdog_event_datacall (opts, state, state->lastsrc, state->lasttg, state->dmr_lrrp_gps[0], 0);
state->lastsrc = 0;
state->lasttg = 0;
watchdog_event_history(opts, state, 0);
watchdog_event_current(opts, state, 0);
}

579
src/p25p1_pdu_trunking.c Normal file
View File

@@ -0,0 +1,579 @@
/*-------------------------------------------------------------------------------
* p25p1_pdu_trunking.c
* P25p1 PDU Alt Format Trunking
*
* LWVMOBILE
* 2025-03 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//trunking data delivered via PDU format
void p25_decode_pdu_trunking(dsd_opts * opts, dsd_state * state, uint8_t * mpdu_byte)
{
//group list mode so we can look and see if we need to block tuning any groups, etc
char mode[8]; //allow, block, digital, enc, etc
sprintf (mode, "%s", "");
//if we are using allow/whitelist mode, then write 'B' to mode for block
//comparison below will look for an 'A' to write to mode if it is allowed
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
uint8_t fmt = mpdu_byte[0] & 0x1F;
uint8_t MFID = mpdu_byte[2];
int blks = mpdu_byte[6] & 0x7F;
uint8_t opcode = 0;
if (fmt == 0x15) fprintf (stderr, " UNC");
else fprintf (stderr, " ALT");
fprintf (stderr, " MBT");
if (fmt == 0x17) opcode = mpdu_byte[7] & 0x3F; //alt MBT
else opcode = mpdu_byte[12] & 0x3F; //unconf MBT
fprintf (stderr, " - OP: %02X", opcode);
//NET_STS_BCST -- TIA-102.AABC-D 6.2.11.2
if (opcode == 0x3B)
{
int lra = mpdu_byte[3];
int sysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
int res_a = mpdu_byte[8];
int res_b = mpdu_byte[9];
long int wacn = (mpdu_byte[12] << 12) | (mpdu_byte[13] << 4) | (mpdu_byte[14] >> 4);
int channelt = (mpdu_byte[15] << 8) | mpdu_byte[16];
int channelr = (mpdu_byte[17] << 8) | mpdu_byte[18];
int ssc = mpdu_byte[19];
UNUSED3(res_a, res_b, ssc);
fprintf (stderr, "%s",KYEL);
fprintf (stderr, "\n Network Status Broadcast MBT - Extended \n");
fprintf (stderr, " LRA [%02X] WACN [%05lX] SYSID [%03X] NAC [%03llX]\n", lra, wacn, sysid, state->p2_cc);
fprintf (stderr, " CHAN-T [%04X] CHAN-R [%04X]", channelt, channelr);
long int ct_freq = process_channel_to_freq(opts, state, channelt);
long int cr_freq = process_channel_to_freq(opts, state, channelr);
UNUSED(cr_freq);
state->p25_cc_freq = ct_freq;
state->p25_cc_is_tdma = 0; //flag off for CC tuning purposes when system is qpsk
//place the cc freq into the list at index 0 if 0 is empty, or not the same,
//so we can hunt for rotating CCs without user LCN list
if (state->trunk_lcn_freq[0] == 0 || state->trunk_lcn_freq[0] != state->p25_cc_freq)
{
state->trunk_lcn_freq[0] = state->p25_cc_freq;
}
//only set IF these values aren't already hard set by the user
if (state->p2_hardset == 0)
{
state->p2_wacn = wacn;
state->p2_sysid = sysid;
}
}
//RFSS Status Broadcast - Extended 6.2.15.2
else if (opcode == 0x3A)
{
int lra = mpdu_byte[3];
int lsysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
int rfssid = mpdu_byte[12];
int siteid = mpdu_byte[13];
int channelt = (mpdu_byte[14] << 8) | mpdu_byte[15];
int channelr = (mpdu_byte[16] << 8) | mpdu_byte[17];
int sysclass = mpdu_byte[18];
fprintf (stderr, "%s",KYEL);
fprintf (stderr, "\n RFSS Status Broadcast MBF - Extended \n");
fprintf (stderr, " LRA [%02X] SYSID [%03X] RFSS ID [%03d] SITE ID [%03d]\n CHAN-T [%04X] CHAN-R [%02X] SSC [%02X] ", lra, lsysid, rfssid, siteid, channelt, channelr, sysclass);
process_channel_to_freq (opts, state, channelt);
process_channel_to_freq (opts, state, channelr);
state->p2_siteid = siteid;
state->p2_rfssid = rfssid;
}
//Adjacent Status Broadcast (ADJ_STS_BCST) Extended 6.2.2.2
else if (opcode == 0x3C)
{
int lra = mpdu_byte[3];
int cfva = mpdu_byte[4] >> 4;
int lsysid = ((mpdu_byte[4] & 0xF) << 8) | mpdu_byte[5];
int rfssid = mpdu_byte[8];
int siteid = mpdu_byte[9];
int channelt = (mpdu_byte[12] << 8) | mpdu_byte[13];
int channelr = (mpdu_byte[14] << 8) | mpdu_byte[15];
int sysclass = mpdu_byte[16];
long int wacn = (mpdu_byte[17] << 12) | (mpdu_byte[18] << 4) | (mpdu_byte[19] >> 4);
fprintf (stderr, "%s",KYEL);
fprintf (stderr, "\n Adjacent Status Broadcast - Extended\n");
fprintf (stderr, " LRA [%02X] CFVA [%X] RFSS[%03d] SITE [%03d] SYSID [%03X]\n CHAN-T [%04X] CHAN-R [%04X] SSC [%02X] WACN [%05lX]\n ", lra, cfva, rfssid, siteid, lsysid, channelt, channelr, sysclass, wacn);
if (cfva & 0x8) fprintf (stderr, " Conventional");
if (cfva & 0x4) fprintf (stderr, " Failure Condition");
if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
else fprintf (stderr, " Last Known");
if (cfva & 0x1) fprintf (stderr, " Valid RFSS Connection Active");
process_channel_to_freq (opts, state, channelt);
process_channel_to_freq (opts, state, channelr);
}
//Group Voice Channel Grant - Extended
else if (opcode == 0x0)
{
int svc = mpdu_byte[8];
int channelt = (mpdu_byte[14] << 8) | mpdu_byte[15];
int channelr = (mpdu_byte[16] << 8) | mpdu_byte[17];
long int source = (mpdu_byte[3] << 16) |(mpdu_byte[4] << 8) | mpdu_byte[5];
int group = (mpdu_byte[18] << 8) | mpdu_byte[19];
long int freq1 = 0;
long int freq2 = 0;
UNUSED2(source, freq2);
fprintf (stderr, "%s\n ",KYEL);
if (svc & 0x80) fprintf (stderr, " Emergency");
if (svc & 0x40) fprintf (stderr, " Encrypted");
if (opts->payload == 1) //hide behind payload due to len
{
if (svc & 0x20) fprintf (stderr, " Duplex");
if (svc & 0x10) fprintf (stderr, " Packet");
else fprintf (stderr, " Circuit");
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
}
fprintf (stderr, " Group Voice Channel Grant Update - Extended");
fprintf (stderr, "\n SVC [%02X] CHAN-T [%04X] CHAN-R [%04X] Group [%d][%04X]", svc, channelt, channelr, group, group);
freq1 = process_channel_to_freq (opts, state, channelt);
freq2 = process_channel_to_freq (opts, state, channelr);
//add active channel to string for ncurses display
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d; ", channelt, group);
state->last_active_time = time(NULL);
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == group)
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
break;
}
}
//TG hold on P25p1 Ext -- block non-matching target, allow matching group
if (state->tg_hold != 0 && state->tg_hold != group) sprintf (mode, "%s", "B");
if (state->tg_hold != 0 && state->tg_hold == group) sprintf (mode, "%s", "A");
//Skip tuning group calls if group calls are disabled
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
//Skip tuning encrypted calls if enc calls are disabled
if ( (svc & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
//tune if tuning available
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
{
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
{
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
if (1 == 1)
{
if (state->p25_chan_tdma[channelt >> 12] == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
//shim fix to stutter/lag by only enabling slot on the target/channel we tuned to
//this will only occur in realtime tuning, not not required .bin or .wav playback
if (channelt & 1) //VCH1
{
opts->slot1_on = 0;
opts->slot2_on = 1;
}
else //VCH0
{
opts->slot1_on = 1;
opts->slot2_on = 0;
}
}
}
//rigctl
if (opts->use_rigctl == 1)
{
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, freq1);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
state->last_vc_sync_time = time(NULL);
}
//rtl
else if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
rtl_dev_tune (opts, freq1);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
opts->p25_is_tuned = 1;
state->last_vc_sync_time = time(NULL);
#endif
}
}
}
}
//Unit to Unit Voice Channel Grant - Extended
else if (opcode == 0x6)
{
//I'm not doing EVERY element of this, just enough for tuning!
int svc = mpdu_byte[8];
int channelt = (mpdu_byte[22] << 8) | mpdu_byte[23];
int channelr = (mpdu_byte[24] << 8) | mpdu_byte[25]; //optional!
//using source and target address, not source and target id (is this correct?)
long int source = (mpdu_byte[3] << 16) | (mpdu_byte[4] << 8) | mpdu_byte[5];
long int target = (mpdu_byte[19] << 16) |(mpdu_byte[20] << 8) | mpdu_byte[21];
//TODO: Test Full Values added here for SUID, particular tgt_nid and tgt_sid
long int src_nid = (mpdu_byte[12] << 24) | (mpdu_byte[13] << 16) | (mpdu_byte[14] << 8) | mpdu_byte[15];
long int src_sid = (mpdu_byte[16] << 16) | (mpdu_byte[17] << 8) | mpdu_byte[18];
long int tgt_nid = (mpdu_byte[26] << 16) | (mpdu_byte[27] << 8) | mpdu_byte[28]; //only has 3 octets on tgt nid, partial only?
long int tgt_sid = (mpdu_byte[29] << 16) | (mpdu_byte[30] << 8) | mpdu_byte[31];
long int freq1 = 0;
long int freq2 = 0;
UNUSED(freq2);
fprintf (stderr, "%s\n ",KYEL);
if (svc & 0x80) fprintf (stderr, " Emergency");
if (svc & 0x40) fprintf (stderr, " Encrypted");
if (opts->payload == 1) //hide behind payload due to len
{
if (svc & 0x20) fprintf (stderr, " Duplex");
if (svc & 0x10) fprintf (stderr, " Packet");
else fprintf (stderr, " Circuit");
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
}
fprintf (stderr, " Unit to Unit Voice Channel Grant Update - Extended");
fprintf (stderr, "\n SVC: %02X; CHAN-T: %04X; CHAN-R: %04X; SRC: %ld; TGT: %ld; FULL SRC: %08lX-%08ld; FULL TGT: %08lX-%08ld;", svc, channelt, channelr, source, target, src_nid, src_sid, tgt_nid, tgt_sid);
freq1 = process_channel_to_freq (opts, state, channelt);
freq2 = process_channel_to_freq (opts, state, channelr); //optional!
//add active channel to string for ncurses display
sprintf (state->active_channel[0], "Active Ch: %04X TGT: %ld; ", channelt, target);
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == target)
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
break;
}
}
//TG hold on P25p1 Ext UU -- will want to disable UU_V grants while TG Hold enabled
if (state->tg_hold != 0 && state->tg_hold != target) sprintf (mode, "%s", "B");
// if (state->tg_hold != 0 && state->tg_hold == target) sprintf (mode, "%s", "A");
//Skip tuning private calls if group calls are disabled
if (opts->trunk_tune_private_calls == 0) goto SKIPCALL;
//Skip tuning encrypted calls if enc calls are disabled
if ( (svc & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
//tune if tuning available
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
{
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
{
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
if (1 == 1)
{
if (state->p25_chan_tdma[channelt >> 12] == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
//shim fix to stutter/lag by only enabling slot on the target/channel we tuned to
//this will only occur in realtime tuning, not not required .bin or .wav playback
if (channelt & 1) //VCH1
{
opts->slot1_on = 0;
opts->slot2_on = 1;
}
else //VCH0
{
opts->slot1_on = 1;
opts->slot2_on = 0;
}
}
}
//rigctl
if (opts->use_rigctl == 1)
{
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, freq1);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
state->last_vc_sync_time = time(NULL);
}
//rtl
else if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
rtl_dev_tune (opts, freq1);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
opts->p25_is_tuned = 1;
state->last_vc_sync_time = time(NULL);
#endif
}
}
}
}
//Telephone Interconnect Voice Channel Grant (or Update) -- Explicit Channel Form
else if ( (opcode == 0x8 || opcode == 0x9) && MFID < 2) //This form does allow for other MFID's but Moto has a seperate function on 9
{
//TELE_INT_CH_GRANT or TELE_INT_CH_GRANT_UPDT
int svc = mpdu_byte[8];
int channel = (mpdu_byte[12] << 8) | mpdu_byte[13];
int timer = (mpdu_byte[16] << 8) | mpdu_byte[17];
int target = (mpdu_byte[3] << 16) | (mpdu_byte[4] << 8) | mpdu_byte[5];
long int freq = 0;
fprintf (stderr, "\n");
if (svc & 0x80) fprintf (stderr, " Emergency");
if (svc & 0x40) fprintf (stderr, " Encrypted");
if (opts->payload == 1) //hide behind payload due to len
{
if (svc & 0x20) fprintf (stderr, " Duplex");
if (svc & 0x10) fprintf (stderr, " Packet");
else fprintf (stderr, " Circuit");
if (svc & 0x8) fprintf (stderr, " R"); //reserved bit is on
fprintf (stderr, " Priority %d", svc & 0x7); //call priority
}
fprintf (stderr, " Telephone Interconnect Voice Channel Grant");
if ( opcode & 1) fprintf (stderr, " Update");
fprintf (stderr, " Extended");
fprintf (stderr, "\n CHAN: %04X; Timer: %f Seconds; Target: %d;", channel, (float)timer*0.1f, target); //timer unit is 100 ms, or 0.1 seconds
freq = process_channel_to_freq (opts, state, channel);
//add active channel to string for ncurses display
sprintf (state->active_channel[0], "Active Tele Ch: %04X TGT: %d; ", channel, target);
state->last_active_time = time(NULL);
//Skip tuning private calls if private calls is disabled (are telephone int calls private, or talkgroup?)
if (opts->trunk_tune_private_calls == 0) goto SKIPCALL;
//Skip tuning encrypted calls if enc calls are disabled
if ( (svc & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
//telephone only has a target address (manual shows combined source/target of 24-bits)
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == target)
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
break;
}
}
//TG hold on UU_V -- will want to disable UU_V grants while TG Hold enabled
if (state->tg_hold != 0 && state->tg_hold != target) sprintf (mode, "%s", "B");
//tune if tuning available
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
{
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
{
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
if (1 == 1)
{
if (state->p25_chan_tdma[channel >> 12] == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
//shim fix to stutter/lag by only enabling slot on the target/channel we tuned to
//this will only occur in realtime tuning, not not required .bin or .wav playback
if (channel & 1) //VCH1
{
opts->slot1_on = 0;
opts->slot2_on = 1;
}
else //VCH0
{
opts->slot1_on = 1;
opts->slot2_on = 0;
}
}
}
//rigctl
if (opts->use_rigctl == 1)
{
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, freq);
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
state->last_vc_sync_time = time(NULL);
}
//rtl
else if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
rtl_dev_tune (opts, freq);
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
opts->p25_is_tuned = 1;
state->last_vc_sync_time = time(NULL);
#endif
}
}
}
if (opts->p25_trunk == 0)
{
if (target == state->lasttg || target == state->lasttgR)
{
//P1 FDMA
if (state->synctype == 0 || state->synctype == 1) state->p25_vc_freq[0] = freq;
//P2 TDMA
else state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
}
}
}
//look at Harris Opcodes and payload portion of MPDU
else if (MFID == 0xA4)
{
//TODO: Add Known Opcodes from Manual (all one of them)
fprintf (stderr, "%s",KCYN);
fprintf (stderr, "\n MFID A4 (Harris); Opcode: %02X; ", opcode);
for (int i = 0; i < (12*(blks+1)%37); i++)
fprintf (stderr, "%02X", mpdu_byte[i]);
fprintf (stderr, " %s",KNRM);
}
//look at Motorola Opcodes and payload portion of MPDU
else if (MFID == 0x90)
{
//TIA-102.AABH
if (opcode == 0x02)
{
int svc = mpdu_byte[8]; //Just the Res, P-bit, and more res bits
int channelt = (mpdu_byte[12] << 8) | mpdu_byte[13];
int channelr = (mpdu_byte[14] << 8) | mpdu_byte[15];
long int source = (mpdu_byte[3] << 16) |(mpdu_byte[4] << 8) | mpdu_byte[5];
int group = (mpdu_byte[16] << 8) | mpdu_byte[17];
long int freq1 = 0;
long int freq2 = 0;
UNUSED2(source, freq2);
fprintf (stderr, "%s\n ",KYEL);
if (svc & 0x40) fprintf (stderr, " Encrypted"); //P-bit
fprintf (stderr, " MFID90 Group Regroup Channel Grant - Explicit");
fprintf (stderr, "\n RES/P [%02X] CHAN-T [%04X] CHAN-R [%04X] SG [%d][%04X]", svc, channelt, channelr, group, group);
freq1 = process_channel_to_freq (opts, state, channelt);
freq2 = process_channel_to_freq (opts, state, channelr);
//add active channel to string for ncurses display
sprintf (state->active_channel[0], "MFID90 Ch: %04X SG: %d ", channelt, group);
state->last_active_time = time(NULL);
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == group)
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
break;
}
}
//TG hold on MFID90 GRG -- block non-matching target, allow matching group
if (state->tg_hold != 0 && state->tg_hold != group) sprintf (mode, "%s", "B");
if (state->tg_hold != 0 && state->tg_hold == group) sprintf (mode, "%s", "A");
//Skip tuning group calls if group calls are disabled
if (opts->trunk_tune_group_calls == 0) goto SKIPCALL;
//Skip tuning encrypted calls if enc calls are disabled
if ( (svc & 0x40) && opts->trunk_tune_enc_calls == 0) goto SKIPCALL;
//tune if tuning available
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0))
{
//reworked to set freq once on any call to process_channel_to_freq, and tune on that, independent of slot
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq1 != 0) //if we aren't already on a VC and have a valid frequency
{
//changed to allow symbol rate change on C4FM Phase 2 systems as well as QPSK
if (1 == 1)
{
if (state->p25_chan_tdma[channelt >> 12] == 1)
{
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
//shim fix to stutter/lag by only enabling slot on the target/channel we tuned to
//this will only occur in realtime tuning, not not required .bin or .wav playback
if (channelt & 1) //VCH1
{
opts->slot1_on = 0;
opts->slot2_on = 1;
}
else //VCH0
{
opts->slot1_on = 1;
opts->slot2_on = 0;
}
}
}
//rigctl
if (opts->use_rigctl == 1)
{
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, freq1);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
state->last_vc_sync_time = time(NULL);
}
//rtl
else if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
rtl_dev_tune (opts, freq1);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq1;
opts->p25_is_tuned = 1;
state->last_vc_sync_time = time(NULL);
#endif
}
}
}
}
else
{
fprintf (stderr, "%s",KCYN);
fprintf (stderr, "\n MFID 90 (Moto); Opcode: %02X; ", mpdu_byte[0] & 0x3F);
for (int i = 0; i < (12*(blks+1)%37); i++)
fprintf (stderr, "%02X", mpdu_byte[i]);
fprintf (stderr, " %s",KNRM);
}
}
else
{
fprintf (stderr, "%s",KCYN);
fprintf (stderr, "\n MFID %02X (Unknown); Opcode: %02X; ", MFID, mpdu_byte[0] & 0x3F);
for (int i = 0; i < (12*(blks+1)%37); i++)
fprintf (stderr, "%02X", mpdu_byte[i]);
fprintf (stderr, " %s",KNRM);
}
SKIPCALL: ; //do nothing
}

View File

@@ -9,6 +9,11 @@
void void
processTDU (dsd_opts* opts, dsd_state* state) processTDU (dsd_opts* opts, dsd_state* state)
{ {
//push current slot to 0, just in case swapping p2 to p1
//or stale slot value from p2 and then decoding a pdu
state->currentslot = 0;
AnalogSignal analog_signal_array[14]; AnalogSignal analog_signal_array[14];
int status_count; int status_count;
@@ -44,8 +49,12 @@ processTDU (dsd_opts* opts, dsd_state* state)
state->aout_gain = opts->audio_gain; state->aout_gain = opts->audio_gain;
//zero out MI, key, alg //zero out MI, key, alg
state->payload_miP = 0; // state->payload_miP = 0;
state->payload_algid = 0; // state->payload_algid = 0;
state->payload_keyid = 0; // state->payload_keyid = 0;
// state->lasttg = 0;
// state->lastsrc = 0;
// state->gi[0] = -1;
} }

View File

@@ -215,6 +215,11 @@ read_zeros(dsd_opts* opts, dsd_state* state, AnalogSignal* analog_signal_array,
void void
processTDULC (dsd_opts* opts, dsd_state* state) processTDULC (dsd_opts* opts, dsd_state* state)
{ {
//push current slot to 0, just in case swapping p2 to p1
//or stale slot value from p2 and then decoding a pdu
state->currentslot = 0;
int i; int i;
uint8_t lcinfo[57], lcformat[9], mfid[9]; uint8_t lcinfo[57], lcformat[9], mfid[9];
@@ -452,8 +457,8 @@ processTDULC (dsd_opts* opts, dsd_state* state)
state->aout_gain = opts->audio_gain; state->aout_gain = opts->audio_gain;
//zero out MI, key, alg //zero out MI, key, alg
state->payload_miP = 0; // state->payload_miP = 0;
state->payload_algid = 0; // state->payload_algid = 0;
state->payload_keyid = 0; // state->payload_keyid = 0;
} }

View File

@@ -18,6 +18,9 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
memset (state->s_r4, 0, sizeof(state->s_r4)); memset (state->s_r4, 0, sizeof(state->s_r4));
opts->slot_preference = 2; opts->slot_preference = 2;
//push current slot to 0, just in case swapping p2 to p1
//or stale slot value from p2 and then decoding a pdu
state->currentslot = 0;
//reset some strings when returning from a call in case they didn't get zipped already //reset some strings when returning from a call in case they didn't get zipped already
sprintf (state->call_string[0], "%s", " "); //21 spaces sprintf (state->call_string[0], "%s", " "); //21 spaces
@@ -664,4 +667,8 @@ void processTSBK(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s ", KNRM); fprintf (stderr, "%s ", KNRM);
fprintf (stderr, "\n"); fprintf (stderr, "\n");
//when on a CC, rotate the symbol out file every hour, if enabled
rotate_symbol_out_file(opts, state);
} }

Some files were not shown because too many files have changed in this diff Show More