34 Commits

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

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

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

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

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

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

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

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

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

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

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

DMR: Init AES keyloader arrays for PDU;

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

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

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

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

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

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

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

NXDN: Decode DST_ID_INFO Message;

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

NXDN: Seperate PROP_FORM 0x3F from Alias via checkdown;

YSF: Soft Decision Viterbi;

YSF: Fix BUMBLEBEETUNA text string on partial decode;

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

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

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

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

Added static_wav_file check to both IMBE and AMBE playback branches.
2026-01-29 14:32:57 +02:00
lwvmobile
99f422898a Update and Push Tag; 2026-01-06 21:18:13 -05:00
42 changed files with 8053 additions and 3481 deletions

View File

@@ -39,6 +39,62 @@ if (LZ)
add_definitions(-DLIMAZULUTWEAKS)
endif ()
#use cmake option -DASCII=ON to enable ASCII only Alias Support in NXDN
option(ASCII
"Build with ASCII only Alias Support in NXDN Enabled" ON)
if (ASCII)
add_definitions(-DASCII_DECODE)
endif ()
#use cmake option -DASCII=OFF -DBIG5=ON to enable ASCII and BIG5 Alias Support in NXDN
option(BIG5
"Build with BIG5 and ASCII Alias Support in NXDN Enabled" OFF)
if (BIG5)
add_definitions(-DBIG5_DECODE)
endif ()
#use cmake option -DASCII=OFF -DSJIS=ON to enable ASCII and SJIS Alias Support in NXDN
option(SJIS
"Build with SJIS and ASCII Alias Support in NXDN Enabled" OFF)
if (SJIS)
add_definitions(-DSJIS_DECODE)
endif ()
#use cmake option -DASCII=OFF -DUTF16=ON to enable ASCII and UTF16BE Alias Support in NXDN
option(UTF16
"Build with UTF16BE and ASCII Alias Support in NXDN Enabled" OFF)
if (UTF16)
add_definitions(-DUTF16_DECODE)
endif ()
#use cmake option -DTYPEC=OFF to disable NXDN Type-C and Conventional Decoding (enabled by default)
option(TYPEC
"Build with NXDN Type-C and Conventional Decoding Enabled" ON)
if (TYPEC)
add_definitions(-DTYPE_C_DECODE)
endif ()
#use cmake option -DTYPED=OFF to disable NXDN Type-D / IDAS Decoding (enabled by default)
option(TYPED
"Build with NXDN Type-D / IDAS Decoding Enabled" ON)
if (TYPED)
add_definitions(-DTYPE_D_DECODE)
endif ()
#use cmake option -DDCR=OFF to disable Japanese DCR (NXDN Variant) Decoding (enabled by default)
option(DCR
"Build with Japanese DCR (NXDN Variant) Decoding Enabled" ON)
if (DCR)
add_definitions(-DJPNDCR_DECODE)
endif ()
#use cmake option -DNOS=ON to enable older NXDN sync w/ pos and inv polarity, disable newer seperated (disabled by default)
option(NOS
"Build with Old NXDN Sync (pos and inv polarity) Enabled" OFF)
if (NOS)
add_definitions(-DNXDN_OLD_SYNC)
endif ()
#use cmake option -DSID=ON to enable P25p1 Soft ID decoding -- Experimental
option(SID
"Build with P25p1 LSD/Soft ID Enabled" OFF)

View File

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

View File

@@ -1,4 +1,4 @@
Build Version: AW 2025-209-g14edf32
Build Version: AW 2026-30-g43d63b2
MBElib Version: 1.3.4
CODEC2 Support Enabled
@@ -50,6 +50,8 @@ Input/Output options:
-w <file> Output synthesized speech to a single static .wav file. (Do not use with -P Per Call Switch)
-P Enable Per Call WAV file saving. (Do not use with -w filename.wav single wav file switch)
(Per Call works with everything now and doesn't require ncurses terminal!)
-j <string> Custom Tag(s), colon delimited, to append to Per Call decoded .wav files.
-j TGT_LAPD:TAG_FIRE:FREQ_851987500 (Total 3 Tags, Maximum Letter Length = 100)
-a Enable Call Alert Beep
(Warning! Might be annoying.)
-J <file> Specify Filename for Event Log Output.
@@ -103,6 +105,7 @@ Decoder options:
-xx Expect non-inverted X2-TDMA signal
-xr Expect inverted DMR signal
-xd Expect inverted ICOM dPMR signal
-xn Expect inverted NXDN/DCR/IDAS signal
* denotes frame types that cannot be auto-detected.
** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.
@@ -220,5 +223,4 @@ Advanced Decoder options:
-t <secs> Set Trunking or Scan Speed VC/sync loss hangtime in seconds. (default = 1 second)
Trunking Example TCP: dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.txt
Trunking Example RTL: dsd-fme -fs -i rtl:0:450M:26:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.txt
Trunking Example RTL: dsd-fme -fs -i rtl:0:450M:26:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.txt

View File

@@ -173,8 +173,8 @@ typedef struct pa_devicelist
typedef struct
{
unsigned long int groupNumber;
char groupMode[8]; //char *?
char groupName[50];
char groupMode[3];
char groupName[100];
} groupinfo;
@@ -324,6 +324,7 @@ typedef struct
char wav_out_file[1024];
char wav_out_fileR[1024];
char wav_out_file_raw[1024];
char wav_custom_tag[100];
char symbol_out_file[1024];
char lrrp_out_file[1024];
char event_out_file[1024];
@@ -354,6 +355,7 @@ typedef struct
int uvquality;
int inverted_x2tdma;
int inverted_dmr;
int inverted_nxdn;
int mod_threshold;
int ssize;
int msize;
@@ -363,6 +365,7 @@ typedef struct
int unmute_encrypted_p25;
int rtl_dev_index;
int rtl_gain_value;
int rtl_gain_actual;
int rtl_squelch_level;
int rtl_volume_multiplier;
int rtl_udp_port;
@@ -869,7 +872,7 @@ typedef struct
//trunking group and lcn freq list
long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully
long int trunk_chan_map[0xFFFF]; //NXDN - 10 bit; P25 - 16 bit; DMR up to 12 bit (standard TIII)
groupinfo group_array[0x3FF]; //max supported by Cygwin is 3FFF, I hope nobody actually tries to import this many groups
groupinfo * group_array;
unsigned int group_tally; //tally number of groups imported from CSV file for referencing later
int lcn_freq_count;
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
@@ -1183,7 +1186,7 @@ void openWavOutFileLR (dsd_opts * opts, dsd_state * state); //stereo wav file fo
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state);
SNDFILE * open_wav_file (char * dir, char * temp_filename, uint16_t sample_rate, uint8_t ext);
SNDFILE * close_wav_file(SNDFILE * wav_file);
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, Event_History_I * event_struct);
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, char * custom_tag, Event_History_I * event_struct);
SNDFILE * close_and_delete_wav_file(SNDFILE * wav_file, char * wav_out_filename);
void openSymbolOutFile (dsd_opts * opts, dsd_state * state);
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state);
@@ -1262,25 +1265,25 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state);
uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c);
void ncursesClose ();
//new NXDN Functions start here!
//NXDN Functions Start Here
void nxdn_frame (dsd_opts * opts, dsd_state * state);
void nxdn_pn95_dibit_scrambler(dsd_state * state, uint8_t * dibits, int len);
//nxdn deinterleaving/depuncturing functions
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
//type-d 'idas' deinterleaving/depuncturing functions
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction);
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
//DCR Mode
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t bits[144], uint8_t lich);
//MT and Voice
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
//Osmocom OP25 12 Rate Trellis Decoder (for NXDN, M17, YSF, etc)
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf);
//All-in-One NXDN Soft Decision Viterbi Function based on libM17
uint32_t nxdn_soft_decision_viterbi(uint8_t * bits, const uint16_t * interleave, uint8_t * puncture, int d_len, int p_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
//NXDN Conventional and Type-C
void nxdn_facch1(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t frame);
void nxdn_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_cac(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
//type-d 'idas'
void idas_scch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t direction);
void idas_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
//Japanese DCR
void dcr_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void dcr_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t lich);
//OP25 NXDN CRC functions
int load_i(const uint8_t val[], int len);
@@ -1289,13 +1292,10 @@ uint16_t crc12f(const uint8_t buf[], int len);
uint16_t crc15(const uint8_t buf[], int len);
uint16_t crc16cac(const uint8_t buf[], int len);
uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
uint32_t nxdn_message_crc32(uint8_t * input, int len);
/* NXDN Convolution functions */
void CNXDNConvolution_start(void);
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
void CNXDNConvolution_init();
//YSF Soft Decision Viterbi
uint32_t ysf_soft_decision_viterbi(uint8_t * dbuf, int d_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
//libM17 viterbi decoder
@@ -1311,6 +1311,9 @@ uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len);
void viterbi_reset(void);
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2);
//libm17 based soft decision viterbi for variable len and new punctures
void slice_symbols_to_len(uint16_t * out, const float * inp, int len);
//keeping these
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
@@ -1319,19 +1322,28 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message);
char * NXDN_Call_Type_To_Str(uint8_t CallType);
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
void NXDN_Data_Call_Option_To_Str(uint8_t DataCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
//added these
void NXDN_decode_Prop(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_Alias16(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_sdcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
//Type-D SCCH Message Decoder
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
void NXDN_decode_VCALL_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
uint32_t sjis_char_to_unicode(uint16_t sjis);
uint32_t big5_char_to_unicode(uint16_t big5);
int utf8_encode(int c, char *buf);
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
@@ -1372,7 +1384,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18]); //simplier trellis decoder
uint32_t viterbi_r34 (uint8_t * input, uint8_t * output);
void beeper (dsd_opts * opts, dsd_state * state, int lr, int id, int ad, int len);
void dmr_gateway_identifier (uint32_t source, uint32_t target); //translate special addresses
@@ -1394,10 +1406,11 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input);
void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int type);
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot);
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input);
void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot, int phase);
void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src);
void utf16_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input);
void utf8_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input);
uint8_t nmea_sentence_checker(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t slot, int len);
//"DMR STEREO"
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
@@ -1606,6 +1619,9 @@ int udp_socket_connectA(dsd_opts * opts, dsd_state * state);
void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
//15-bit Scrambler
void pdu_scrambler_keystream_creation(uint8_t * ks, int lfsr, int len);
//RC4 function prototypes
void rc4_voice_decrypt (int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[]);
void rc4_block_output (int drop, int keylen, int meslen, uint8_t * key, uint8_t * output_blocks);
@@ -1648,6 +1664,7 @@ void lfsr_64_to_128(uint8_t * iv);
void LFSR128(dsd_state * state);
void LFSR128n(dsd_state * state);
void LFSR128d(dsd_state * state);
void LFSR128npdu(dsd_state * state);
#ifdef __cplusplus

View File

@@ -16,7 +16,7 @@
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
// 02110-1301, USA.
static const uint8_t PERM_12_5[] = {
static const uint16_t PERM_12_5[] = {
0,12,24,36,48,
1,13,25,37,49,
2,14,26,38,50,
@@ -31,7 +31,7 @@ static const uint8_t PERM_12_5[] = {
11,23,35,47,59
};
static const uint8_t PERM_16_9[] = {
static const uint16_t PERM_16_9[] = {
0, 16, 32, 48, 64, 80, 96, 112, 128,
1, 17, 33, 49, 65, 81, 97, 113, 129,
2, 18, 34, 50, 66, 82, 98, 114, 130,

View File

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

View File

@@ -270,13 +270,12 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
{
strcat (rsp_string, "NACK - ");
if (r_type == 0) strcat (rsp_string, "Illegal Format");
if (r_type == 1) strcat (rsp_string, "Illegal Format");
if (r_type == 2) strcat (rsp_string, "Packet CRC ERR");
if (r_type == 3) strcat (rsp_string, "Memory Full");
if (r_type == 4) strcat (rsp_string, "FSN Out of Seq");
if (r_type == 5) strcat (rsp_string, "Undeliverable");
if (r_type == 6) strcat (rsp_string, "PKT Out of Seq");
if (r_type == 7) strcat (rsp_string, "Invalid User");
if (r_type == 1) strcat (rsp_string, "Packet CRC ERR");
if (r_type == 2) strcat (rsp_string, "Memory Full");
if (r_type == 3) strcat (rsp_string, "FSN Out of Seq");
if (r_type == 4) strcat (rsp_string, "Undeliverable");
if (r_type == 5) strcat (rsp_string, "PKT Out of Seq");
if (r_type == 6) strcat (rsp_string, "Invalid User");
}
if (r_class == 2) strcat (rsp_string, "SACK - Retry");
// if (r_status) strcat (rsp_string, " - %d", r_status);
@@ -293,8 +292,19 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
//Confirmed or Unconfirmed Data Packets Header
if (dpf == 2 || dpf == 3)
{
if (dpf == 2) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN %d", sap, sap_string, f, bf, poc, fsn);
if (dpf == 3) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN %d", sap, sap_string, f, bf, poc, s, ns, fsn);
uint8_t fsn_number = (fsn & 7) + 1;
char fsn_string[50];
memset(fsn_string, 0, sizeof(fsn_string));
if (fsn == 0) sprintf(fsn_string, "%s", "Unconfirmed data single fragment");
else if (fsn == 8) sprintf(fsn_string, "%s", "Confirmed data single fragment");
else if (fsn >= 9) sprintf(fsn_string, "%s", "Last confirmed data fragment with number");
else sprintf(fsn_string, "%s", "Subsequent confirmed data fragment with number");
if (dpf == 2) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN: [%X] - %s %d; ", sap, sap_string, f, bf, poc, fsn, fsn_string, fsn_number);
if (dpf == 3) fprintf (stderr, "\n SAP %02d [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN: [%X] - %s %d; ", sap, sap_string, f, bf, poc, s, ns, fsn, fsn_string, fsn_number);
state->data_header_blocks[slot] = bf;
if (dpf == 3) state->data_conf_data[slot] = 1; //set confirmed data delivery flag for additional CRC checks, block assembly, etc.
@@ -1028,9 +1038,9 @@ void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_byte
R = state->rkey_array[state->payload_keyidR];
//loader for aes keys
uint8_t kaes[32];
uint8_t empt[32];
uint8_t maes[16];
uint8_t kaes[32]; memset(kaes, 0, sizeof(kaes));
uint8_t empt[32]; memset(empt, 0, sizeof(empt));
uint8_t maes[16]; memset(maes, 0, sizeof(maes));
for (i = 0; i < 8; i++)
{
kaes[i+0] = ((state->rkey_array[kid+0x000]) >> (56-(i*8))) & 0xFF;

View File

@@ -702,8 +702,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
}
else if (state->payload_algid == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 0;
hytera_enhanced_alg_refresh(state);
dmr_alg_refresh (opts, state);
}
if (state->payload_algidR >= 0x21)
@@ -713,8 +713,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
}
else if (state->payload_algidR == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 1;
hytera_enhanced_alg_refresh(state);
dmr_alg_refresh (opts, state);
}
@@ -1069,10 +1069,10 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
state->currentslot = 1;
dmr_alg_refresh (opts, state);
}
else if (state->payload_algid == 0x02)
else if (state->payload_algidR == 0x02)
{
hytera_enhanced_alg_refresh(state);
state->currentslot = 1;
hytera_enhanced_alg_refresh(state);
dmr_alg_refresh (opts, state);
}

View File

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

View File

@@ -560,7 +560,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
if (opts->p25_trunk == 1 && opts->trunk_tune_enc_calls == 0) //&& type != 2
{
int i, lo = 0;
uint32_t t = 0; char gm[8]; char gn[50];
uint32_t t = 0; char gm[3]; char gn[100];
//check to see if this group already exists, or has already been locked out, or is allowed
for (i = 0; i <= state->group_tally; i++)

View File

@@ -521,6 +521,12 @@ void decode_ip_pdu (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t *
lip_protocol_decoder(opts, state, bits);
}
//known P25 Ports
else if (port1 == 9361 && port2 == 9361)
{
sprintf (state->dmr_lrrp_gps[slot], "P25 Atlas SRC(IP): %d.%d.%d.%d; DST(IP): %d.%d.%d.%d; ",
input[12], input[13], input[14], input[15], input[16], input[17], input[18], input[19]);
fprintf (stderr, "Atlas Data Registration Server;"); //EF Johnson Atlas
}
else if (port1 == 49198 && port2 == 49198)
{
sprintf (state->dmr_lrrp_gps[slot], "P25 Tier 2 LOCN SRC(IP): %d.%d.%d.%d; DST(IP): %d.%d.%d.%d; ",
@@ -995,8 +1001,9 @@ void dmr_locn (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * DMR_P
//write to LRRP file
if (opts->lrrp_file_output == 1)
{
char * timestr = getTimeC();
char * datestr = getDateS();
char * timestr = getTimeC();
//open file by name that is supplied in the ncurses terminal, or cli
FILE * pFile; //file pointer

View File

@@ -581,11 +581,15 @@ void tait_iso7_embedded_alias_decode (dsd_opts * opts, dsd_state * state, uint8_
for (uint8_t i = 0; i < len; i++)
{
alias[i] = (uint8_t)ConvertBitIntoBytes(&input[16+(i*7)], 7);
fprintf (stderr, "%c", alias[i]);
// fprintf (stderr, "%X,", alias[i]); //debug
if (alias[i] == 0x2C) //change a comma to a dot
alias[i] = 0x2E;
else if (alias[i] < 0x20) //change any garble / control chars to a space
alias[i] = 0x20;
fprintf (stderr, "%c", alias[i]);
}
//flag to indicate this already exists in import or group struct

View File

@@ -2194,6 +2194,8 @@ void agsm (dsd_opts * opts, dsd_state * state, short * input, int len)
state->aout_gainA = coeff; //store for internal use
UNUSED(avg);
}
//until analog agc is fixed, going to use a manual gain control on this

View File

@@ -218,13 +218,13 @@ void watchdog_event_history (dsd_opts * opts, dsd_state * state, uint8_t slot)
if (slot == 0 && opts->wav_out_f != NULL)
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, event_struct);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, event_struct);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
}
else if (slot == 1 && opts->wav_out_fR != NULL)
{
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, event_struct);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, event_struct);
opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0);
}

View File

@@ -502,7 +502,7 @@ SNDFILE * close_wav_file(SNDFILE * wav_file)
return wav_file;
}
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, Event_History_I * event_struct)
SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename, char * dir, char * custom_tag, Event_History_I * event_struct)
{
sf_close(wav_file);
@@ -538,11 +538,46 @@ SNDFILE * close_and_rename_wav_file(SNDFILE * wav_file, char * wav_out_filename,
char new_filename[2000];
memset (new_filename, 0, sizeof(new_filename));
//parse any possible custom tags or aliasing (up to 3)
char custom_string_temp[100];
memset(custom_string_temp, 0, sizeof(custom_string_temp));
memcpy(custom_string_temp, custom_tag, sizeof(custom_string_temp));
char * token = strtok(custom_string_temp, ":");
char tag[3][25];
memset(tag, 0, sizeof(tag));
sprintf(tag[0], "%s", "");
sprintf(tag[1], "%s", "");
sprintf(tag[2], "%s", "");
for (int i = 0; i < 3; i++)
{
if (token != NULL)
{
tag[i][0] = '_';
strncpy(tag[i]+1, token, 23);
tag[i][24] = '\0';
token = strtok(NULL, ":");
//look for _TGT_, convert to strings
if (strncmp("_TGT_", tag[i], 5) == 0)
{
sprintf (tgt_str, "%s", tag[i]+5);
sprintf (src_str, "%d", source_id);
is_string = 1;
memset(tag[i], 0, sizeof(tag[i]));
sprintf(tag[i], "%s", "");
}
}
else break;
}
//check for String based TGT and SRC values (M17, YSF, DSTAR)
if (is_string == 1)
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tgt_str, src_str);
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s%s%s%s_TGT_%s_SRC_%s.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tag[0], tag[1], tag[2], tgt_str, src_str);
else //is a numerical value
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s_TGT_%d_SRC_%d.wav", dir, datestr, timestr, random_number, sys_str, gi_str, target_id, source_id);
sprintf (new_filename, "%s/%s_%s_%05d_%s_%s%s%s%s_TGT_%d_SRC_%d.wav", dir, datestr, timestr, random_number, sys_str, gi_str, tag[0], tag[1], tag[2], target_id, source_id);
if (timestr != NULL)
{
@@ -1345,6 +1380,18 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
state->lastsynctype = 10;
}
if (strncmp ("NXDN", str_buffer, 4) == 0)
{
//set AMBE+2 protocol here
protocol = 3;
//disable dmra (+7 on ks_idx)
is_dmra = 0;
state->synctype = 28;
state->lastsynctype = 28;
}
//open .imb or .amb file, if desired, but only after setting a synctype
if (state->synctype != -1)
{
@@ -1355,6 +1402,57 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
if (strncmp ("tag", str_buffer, 3) == 0)
{
str_buffer = strtok(NULL, "\n"); //copies to End of Line
//debug
// fprintf (stderr, " TAG: %s", str_buffer);
//NOTE: Nothing coded for SDRTrunk MBE files provides information regarding
//possible FACCH1 steals on voice frames, so keystrem index position here can
//become unsynced on some voice frames if trunking or other does FACCH1 steals
//when the system is really busy, or radio drops out. Writing code to read ahead
//would still not tell us if the FACCH1 steal was in vocoder socket 1 or 2
//NOTE: Above may become even more noticeable on DES or AES as those keystreams
//are generated for two voice superframes, and the decryption may become unsynced
//until the next VCALL_IV is read in from the MBE file.
//Check NXDN tag value, set the ks_idx to proper position
//by looking for SACCH_1, SACCH_2, SACCH_3, or SACCH_4 for scrambler
if (alg_id == 1 && protocol == 3)
{
if (strncmp ("SACCH 1", str_buffer+4, 7) == 0)
{
ks_idx = 0;
if (opts->payload == 1)
fprintf (stderr, "\n PF 1/4");
}
else if (strncmp ("SACCH 2", str_buffer+4, 7) == 0)
{
ks_idx = 49*4;
if (opts->payload == 1)
fprintf (stderr, "\n PF 2/4");
}
else if (strncmp ("SACCH 3", str_buffer+4, 7) == 0)
{
ks_idx = 49*8;
if (opts->payload == 1)
fprintf (stderr, "\n PF 3/4");
}
else if (strncmp ("SACCH 4", str_buffer+4, 7) == 0)
{
ks_idx = 49*12;
if (opts->payload == 1)
fprintf (stderr, "\n PF 4/4");
}
}
}
if (strncmp ("call_type", str_buffer, 9) == 0)
{
str_buffer = strtok(NULL, " : \""); //next value after any : "" string
@@ -1388,6 +1486,29 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
key_id = 0;
iv_hex = 0;
//NXDN Forced Scrambler
if (is_enc == 1 && state->forced_alg_id == 1 && protocol == 3)
{
alg_id = state->forced_alg_id;
//Key Load Forced Scrambler Key by TGT or SRC value
if (state->forced_alg_id == 1 && state->keyloader == 1)
{
if (state->rkey_array[target] != 0)
state->R = state->rkey_array[target];
else if (state->rkey_array[source] != 0)
state->R = state->rkey_array[source];
}
if (alg_id == 1 && state->R != 0)
{
pdu_scrambler_keystream_creation(ks, state->R, 16*49);
ks_available = 1;
}
//DES and AES still require the IV for KS creation
}
//debug set value
// fprintf (stderr, " ENC: %d;", is_enc);
@@ -1461,10 +1582,6 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
{
//Hytera BP
}
else if (state->R != 0)
{
//NXDN Scrambler
}
}
if (strncmp ("to", str_buffer, 2) == 0)
@@ -1523,6 +1640,36 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
key_id = strtol (str_buffer, NULL, 10);
//NXDN Scrambler Setup
if (alg_id == 1 && protocol == 3)
{
state->payload_keyid = key_id;
//Scrambler Key Loading
if (state->keyloader == 1)
keyring(opts, state);
//If Scrambler Key still not available on KID
if (state->R == 0)
{
//Key Load Scrambler Key by TGT or SRC value
if (state->forced_alg_id == 1 && state->keyloader == 1)
{
if (state->rkey_array[target] != 0)
state->R = state->rkey_array[target];
else if (state->rkey_array[source] != 0)
state->R = state->rkey_array[source];
}
}
if (state->R != 0)
{
pdu_scrambler_keystream_creation(ks, state->R, 16*49);
ks_available = 1;
}
}
//debug set value
if (opts->payload == 1)
fprintf (stderr, "\n Key ID: %04X;", key_id);
@@ -1708,6 +1855,50 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
//WIP: Add handling for NXDN DES and AES256
//NOTE: This hinges on where SDRTrunk places the IV for the call,
//ideally needs to be after the SACCH4 voice frames
//NOTE: This will NOT WORK on Type-D with its 22-bit IV
//I doubt anybody will ever test that theory out here either
else if (protocol == 3 && alg_id == 2 && state->R != 0)
{
des_multi_keystream_output(iv_hex, state->R, ks_bytes, 1, 32);
unpack_byte_array_into_bit_array(ks_bytes+8, ks, 256-8);
ambe2_counter = 0;
ks_idx = 0;
ks_available = 1;
}
else if (protocol == 3 && alg_id == 3 && state->aes_key_loaded[0] == 1)
{
uint8_t aes_key[32];
memset (aes_key, 0, sizeof(aes_key));
//Load key from A1 - A4
for (int i = 0; i < 8; i++)
{
aes_key[i+0] = (state->A1[0] >> (56-(i*8))) & 0xFF;
aes_key[i+8] = (state->A2[0] >> (56-(i*8))) & 0xFF;
aes_key[i+16] = (state->A3[0] >> (56-(i*8))) & 0xFF;
aes_key[i+24] = (state->A4[0] >> (56-(i*8))) & 0xFF;
}
//Generate 128-bit IV for AES
LFSR128npdu(state); //npdu variant uses payload_mi here, and loads to aes_ivR
aes_ofb_keystream_output (state->aes_ivR, aes_key, ks_bytes, 2, 16);
unpack_byte_array_into_bit_array(ks_bytes+16, ks, 256-16);
ambe2_counter = 0;
ks_idx = 0;
ks_available = 1;
}
//Pull request: https://github.com/DSheirer/sdrtrunk/pull/2273
//Merged into SDRTrunk nightly so should be able to handle this better now
@@ -1776,7 +1967,7 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
// fprintf (stderr, " Enc Play;");
}
else if (protocol == 2) //P25p2 AMBE
else if (protocol == 2) //P25p2 or DMR AMBE+2
{
//debug print current str_buffer
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
@@ -1801,6 +1992,37 @@ void read_sdrtrunk_json_format (dsd_opts * opts, dsd_state * state)
}
}
else if (protocol == 3) //NXDN AMBE+2 (EHR)
{
//debug print current str_buffer
// fprintf (stderr, "\n AMBE HEX: %s", str_buffer);
//18 hex characters on 'hex' which is the AMBE interleaved C codewords
ks_idx = ambe2_str_to_decode(opts, state, str_buffer, ks, ks_idx, is_dmra, is_enc, ks_available);
//debug
// if (is_enc == 1 && ks_available == 0)
// fprintf (stderr, " Enc Mute;");
// else if (is_enc == 1 && ks_available == 1)
// fprintf (stderr, " Enc Play;");
//increment AMBE+2 counter
ambe2_counter++;
//Really actually need the Structure Field (PF values here)
//reset if over for NXDN 16 AMBE+2 frames for Scrambler
if (alg_id == 1 && ambe2_counter == 16)
{
ambe2_counter = 0;
ks_idx = 0;
}
else if (alg_id > 1 && ambe2_counter == 32)
{
ambe2_counter = 0;
ks_idx = 0;
}
}
}
if (strncmp ("time", str_buffer, 4) == 0)

View File

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

View File

@@ -379,8 +379,19 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
lbuf2[i] = lbuf[i];
}
qsort (lbuf2, t_max, sizeof (int), comp);
lmin = (lbuf2[1] + lbuf2[2] + lbuf2[3]) / 3;
lmax = (lbuf2[t_max - 3] + lbuf2[t_max - 2] + lbuf2[t_max - 1]) / 3;
//on shorter sync patterns, we need to look at the edges since we don't have many to pull from
if (t_max < 13)
{
lmin = (lbuf2[0] + lbuf2[1] + lbuf2[2]) / 3;
lmax = (lbuf2[t_max - 3] + lbuf2[t_max - 2] + lbuf2[t_max - 1]) / 3;
}
//on longer sync patterns, we can get more towards the middle and eliminate outlier values
else
{
lmin = (lbuf2[2] + lbuf2[3] + lbuf2[4]) / 3;
lmax = (lbuf2[t_max - 5] + lbuf2[t_max - 4] + lbuf2[t_max - 3]) / 3;
}
if (state->rf_mod == 1)
{
@@ -1140,8 +1151,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
}
//NXDN
#ifdef NXDN_OLD_SYNC
//NXDN (previous sync, now a cmake option -DNOS=ON ..)
else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
{
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
@@ -1155,11 +1166,14 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
//debug
// fprintf (stderr, " %s ", synctest10);
if (state->lastsynctype == 28)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->last_cc_sync_time = time(NULL);
return (28);
}
@@ -1177,9 +1191,72 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
)
{
//debug
// fprintf (stderr, " %s ", synctest10);
if (state->lastsynctype == 29)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->last_cc_sync_time = time(NULL);
return (29);
}
state->lastsynctype = 29;
}
}
#else //new sync
//NXDN (positive sync only)
else if (opts->inverted_nxdn == 0 && ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) )
{
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
if (
(strcmp (synctest10, "3131331131") == 0 )
|| (strcmp (synctest10, "3331331131") == 0 )
|| (strcmp (synctest10, "3131331111") == 0 )
// || (strcmp (synctest10, "3331331111") == 0 )
// || (strcmp (synctest10, "3131311131") == 0 )
)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
//debug
// fprintf (stderr, " %s ", synctest10);
if (state->lastsynctype == 28)
{
state->last_cc_sync_time = time(NULL);
return (28);
}
state->lastsynctype = 28;
}
}
//NXDN (inverted sync only)
else if (opts->inverted_nxdn == 1 && ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) )
{
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
if (
(strcmp (synctest10, "1313113313") == 0 )
|| (strcmp (synctest10, "1113113313") == 0 )
|| (strcmp (synctest10, "1313113333") == 0 )
// || (strcmp (synctest10, "1113113333") == 0 )
// || (strcmp (synctest10, "1313133313") == 0 )
)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
//debug
// fprintf (stderr, " %s ", synctest10);
if (state->lastsynctype == 29)
{
state->last_cc_sync_time = time(NULL);
@@ -1187,7 +1264,9 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
state->lastsynctype = 29;
}
}
#endif
//Provoice Conventional -- Some False Positives due to shortened frame sync pattern, so use squelch if possible
#ifdef PVCONVENTIONAL

View File

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

View File

@@ -7,26 +7,31 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
{
char filename[1024] = "filename.csv";
sprintf (filename, "%s", opts->group_in_file);
//filename[1023] = '\0'; //necessary?
char buffer[BSIZE];
FILE * fp;
fp = fopen(filename, "r");
if (fp == NULL) {
if (fp == NULL)
{
printf("Unable to open group file '%s'\n", filename);
exit(1);
}
int row_count = 0;
int field_count = 0;
long int group_number = 0; //local group number for array index value
UNUSED(group_number);
int i = 0;
while (fgets(buffer, BSIZE, fp)) {
unsigned int i = 0;
while (fgets(buffer, BSIZE, fp))
{
field_count = 0;
row_count++;
if (row_count == 1)
continue; //don't want labels
char * field = strtok(buffer, ","); //seperate by comma
while (field) {
while (field)
{
if (field_count == 0)
{
@@ -34,23 +39,38 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
state->group_array[i].groupNumber = atol(field);
fprintf (stderr, "%ld, ", state->group_array[i].groupNumber);
}
if (field_count == 1)
else if (field_count == 1)
{
strcpy(state->group_array[i].groupMode, field);
// strcpy(state->group_array[i].groupMode, field);
strncpy(state->group_array[i].groupMode, field, 2); //only copy two letters max A, B, D, DE
fprintf (stderr, "%s, ", state->group_array[i].groupMode);
}
if (field_count == 2)
else if (field_count == 2)
{
strcpy(state->group_array[i].groupName, field);
// strcpy(state->group_array[i].groupName, field);
strncpy(state->group_array[i].groupName, field, 98); //this should also fix potential issues observed in past when there is no comma after the name
uint16_t len = strlen((const char*)state->group_array[i].groupName);
//remove any potential linebreaks at this point
if (state->group_array[i].groupName[len-1] == '\n')
{
// fprintf(stderr, " ** LEN: %d; ** ", len); //debug
state->group_array[i].groupName[len-1] = '\0';
}
fprintf (stderr, "%s ", state->group_array[i].groupName);
}
field = strtok(NULL, ",");
field_count++;
}
fprintf (stderr, "\n");
i++;
state->group_tally++;
}
fclose(fp);
return 0;

View File

@@ -234,7 +234,7 @@
state->dibit_buf_p = state->dibit_buf + 200;
memset (state->dibit_buf, 0, sizeof (int) * 200);
//dmr buffer
state->dmr_payload_p = state->dibit_buf + 200;
state->dmr_payload_p = state->dmr_payload_buf + 200;
memset (state->dmr_payload_buf, 0, sizeof (int) * 200);
memset (state->dmr_stereo_payload, 1, sizeof(int) * 144);
//dmr buffer end
@@ -417,7 +417,7 @@
// state->p25_vc_freq[1] = 0;
//new nxdn stuff
state->nxdn_part_of_frame = 0;
state->nxdn_part_of_frame = 3;
state->nxdn_ran = 0;
state->nxdn_sf = 0;
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
@@ -636,6 +636,7 @@
opts->wav_out_file[0] = 0;
opts->wav_out_fileR[0] = 0;
opts->wav_out_file_raw[0] = 0;
opts->wav_custom_tag[0] = 0;
opts->symbol_out_file[0] = 0;
opts->lrrp_out_file[0] = 0;
opts->event_out_file[0] = 0;
@@ -678,6 +679,7 @@
opts->uvquality = 3;
opts->inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
opts->inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
opts->inverted_nxdn = 0; //only seek pos by default to reduce false sync pattern detections
opts->mod_threshold = 26;
opts->ssize = 128; //36 default, max is 128, much cleaner data decodes on Phase 2 cqpsk at max
opts->msize = 1024; //15 default, max is 1024, much cleaner data decodes on Phase 2 cqpsk at max
@@ -688,6 +690,7 @@
//all RTL user options -- enabled AGC by default due to weak signal related issues
opts->rtl_dev_index = 0; //choose which device we want by index number
opts->rtl_gain_value = 0; //mid value, 0 - AGC - 0 to 49 acceptable values
opts->rtl_gain_actual = -100; //mirror dongle.gain actual values -100 is AUTO_GAIN, else nearest_gain(dongle.dev, dongle.gain);
opts->rtl_squelch_level = 100; //100 by default, but only affects NXDN and dPMR during framesync test, compared to RMS value
opts->rtl_volume_multiplier = 2; //sample multiplier; This multiplies the sample value to produce a higher 'inlvl' for the demodulator
opts->rtl_udp_port = 0; //set UDP port for RTL remote -- 0 by default, will be making this optional for some external/legacy use cases (edacs-fm, etc)
@@ -1185,7 +1188,9 @@
//trunking
memset (state->trunk_lcn_freq, 0, sizeof(state->trunk_lcn_freq));
memset (state->trunk_chan_map, 0, sizeof(state->trunk_chan_map));
state->group_tally = 0;
//allocate max 24-bit address w/ memory with zero fill (termination) for end of strings, import will only copy 98, leaving the terminator intact
state->group_array = calloc(16778000, sizeof(groupinfo));
state->group_tally = 0; //is an unsigned int
state->lcn_freq_count = 0; //number of frequncies imported as an enumerated lcn list
state->lcn_freq_roll = 0; //needs reset if sync is found?
state->last_cc_sync_time = time(NULL);
@@ -1201,7 +1206,7 @@
state->tg_hold = 0;
//new nxdn stuff
state->nxdn_part_of_frame = 0;
state->nxdn_part_of_frame = 3;
state->nxdn_ran = 0;
state->nxdn_sf = 0;
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
@@ -1432,6 +1437,8 @@
printf (" -w <file> Output synthesized speech to a single static .wav file. (Do not use with -P Per Call Switch)\n");
printf (" -P Enable Per Call WAV file saving. (Do not use with -w filename.wav single wav file switch)\n");
printf (" (Per Call works with everything now and doesn't require ncurses terminal!)\n");
printf (" -j <string> Custom Tag(s), colon delimited, to append to Per Call decoded .wav files.\n");
printf (" -j TGT_LAPD:TAG_FIRE:FREQ_851987500 (Total 3 Tags, Maximum Letter Length = 100)\n");
printf (" -a Enable Call Alert Beep\n");
printf (" (Warning! Might be annoying.)\n");
printf (" -J <file> Specify Filename for Event Log Output.\n");
@@ -1489,6 +1496,11 @@
printf (" -xx Expect non-inverted X2-TDMA signal\n");
printf (" -xr Expect inverted DMR signal\n");
printf (" -xd Expect inverted ICOM dPMR signal\n");
#ifdef NXDN_OLD_SYNC
//
#else
printf (" -xn Expect inverted NXDN/DCR/IDAS signal\n");
#endif
printf ("\n");
printf (" * denotes frame types that cannot be auto-detected.\n");
printf (" ** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.\n");
@@ -1767,10 +1779,10 @@
if (opts->static_wav_file == 0)
{
if (opts->wav_out_f != NULL)
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
if (opts->wav_out_fR != NULL)
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
}
else if (opts->static_wav_file == 1)
@@ -1882,11 +1894,11 @@
init_audio_filters(&state); //audio filters
init_rrc_filter_memory(); //initialize input filtering
InitAllFecFunction();
CNXDNConvolution_init();
exitflag = 0;
while ((c = getopt (argc, argv, "~yhaepPqs:t:v:z:i:o:d:c:g:n:w:B:C:R:f:m:u:x:A:S:G:D:L:V:U:YK:b:H:X:M:NQ:WrlZTF@:!:01:2:345:6:^:7:8_:9:Ek:I:J:O+:")) != -1)
while ((c = getopt (argc, argv, "~yhaepPqs:t:v:z:i:o:d:c:g:n:w:B:C:R:f:m:u:x:A:S:G:D:L:V:U:YK:b:H:X:M:NQ:WrlZTF@:!:01:2:345:6:^:7:8_:9:Ek:I:J:O+:j:")) != -1)
{
switch (c)
@@ -1900,7 +1912,7 @@
opts.call_alert = 1;
break;
//Free'd up switches include: j, M,
//NOTE: Don't use -*, found it exhibits unusual behavior, was setting
//itself to -3 for some reason, may be reading contents of directory
@@ -1909,6 +1921,12 @@
//NOTE: The 'K' option for single BP key has been swapped to 'b'
//'K' is now used for hexidecimal key.csv imports
case 'j': //custom wav tags appended to per-call wav files
strncpy(opts.wav_custom_tag, optarg, 99);
opts.wav_custom_tag[99] = '\0';
fprintf(stderr, "Appending Tags %s to per-call wav files. \n", opts.wav_custom_tag);
break;
//this is a debug option hidden from users, but use it to replay .bin files on loop
case '~':
@@ -1928,10 +1946,10 @@
fprintf (stderr, "TG Hold set to %d \n", state.tg_hold);
break;
//experimental audio monitoring
//raw audio monitoring
case '8':
opts.monitor_input_audio = 1;
fprintf (stderr,"Experimental Raw Analog Source Monitoring Enabled (Pulse Audio Only!)\n");
fprintf (stderr,"Raw Analog Source Monitoring Enabled.\n");
break;
//rc4 enforcement on DMR (due to missing the PI header)
@@ -2757,6 +2775,11 @@
sprintf (opts.output_name, "NXDN48");
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n");
#ifdef NXDN_OLD_SYNC
//
#else
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
#endif
}
else if (optarg[0] == 'y')
{
@@ -2892,6 +2915,7 @@
// opts.setmod_bw = 12000; //causing issues
sprintf (opts.output_name, "NXDN96");
fprintf (stderr,"Decoding only NXDN 9600 baud frames.\n");
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
}
else if (optarg[0] == 'r')
{
@@ -3077,6 +3101,15 @@
opts.inverted_dpmr = 1;
fprintf (stderr, "Expecting inverted ICOM dPMR signals.\n");
}
else if (optarg[0] == 'n')
{
opts.inverted_nxdn = 1;
#ifdef NXDN_OLD_SYNC
//
#else
fprintf (stderr, "Expecting inverted NXDN/DCR/IDAS signals.\n");
#endif
}
break;
case 'r':
@@ -3088,7 +3121,6 @@
opts.dmr_stereo = 0;
state.dmr_stereo = 0;
sprintf (opts.output_name, "MBE Playback");
state.optind = optind;
break;
case 'l':
opts.use_cosine_filter = 0;
@@ -3098,6 +3130,10 @@
exit (0);
}
}
// Set optind after getopt completes so -r works regardless of argument order
if (opts.playfiles == 1)
state.optind = optind;
if (opts.resume > 0)
{
@@ -3532,5 +3568,5 @@
return (0);
}
//Tag: 2025
//Tag: 2026

View File

@@ -93,7 +93,7 @@ void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
//static wav file only, handled by playSynthesizedVoiceMS
//NOTE: if using -o null, playSynthesizedVoiceMS will not write to static wav file
//Per call will work, but will end up with a single file with no meta info
if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1)
if (opts->wav_out_f != NULL && (opts->dmr_stereo_wav == 1 || opts->static_wav_file == 1))
{
writeSynthesizedVoice (opts, state);
}
@@ -141,7 +141,7 @@ void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
//static wav file only, handled by playSynthesizedVoiceMS
//NOTE: if using -o null, playSynthesizedVoiceMS will not write to static wav file
//Per call will work, but will end up with a single file with no meta info
if (opts->wav_out_f != NULL && opts->dmr_stereo_wav == 1)
if (opts->wav_out_f != NULL && (opts->dmr_stereo_wav == 1 || opts->static_wav_file == 1))
{
writeSynthesizedVoice (opts, state);
}
@@ -535,7 +535,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
state->bit_counterL = 0;
//unpack the octets into a bit-wise keystream
unpack_byte_array_into_bit_array(state->ks_octetL+8, state->ks_bitstreamL, 15*8);
unpack_byte_array_into_bit_array(state->ks_octetL+16, state->ks_bitstreamL, 14*16);
//reset flag to 0
state->nxdn_new_iv = 0;
@@ -546,7 +546,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
if (state->bit_counterL > (1568-49))
state->bit_counterL = (1568-49);
//Keystream creation is currently inside of the NXDN_decode_VCALL_IV function
//Keystream Application (no +7 discard like DMRA)
for (i = 0; i < 49; i++)
ambe_d[i] ^= state->ks_bitstreamL[state->bit_counterL++];
@@ -674,10 +674,15 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
pos = state->DMRvcL * 49;
for(i = 0; i < 49; i++)
if (memcmp(ambe_d, ambe_silence, 49) == 0)
pos += 49;
else
{
ambe_d[i] ^= pN[pos];
pos++;
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
}
state->DMRvcL++;
}
@@ -1136,10 +1141,15 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
}
pos = state->DMRvcR * 49;
for(i = 0; i < 49; i++)
if (memcmp(ambe_d, ambe_silence, 49) == 0)
pos += 49;
else
{
ambe_d[i] ^= pN[pos];
pos++;
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
}
state->DMRvcR++;
}

View File

@@ -8,322 +8,6 @@
#include "dsd.h"
static const int PARITY[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};
// trellis_1_2 encode: source is in bits, result in bits
void trellis_encode(uint8_t result[], const uint8_t source[], int result_len, int reg)
{
for (int i=0; i<result_len; i+=2) {
reg = (reg << 1) | source[i>>1];
result[i] = PARITY[reg & 0x19];
result[i+1] = PARITY[reg & 0x17];
}
}
// simplified trellis 2:1 decode; source and result in bits
// assumes that encoding was done with NTEST trailing zero bits
// result_len should be set to the actual number of data bits
// in the original unencoded message (excl. these trailing bits)
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len)
{
int reg = 0;
int min_d;
int min_bt;
static const int NTEST = 4;
static const int NTESTC = 1 << NTEST;
uint8_t bt[NTEST];
uint8_t tt[NTEST*2];
int dstats[4];
int sum;
for (int p=0; p < 4; p++)
dstats[p] = 0;
for (int p=0; p < result_len; p++) {
for (int i=0; i<NTESTC; i++) {
bt[0] = (i&8)>>3;
bt[1] = (i&4)>>2;
bt[2] = (i&2)>>1;
bt[3] = (i&1);
trellis_encode(tt, bt, NTEST*2, reg);
sum=0;
for (int j=0; j<NTEST*2; j++) {
sum += tt[j] ^ source[p*2+j];
}
if (i == 0 || sum < min_d) {
min_d = sum;
min_bt = bt[0];
}
}
result[p] = min_bt;
reg = (reg << 1) | min_bt;
dstats[(min_d > 3) ? 3 : min_d] += 1;
}
//debug output
// fprintf (stderr, "\n stats\t%d %d %d %d\n", dstats[0], dstats[1], dstats[2], dstats[3]);
}
//Original Copyright/License
/* -*- c++ -*- */
/*
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
*
* This file is part of OP25
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
//Ripped from libM17
#define K 5 //constraint length
#define NUM_STATES (1 << (K - 1)) //number of states
static uint32_t prevMetrics[NUM_STATES];
static uint32_t currMetrics[NUM_STATES];
static uint32_t prevMetricsData[NUM_STATES];
static uint32_t currMetricsData[NUM_STATES];
static uint16_t viterbi_history[244];
/**
* @brief Decode unpunctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param len Input length in bits.
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
{
if(len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
viterbi_reset();
size_t pos = 0;
for(size_t i = 0; i < len; i += 2)
{
uint16_t s0 = in[i];
uint16_t s1 = in[i + 1];
viterbi_decode_bit(s0, s1, pos);
pos++;
}
uint32_t err = viterbi_chainback(out, pos, len/2);
//debug
// fprintf (stderr, "\n vcb: \n");
// for (size_t i = 0; i < len; i++)
// fprintf (stderr, "%02X", out[i]);
return err;
}
/**
* @brief Decode punctured convolutionally encoded data.
*
* @param out Destination array where decoded data is written.
* @param in Input data.
* @param punct Puncturing matrix.
* @param in_len Input data length.
* @param p_len Puncturing matrix length (entries).
* @return Number of bit errors corrected.
*/
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
{
if(in_len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
uint16_t umsg[244*2]; //unpunctured message
uint8_t p=0; //puncturer matrix entry
uint16_t u=0; //bits count - unpunctured message
uint16_t i=0; //bits read from the input message
while(i<in_len)
{
if(punct[p])
{
umsg[u]=in[i];
i++;
}
else
{
umsg[u]=0x7FFF;
}
u++;
p++;
p%=p_len;
}
//debug
// fprintf (stderr, " p: %d, u: %d; p_len: %d; len: %d;", p, u, p_len, (u-in_len)*0x7FFF);
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
}
/**
* @brief Decode one bit and update trellis.
*
* @param s0 Cost of the first symbol.
* @param s1 Cost of the second symbol.
* @param pos Bit position in history.
*/
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos)
{
static const uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
static const uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0, 0, 0xFFFF, 0xFFFF, 0};
for(uint8_t i = 0; i < NUM_STATES/2; i++)
{
uint32_t metric = q_abs_diff(COST_TABLE_0[i], s0)
+ q_abs_diff(COST_TABLE_1[i], s1);
uint32_t m0 = prevMetrics[i] + metric;
uint32_t m1 = prevMetrics[i + NUM_STATES/2] + (0x1FFFE - metric);
uint32_t m2 = prevMetrics[i] + (0x1FFFE - metric);
uint32_t m3 = prevMetrics[i + NUM_STATES/2] + metric;
uint8_t i0 = 2 * i;
uint8_t i1 = i0 + 1;
if(m0 >= m1)
{
viterbi_history[pos]|=(1<<i0);
currMetrics[i0] = m1;
}
else
{
viterbi_history[pos]&=~(1<<i0);
currMetrics[i0] = m0;
}
if(m2 >= m3)
{
viterbi_history[pos]|=(1<<i1);
currMetrics[i1] = m3;
}
else
{
viterbi_history[pos]&=~(1<<i1);
currMetrics[i1] = m2;
}
}
//swap
uint32_t tmp[NUM_STATES];
for(uint8_t i=0; i<NUM_STATES; i++)
{
tmp[i]=currMetrics[i];
}
for(uint8_t i=0; i<NUM_STATES; i++)
{
currMetrics[i]=prevMetrics[i];
prevMetrics[i]=tmp[i];
}
}
/**
* @brief History chainback to obtain final byte array.
*
* @param out Destination byte array for decoded data.
* @param pos Starting position for the chainback.
* @param len Length of the output in bits.
* @return Minimum Viterbi cost at the end of the decode sequence.
*/
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
{
uint8_t state = 0;
size_t bitPos = len+4;
memset(out, 0, (len-1)/8+1);
while(pos > 0)
{
bitPos--;
pos--;
uint16_t bit = viterbi_history[pos]&((1<<(state>>4)));
state >>= 1;
if(bit)
{
state |= 0x80;
out[bitPos/8]|=1<<(7-(bitPos%8));
}
}
uint32_t cost = prevMetrics[0];
for(size_t i = 0; i < NUM_STATES; i++)
{
uint32_t m = prevMetrics[i];
if(m < cost) cost = m;
}
//debug
// fprintf (stderr, "\n cb: \n");
// for (size_t i = 0; i < len; i++)
// fprintf (stderr, "%02X", out[i]);
return cost;
}
/**
* @brief Reset the decoder state. No args.
*
*/
void viterbi_reset(void)
{
memset((uint8_t*)viterbi_history, 0, 2*244);
memset((uint8_t*)currMetrics, 0, 4*NUM_STATES);
memset((uint8_t*)prevMetrics, 0, 4*NUM_STATES);
memset((uint8_t*)currMetricsData, 0, 4*NUM_STATES);
memset((uint8_t*)prevMetricsData, 0, 4*NUM_STATES);
}
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
{
if(v2 > v1) return v2 - v1;
return v1 - v2;
}
//original sources
//--------------------------------------------------------------------
// M17 C library - decode/viterbi.c
//
// This file contains:
// - the Viterbi decoder
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// M17 C library - math/math.c
//
// This file contains:
// - absolute difference value
// - Euclidean norm (L2) calculation for n-dimensional vectors (float)
// - soft-valued arrays to integer conversion (and vice-versa)
// - fixed-valued multiplication and division
//
// Wojciech Kaczmarski, SP5WWP
// M17 Project, 29 December 2023
//--------------------------------------------------------------------
//audio filter stuff sourced from: https://github.com/NedSimao/FilteringLibrary
//no license / information provided in source code
#define PI 3.141592653
@@ -337,7 +21,6 @@ uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
// #define HFC 50
// #define LFC 100
void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
{
@@ -351,7 +34,6 @@ void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
}
float LPFilter_Update(LPFilter *filter, float v_in)
{
@@ -382,7 +64,6 @@ void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS)
}
float HPFilter_Update(HPFilter *filter, float v_in)
{
@@ -592,130 +273,3 @@ long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as
if (rms < 0) rms = 150;
return rms;
}
//same as symbol levels in m17.h (m17-fme version)
const int8_t symbol_list[4]={-3, -1, +1, +3};
/**
* @brief Slice payload symbols into soft dibits.
* Input (RRC filtered baseband sampled at symbol centers)
* should be already normalized to {-3, -1, +1 +3}.
* @param out Soft valued dibits (type-4).
* @param inp Array of 184 floats (1 sample per symbol).
*/
void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD])
{
for(uint8_t i=0; i<SYM_PER_PLD; i++)
{
//bit 0
if(inp[i]>=symbol_list[3])
{
out[i*2+1]=0xFFFF;
}
else if(inp[i]>=symbol_list[2])
{
out[i*2+1]=-(float)0xFFFF/(symbol_list[3]-symbol_list[2])*symbol_list[2]+inp[i]*(float)0xFFFF/(symbol_list[3]-symbol_list[2]);
}
else if(inp[i]>=symbol_list[1])
{
out[i*2+1]=0x0000;
}
else if(inp[i]>=symbol_list[0])
{
out[i*2+1]=(float)0xFFFF/(symbol_list[1]-symbol_list[0])*symbol_list[1]-inp[i]*(float)0xFFFF/(symbol_list[1]-symbol_list[0]);
}
else
{
out[i*2+1]=0xFFFF;
}
//bit 1
if(inp[i]>=symbol_list[2])
{
out[i*2]=0x0000;
}
else if(inp[i]>=symbol_list[1])
{
out[i*2]=0x7FFF-inp[i]*(float)0xFFFF/(symbol_list[2]-symbol_list[1]);
}
else
{
out[i*2]=0xFFFF;
}
}
}
/**
* @brief Soft logic NOT.
*
* @param a Input A.
* @return uint16_t Output = not A.
*/
uint16_t soft_bit_NOT(const uint16_t a)
{
return 0xFFFFU-a;
}
//randomizing pattern
const uint8_t rand_seq[46]=
{
0xD6, 0xB5, 0xE2, 0x30, 0x82, 0xFF, 0x84, 0x62, 0xBA, 0x4E, 0x96, 0x90, 0xD8, 0x98, 0xDD, 0x5D, 0x0C, 0xC8, 0x52, 0x43, 0x91, 0x1D, 0xF8,
0x6E, 0x68, 0x2F, 0x35, 0xDA, 0x14, 0xEA, 0xCD, 0x76, 0x19, 0x8D, 0xD5, 0x80, 0xD1, 0x33, 0x87, 0x13, 0x57, 0x18, 0x2D, 0x29, 0x78, 0xC3
};
/**
* @brief Randomize type-4 soft bits.
*
* @param inp Input 368 soft type-4 bits.
*/
void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2])
{
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
{
if((rand_seq[i/8]>>(7-(i%8)))&1) //flip bit if '1'
{
inp[i]=soft_bit_NOT(inp[i]);
}
}
}
//interleaver pattern
const uint16_t intrl_seq[SYM_PER_PLD*2]=
{
0, 137, 90, 227, 180, 317, 270, 39, 360, 129, 82, 219, 172, 309, 262, 31,
352, 121, 74, 211, 164, 301, 254, 23, 344, 113, 66, 203, 156, 293, 246, 15,
336, 105, 58, 195, 148, 285, 238, 7, 328, 97, 50, 187, 140, 277, 230, 367,
320, 89, 42, 179, 132, 269, 222, 359, 312, 81, 34, 171, 124, 261, 214, 351,
304, 73, 26, 163, 116, 253, 206, 343, 296, 65, 18, 155, 108, 245, 198, 335,
288, 57, 10, 147, 100, 237, 190, 327, 280, 49, 2, 139, 92, 229, 182, 319,
272, 41, 362, 131, 84, 221, 174, 311, 264, 33, 354, 123, 76, 213, 166, 303,
256, 25, 346, 115, 68, 205, 158, 295, 248, 17, 338, 107, 60, 197, 150, 287,
240, 9, 330, 99, 52, 189, 142, 279, 232, 1, 322, 91, 44, 181, 134, 271,
224, 361, 314, 83, 36, 173, 126, 263, 216, 353, 306, 75, 28, 165, 118, 255,
208, 345, 298, 67, 20, 157, 110, 247, 200, 337, 290, 59, 12, 149, 102, 239,
192, 329, 282, 51, 4, 141, 94, 231, 184, 321, 274, 43, 364, 133, 86, 223,
176, 313, 266, 35, 356, 125, 78, 215, 168, 305, 258, 27, 348, 117, 70, 207,
160, 297, 250, 19, 340, 109, 62, 199, 152, 289, 242, 11, 332, 101, 54, 191,
144, 281, 234, 3, 324, 93, 46, 183, 136, 273, 226, 363, 316, 85, 38, 175,
128, 265, 218, 355, 308, 77, 30, 167, 120, 257, 210, 347, 300, 69, 22, 159,
112, 249, 202, 339, 292, 61, 14, 151, 104, 241, 194, 331, 284, 53, 6, 143,
96, 233, 186, 323, 276, 45, 366, 135, 88, 225, 178, 315, 268, 37, 358, 127,
80, 217, 170, 307, 260, 29, 350, 119, 72, 209, 162, 299, 252, 21, 342, 111,
64, 201, 154, 291, 244, 13, 334, 103, 56, 193, 146, 283, 236, 5, 326, 95,
48, 185, 138, 275, 228, 365, 318, 87, 40, 177, 130, 267, 220, 357, 310, 79,
32, 169, 122, 259, 212, 349, 302, 71, 24, 161, 114, 251, 204, 341, 294, 63,
16, 153, 106, 243, 196, 333, 286, 55, 8, 145, 98, 235, 188, 325, 278, 47
};
/**
* @brief Reorder (interleave) 368 soft bits.
*
* @param outp Reordered soft bits.
* @param inp Input soft bits.
*/
void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_PLD*2])
{
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
outp[i]=inp[intrl_seq[i]];
}

View File

@@ -230,6 +230,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
opts->inverted_dpmr = 1;
opts->inverted_x2tdma = 1;
opts->inverted_ysf = 1;
opts->inverted_nxdn = 1;
}
else
{
@@ -237,6 +238,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
opts->inverted_dpmr = 0;
opts->inverted_x2tdma = 0;
opts->inverted_ysf = 0;
opts->inverted_nxdn = 0;
}
}
@@ -377,8 +379,8 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
if (c == 112) //'p' key - stop all per call wav files //TODO: Fix
{
//TODO: Add Closing of RAW files as well?
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling
opts->wav_out_fileR[0] = 0;
opts->dmr_stereo_wav = 0;
@@ -592,7 +594,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
else opts->trunk_tune_enc_calls = 1;
}
if (opts->p25_trunk == 1 && c == 103) //'g' key, toggle tune group calls
if (opts->p25_trunk == 1 && c == 84) //'T' key, toggle tune group calls
{
if (opts->trunk_tune_group_calls == 1) opts->trunk_tune_group_calls = 0;
else opts->trunk_tune_group_calls = 1;
@@ -916,6 +918,26 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
if (c == 123)
opts->rtlsdr_ppm_error--;
//RTL Gain Manual Adjustment
if (c == 103)
{
if (opts->rtl_gain_value > 0)
{
opts->rtl_gain_value--;
opts->rtl_gain_actual = 0;
}
}
if (c == 71)
{
if (opts->rtl_gain_value < 49)
{
opts->rtl_gain_value++;
opts->rtl_gain_actual = 0;
}
}
//anything with an entry box will need the inputs and outputs stopped first
//so probably just write a function to handle c input, and when c = certain values
//needing an entry box, then stop all of those

View File

@@ -694,8 +694,8 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
if (choicec == 12)
{
//TODO: Add Closing of RAW files as well?
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[1]);
opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling
opts->wav_out_fileR[0] = 0;
opts->dmr_stereo_wav = 0;

View File

@@ -245,10 +245,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->audio_in_type == 3)
{
printw ("| RTL: %d;", opts->rtl_dev_index);
if (opts->rtl_gain_value == 0)
if (opts->rtl_gain_actual == -100)
printw (" G: AGC;");
else
printw (" G: %idB;", opts->rtl_gain_value);
printw (" G: %i/%i;", opts->rtl_gain_value, opts->rtl_gain_actual);
printw (" V: %iX;", opts->rtl_volume_multiplier);
printw (" PPM: %i;", opts->rtlsdr_ppm_error); //Adjust manually now with { and }
printw (" SQ: %i;", opts->rtl_squelch_level);
@@ -363,7 +363,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
{
printw ("| Trunking -");
if (opts->trunk_tune_group_calls == 0) attron(COLOR_PAIR(2));
printw (" Group(g)");
printw (" Group(T)");
attron(COLOR_PAIR(4));
if (opts->trunk_tune_private_calls == 0) attron(COLOR_PAIR(2));
printw (" Private(u)");
@@ -382,8 +382,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if (opts->p25_trunk == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3) )
{
printw ("| Trunking -");
if (opts->trunk_tune_group_calls == 0) printw (" group(g)");
else printw (" GROUP(g)");
if (opts->trunk_tune_group_calls == 0) printw (" group(T)");
else printw (" GROUP(T)");
if (opts->trunk_tune_private_calls == 0) printw (" private(u)");
else printw (" PRIVATE(u)");
if (opts->trunk_tune_data_calls == 0) printw (" data(d)");

View File

@@ -1098,7 +1098,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1214,7 +1214,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1321,7 +1321,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1501,7 +1501,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1757,7 +1757,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -1870,7 +1870,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -2166,7 +2166,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
if (is_digital == 1) {} //just write to already open temp file to be renamed later
else //close the temp 8k wav file and open as 48k
{
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 48000, 0);
}
}
@@ -2312,7 +2312,7 @@ void eot_cc(dsd_opts * opts, dsd_state * state)
if (opts->dmr_stereo_wav == 1)
{
if (opts->wav_out_f != NULL)
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]);
opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, opts->wav_custom_tag, &state->event_history_s[0]);
opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0);
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -506,16 +506,16 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
fprintf (stderr, " MFIDA4 (Harris) GPS Block 1");
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
memcpy(state->dmr_pdu_sf[0], LCW_bits, 16*sizeof(uint8_t)); //opcode and mfid for check
memcpy(state->dmr_pdu_sf[0]+40, LCW_bits+16, 56*sizeof(uint8_t)); //+40 offset to match the vPDU decoder
memcpy(state->dmr_pdu_sf[0]+16, LCW_bits+16, 56*sizeof(uint8_t)); //contents of message in this block
}
else if (lc_mfid == 0xA4 && lc_opcode == 0x2B)
{
fprintf (stderr, " MFIDA4 (Harris) GPS Block 2");
memcpy(state->dmr_pdu_sf[0]+40+56, LCW_bits+16, 56*sizeof(uint8_t)); //+40 +56 to offset first block
memcpy(state->dmr_pdu_sf[0]+16+56, LCW_bits+16, 56*sizeof(uint8_t)); //+16 +56 to offset first block
uint16_t check = (uint16_t)ConvertBitIntoBytes(&state->dmr_pdu_sf[0][0], 16);
if (check == 0x2AA4)
nmea_harris(opts, state, state->dmr_pdu_sf[0], (uint32_t)state->lastsrc, 0);
harris_lptt(opts, state, state->dmr_pdu_sf[0]+16, (uint32_t)state->lastsrc, 0, 1);
else fprintf (stderr, " Missing GPS Block 1");
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
}
@@ -538,6 +538,15 @@ void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t ir
tait_iso7_embedded_alias_decode(opts, state, 0, 8, LCW_bits);
}
//observed alongside Tait Talker Alias ID: 10T
else if (lc_mfid == 0xD8 && lc_format == 0x01)
{
uint32_t wacn = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 20);
uint32_t sys = (uint32_t)ConvertBitIntoBytes(&LCW_bits[36], 12);
uint32_t rid = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
fprintf (stderr, " MFIDD8 (Tait) Subscriber FQ-SUID: %05X.%03X.%d", wacn, sys, rid);
}
//not a duplicate, this one will print if not MFID 0 or 1
else
{

View File

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

View File

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

View File

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

View File

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

View File

@@ -465,27 +465,27 @@ void dc_block_filter(struct demod_state *fm)
}
long int rms(int16_t *samples, int len, int step)
/* largely lifted from rtl_power */
{
int i;
long int rms;
long p, t, s;
double dc, err;
long int rms_val;
int64_t p = 0, t = 0;
int64_t s;
p = t = 0L;
for (i=0; i<len; i+=step) {
s = (long)samples[i];
for (i = 0; i < len; i += step) {
s = samples[i];
t += s;
p += s * s;
}
/* correct for dc offset in squares */
dc = (double)(t*step) / (double)len;
err = t * 2 * dc - dc * dc * len;
rms = (long int)sqrt((p-err) / len);
//going with a value that's easy to figure out when its done the thing
if (rms < 0){ rms = 999; }
return rms;
double dc = (double)(t * (int64_t)step) / (double)len;
double err = (double)t * 2.0 * dc - dc * dc * (double)len;
double val = ((double)p - err) / (double)len;
if (val < 0.0) val = 0.0;
rms_val = (long int)sqrt(val);
if (rms_val < 0) rms_val = 999;
return rms_val;
}
void full_demod(struct demod_state *d)
@@ -1031,6 +1031,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
//this needs to be initted first, then we set the parameters
dongle_init(&dongle);
//init with low pass if decoding P25 or EDACS/Provoice
if (opts->frame_p25p1 == 1 || opts->frame_p25p2 == 1 || opts->frame_provoice == 1)
demod_init_ro2(&demod);
@@ -1089,7 +1090,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
verbose_auto_gain(dongle.dev);
fprintf (stderr, "Setting RTL Autogain. \n");
} else {
dongle.gain = nearest_gain(dongle.dev, dongle.gain);
opts->rtl_gain_actual = dongle.gain = nearest_gain(dongle.dev, dongle.gain);
verbose_gain_set(dongle.dev, dongle.gain);
// fprintf (stderr, "Setting RTL Nearest Gain to %d. \n", dongle.gain); //seems to be working now
}
@@ -1151,6 +1152,26 @@ int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state)
verbose_ppm_set(dongle.dev, dongle.ppm_error);
}
//if Gain or AGC Value is Manually Changed, Change it here now
if (opts->rtl_gain_actual != dongle.gain && opts->rtl_gain_value != 0)
{
dongle.gain = (opts->rtl_gain_value * 10);
opts->rtl_gain_actual = dongle.gain = nearest_gain(dongle.dev, dongle.gain);
verbose_gain_set(dongle.dev, dongle.gain);
//debug
// fprintf (stderr, "\n Dongle Gain Set: %d; \n", dongle.gain);
}
else if (opts->rtl_gain_value == 0 && dongle.gain != AUTO_GAIN)
{
opts->rtl_gain_actual = dongle.gain = AUTO_GAIN;
verbose_auto_gain(dongle.dev);
//debug
// fprintf (stderr, "\n AGC Set; \n");
}
while (output.queue.empty())
{
struct timespec ts;
@@ -1193,20 +1214,18 @@ void rtl_dev_tune(dsd_opts * opts, long int frequency)
}
//return RMS value (root means square) power level -- used as soft squelch inside of framesync
static long int prev_rms = 0;
long int rtl_return_rms()
{
long int sr = 0;
// #ifdef __arm__
// sr = 100;
// #else
//debug -- on main machine, lp_len is around 6420, so this probably contributes to very high CPU usage
// fprintf (stderr, "LP_LEN: %d \n", demod.lp_len);
//I've found that just using a sample size of 160 will give us a good approximation without killing the CPU
// sr = rms(demod.lowpassed, demod.lp_len, 1);
sr = rms(demod.lowpassed, 160, 1); //I wonder what a reasonable value would be for #2 (input len) there
// #endif
return (sr);
long int sr = rms(demod.lowpassed, 512, 1); //change 512 to 160 if using too much CPU
// simple 4-block IIR average for stability
sr = (prev_rms * 3 + sr) / 4;
prev_rms = sr;
return sr;
}
//simple function to clear the rtl sample queue when tuning and during other events (ncurses menu open/close)

File diff suppressed because it is too large Load Diff

541
src/soft_viterbi_k5.c Normal file
View File

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

205
src/ysf.c
View File

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