mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 17:27:21 -05:00
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;
This commit is contained in:
@@ -39,6 +39,55 @@ 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 -DSID=ON to enable P25p1 Soft ID decoding -- Experimental
|
||||
option(SID
|
||||
"Build with P25p1 LSD/Soft ID Enabled" OFF)
|
||||
|
||||
@@ -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
|
||||
@@ -311,70 +311,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.
|
||||
@@ -1,4 +1,4 @@
|
||||
Build Version: AW 2025-209-g14edf32
|
||||
Build Version: AW 2026-1-g99f4228
|
||||
MBElib Version: 1.3.4
|
||||
CODEC2 Support Enabled
|
||||
|
||||
@@ -103,6 +103,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.
|
||||
|
||||
@@ -354,6 +354,7 @@ typedef struct
|
||||
int uvquality;
|
||||
int inverted_x2tdma;
|
||||
int inverted_dmr;
|
||||
int inverted_nxdn;
|
||||
int mod_threshold;
|
||||
int ssize;
|
||||
int msize;
|
||||
@@ -1262,25 +1263,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);
|
||||
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);
|
||||
@@ -1290,12 +1291,8 @@ 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
|
||||
|
||||
/* 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 +1308,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,
|
||||
@@ -1321,17 +1321,21 @@ 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);
|
||||
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);
|
||||
//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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1141,8 +1141,56 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
}
|
||||
|
||||
//NXDN
|
||||
else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
|
||||
// //NXDN (previous sync, revert if issues / complaints arise)
|
||||
// else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
|
||||
// {
|
||||
// strncpy (synctest10, (synctest_p - 9), 10); //FSW only
|
||||
// if (
|
||||
// (strcmp (synctest10, "3131331131") == 0 ) //this seems to be the most common 'correct' pattern on Type-C
|
||||
// || (strcmp (synctest10, "3331331131") == 0 ) //this one hits on new sync but gives a bad lich code
|
||||
// || (strcmp (synctest10, "3131331111") == 0 )
|
||||
// || (strcmp (synctest10, "3331331111") == 0 )
|
||||
// || (strcmp (synctest10, "3131311131") == 0 ) //First few FSW on NXDN48 Type-C seems to hit this for some reason
|
||||
|
||||
// )
|
||||
// {
|
||||
|
||||
// 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);
|
||||
// }
|
||||
// state->lastsynctype = 28;
|
||||
// }
|
||||
|
||||
// else if (
|
||||
|
||||
// (strcmp (synctest10, "1313113313") == 0 )
|
||||
// || (strcmp (synctest10, "1113113313") == 0 )
|
||||
// || (strcmp (synctest10, "1313113333") == 0 )
|
||||
// || (strcmp (synctest10, "1113113333") == 0 )
|
||||
// || (strcmp (synctest10, "1313133313") == 0 )
|
||||
|
||||
// )
|
||||
// {
|
||||
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
|
||||
//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 (
|
||||
@@ -1155,38 +1203,44 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
)
|
||||
{
|
||||
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
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);
|
||||
}
|
||||
state->lastsynctype = 28;
|
||||
}
|
||||
|
||||
else if (
|
||||
}
|
||||
|
||||
(strcmp (synctest10, "1313113313") == 0 )
|
||||
|| (strcmp (synctest10, "1113113313") == 0 )
|
||||
|| (strcmp (synctest10, "1313113333") == 0 )
|
||||
|| (strcmp (synctest10, "1113113333") == 0 )
|
||||
|| (strcmp (synctest10, "1313133313") == 0 )
|
||||
//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;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Provoice Conventional -- Some False Positives due to shortened frame sync pattern, so use squelch if possible
|
||||
|
||||
@@ -678,6 +678,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
|
||||
@@ -1489,6 +1490,7 @@
|
||||
printf (" -xx Expect non-inverted X2-TDMA signal\n");
|
||||
printf (" -xr Expect inverted DMR signal\n");
|
||||
printf (" -xd Expect inverted ICOM dPMR signal\n");
|
||||
printf (" -xn Expect inverted NXDN/DCR/IDAS signal\n");
|
||||
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");
|
||||
@@ -1882,7 +1884,7 @@
|
||||
init_audio_filters(&state); //audio filters
|
||||
init_rrc_filter_memory(); //initialize input filtering
|
||||
InitAllFecFunction();
|
||||
CNXDNConvolution_init();
|
||||
|
||||
|
||||
exitflag = 0;
|
||||
|
||||
@@ -1928,10 +1930,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 +2759,7 @@
|
||||
sprintf (opts.output_name, "NXDN48");
|
||||
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
|
||||
fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n");
|
||||
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
|
||||
}
|
||||
else if (optarg[0] == 'y')
|
||||
{
|
||||
@@ -2892,6 +2895,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 +3081,11 @@
|
||||
opts.inverted_dpmr = 1;
|
||||
fprintf (stderr, "Expecting inverted ICOM dPMR signals.\n");
|
||||
}
|
||||
else if (optarg[0] == 'n')
|
||||
{
|
||||
opts.inverted_nxdn = 1;
|
||||
fprintf (stderr, "Expecting inverted NXDN/DCR/IDAS signals.\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
|
||||
270
src/dsd_misc.c
270
src/dsd_misc.c
@@ -8,84 +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
|
||||
@@ -94,7 +16,7 @@ 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];
|
||||
static uint16_t viterbi_history[400]; //CAC needs more, expand to 400 for testing
|
||||
|
||||
/**
|
||||
* @brief Decode unpunctured convolutionally encoded data.
|
||||
@@ -106,8 +28,9 @@ static uint16_t viterbi_history[244];
|
||||
*/
|
||||
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");
|
||||
//variable in len
|
||||
// if(len > 244*2)
|
||||
// fprintf(stderr, "Input size exceeds max history\n");
|
||||
|
||||
viterbi_reset();
|
||||
|
||||
@@ -142,11 +65,12 @@ uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
|
||||
*/
|
||||
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
|
||||
{
|
||||
if(in_len > 244*2)
|
||||
fprintf(stderr, "Input size exceeds max history\n");
|
||||
//variable len now
|
||||
// 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 umsg[400*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
|
||||
|
||||
@@ -168,7 +92,7 @@ uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " p: %d, u: %d; p_len: %d; len: %d;", p, u, p_len, (u-in_len)*0x7FFF);
|
||||
// fprintf (stderr, " p: %d, u: %d; p_len: %d; ", p, u, p_len); //
|
||||
|
||||
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
|
||||
}
|
||||
@@ -249,7 +173,7 @@ 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);
|
||||
// memset(out, 0, (len-1)/8+1); //upstream bugfix, but arrays passed here are already memset / zeroed out
|
||||
|
||||
while(pos > 0)
|
||||
{
|
||||
@@ -646,6 +570,57 @@ void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
@@ -680,7 +655,7 @@ void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2])
|
||||
}
|
||||
}
|
||||
|
||||
//interleaver pattern
|
||||
//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,
|
||||
@@ -719,3 +694,122 @@ void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_
|
||||
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 dibits 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];
|
||||
|
||||
// memset(viterbi_bytes, 0, sizeof(viterbi_bytes));
|
||||
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];
|
||||
|
||||
// memset(viterbi_bytes, 0, sizeof(viterbi_bytes));
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
2609
src/nxdn_deperm.c
2609
src/nxdn_deperm.c
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,29 @@
|
||||
/*
|
||||
============================================================================
|
||||
Name : nxdn_element.c (formerly nxdn_lib)
|
||||
Author :
|
||||
Version : 1.0
|
||||
Date : 2018 December 26
|
||||
Copyright : No copyright
|
||||
Description : NXDN decoding source lib - modified from nxdn_lib
|
||||
Origin : Originally found at - https://github.com/LouisErigHerve/dsd
|
||||
============================================================================
|
||||
*/
|
||||
/*-------------------------------------------------------------------------------
|
||||
* nxdn_element.c
|
||||
* NXDN Message Decoding
|
||||
*
|
||||
* Reworked portions from NXDN decoding source lib - modified from nxdn_lib
|
||||
* Originally found at - https://github.com/LouisErigHerve/dsd
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2026-01 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
//below items are in cmake as -DASCII=ON (default), or -DBIG5=ON , -DSJIS=ON , or -DUTF16=ON
|
||||
// #define ASCII_DECODE //if using older ASCII only Alias Decoder, and not new one.
|
||||
// #define SJIS_DECODE //if using ASCII/SJIS decoder, and not BIG5 decoder
|
||||
// #define BIG5_DECODE //if using ASCII/BIG5 decoder, and not SJIS decoder
|
||||
// #define UTF16_DECODE //if using straight UTF16 with Alias16 decoder (untested/not observed)
|
||||
|
||||
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
uint8_t SACCH[72]; //72
|
||||
uint8_t sacch_bytes[9];
|
||||
//below sizes are matching largest message carrier (facch2) when
|
||||
//sending to content element decoder, we don't want to go address a memory range out of bounds
|
||||
//so this is a safeguard to prevent doing so. This is same as viterbi bytes and bits sizes.
|
||||
uint8_t SACCH[26*8]; //72
|
||||
uint8_t sacch_bytes[26]; //9
|
||||
|
||||
uint32_t i;
|
||||
uint8_t CrcCorrect = 1;
|
||||
@@ -58,28 +66,14 @@ void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state)
|
||||
|
||||
} /* End NXDN_SACCH_Full_decode() */
|
||||
|
||||
|
||||
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||
uint8_t CrcCorrect, uint8_t * ElementsContent)
|
||||
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t CrcCorrect, uint8_t * ElementsContent)
|
||||
{
|
||||
uint8_t MessageType;
|
||||
/* Get the "Message Type" field */
|
||||
MessageType = (ElementsContent[2] & 1) << 5;
|
||||
MessageType |= (ElementsContent[3] & 1) << 4;
|
||||
MessageType |= (ElementsContent[4] & 1) << 3;
|
||||
MessageType |= (ElementsContent[5] & 1) << 2;
|
||||
MessageType |= (ElementsContent[6] & 1) << 1;
|
||||
MessageType |= (ElementsContent[7] & 1) << 0;
|
||||
|
||||
//Including Flags on bit 0 and bit 1 for more precise usage below
|
||||
uint8_t MessageType = (uint8_t)convert_bits_into_output(ElementsContent+0, 8);
|
||||
|
||||
nxdn_message_type (opts, state, MessageType);
|
||||
|
||||
/* Save the "F1" and "F2" flags */
|
||||
state->NxdnElementsContent.F1 = ElementsContent[0];
|
||||
state->NxdnElementsContent.F2 = ElementsContent[1];
|
||||
|
||||
/* Save the "Message Type" field */
|
||||
state->NxdnElementsContent.MessageType = MessageType;
|
||||
|
||||
/* Set the CRC state */
|
||||
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
|
||||
|
||||
@@ -100,13 +94,13 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||
//observed new messages in #318, should also be noted that F1 and F2 are both set on these messages
|
||||
|
||||
//VCALL and TX_REL custom to certain radios, but they have different elements in them
|
||||
case 0x21:
|
||||
case 0x28:
|
||||
case 0xE1:
|
||||
case 0xE8:
|
||||
NXDN_decode_VCALL_ARIB(opts, state, ElementsContent);
|
||||
break;
|
||||
|
||||
//Shift-JIS Talker Alias
|
||||
case 0x27:
|
||||
case 0xE7:
|
||||
NXDN_decode_ALIAS_ARIB(opts, state, ElementsContent);
|
||||
break;
|
||||
|
||||
@@ -126,9 +120,9 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||
NXDN_decode_VCALL_ASSGN(opts, state, ElementsContent);
|
||||
break;
|
||||
|
||||
//Alias 0x3F
|
||||
//PROP_FORM 0x3F
|
||||
case 0x3F:
|
||||
NXDN_decode_Alias(opts, state, ElementsContent);
|
||||
NXDN_decode_Prop(opts, state, ElementsContent);
|
||||
break;
|
||||
|
||||
//SRV_INFO
|
||||
@@ -143,6 +137,11 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||
NXDN_decode_cch_info(opts, state, ElementsContent);
|
||||
break;
|
||||
|
||||
//DST_ID_INFO
|
||||
case 0x17:
|
||||
nxdn_decode_dst_info(opts, state, ElementsContent);
|
||||
break;
|
||||
|
||||
//SITE_INFO
|
||||
case 0x18:
|
||||
NXDN_decode_site_info(opts, state, ElementsContent);
|
||||
@@ -165,14 +164,11 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||
//DISC
|
||||
case 0x11:
|
||||
NXDN_decode_VCALL(opts, state, ElementsContent);
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
memset (state->generic_talker_alias[0], 0, sizeof(state->generic_talker_alias[0]));
|
||||
sprintf (state->generic_talker_alias[0], "%s", "");
|
||||
sprintf (state->call_string[0], "%s", "");
|
||||
sprintf (state->nxdn_call_type, "%s", "");
|
||||
|
||||
// #ifdef LIMAZULUTWEAKS
|
||||
// ; //do nothing -- testing errors on CAC messages when returning quickly from RTCH
|
||||
// #else
|
||||
|
||||
//tune back to CC here - save about 1-2 seconds
|
||||
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
|
||||
{
|
||||
@@ -749,95 +745,224 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
|
||||
|
||||
} /* End NXDN_decode_VCALL_ASSGN() */
|
||||
|
||||
//Multi Encoding Alias Decoder (ASCII, and SJIS or BIG5 or UTF16BE)
|
||||
void NXDN_decode_Alias16(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
{
|
||||
|
||||
uint8_t mfid = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
|
||||
uint16_t unk = (uint16_t)ConvertBitIntoBytes(&Message[16], 16);
|
||||
|
||||
uint8_t seg_num = (uint8_t)ConvertBitIntoBytes(&Message[32], 4);
|
||||
uint8_t seg_len = (uint8_t)ConvertBitIntoBytes(&Message[36], 4);
|
||||
|
||||
uint8_t seg_bytes[12]; memset(seg_bytes, 0, sizeof(seg_bytes));
|
||||
|
||||
int seg_byte_num = 4;
|
||||
for (int i = 0; i < seg_byte_num; i++)
|
||||
seg_bytes[i] = (uint8_t)ConvertBitIntoBytes(&Message[(i*8)+40], 8);
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "\n Multi Segment Alias %d/%d; ", seg_num, seg_len);
|
||||
for (int i = 0; i < seg_byte_num; i++)
|
||||
fprintf (stderr, "%02X", seg_bytes[i]);
|
||||
}
|
||||
else fprintf (stderr, " (%d/%d) ", seg_num, seg_len);
|
||||
|
||||
//copy to PDU superframe
|
||||
memcpy(state->dmr_pdu_sf[0]+(seg_num-1)*seg_byte_num, seg_bytes, seg_byte_num*sizeof(uint8_t));
|
||||
|
||||
int len = seg_byte_num * seg_len;
|
||||
|
||||
//TODO: Appears to have embedded 2 byte crc, populated up to 12 bits
|
||||
//not a match (may need bitwise, or may need entire messages put together and not just alias)
|
||||
// uint16_t crc_chk = crc12f(state->dmr_pdu_sf[0], 12);
|
||||
|
||||
//extract CRC
|
||||
uint16_t crc_ext = (state->dmr_pdu_sf[0][len-2] << 8) | (state->dmr_pdu_sf[0][len-1] << 0);
|
||||
|
||||
//remove CRC bytes
|
||||
if (len > 2)
|
||||
len -= 2;
|
||||
|
||||
if (seg_num == seg_len)
|
||||
{
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "\n Completed Alias Encoded: ");
|
||||
for (int i = 0; i < (seg_len*seg_byte_num); i++)
|
||||
fprintf (stderr, "%02X", state->dmr_pdu_sf[0][i]);
|
||||
}
|
||||
|
||||
//Alias String
|
||||
char alias[500]; memset(alias, 0, sizeof(alias));
|
||||
int ptr = 0; //pointer to position in alias buffer
|
||||
int k = 0; //pointer to position in completed message
|
||||
uint16_t c16b = 0; //16-bit character encoded in BIG5 or SJIS
|
||||
uint8_t utf8 = 0; //UTF-8 Character
|
||||
uint32_t uni = 0; //Unicode Character returned from BIG5, or SJIS decoder
|
||||
|
||||
//SHIFT-JIS or BIG5 to UNICODE Conversion
|
||||
setlocale(LC_ALL, ""); //needed when encoded alias contains CJK
|
||||
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n MFID: %02X; Unk: %04X; Encoded Len: %d; CRC: %03X; Alias: ", mfid, unk, len, crc_ext);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
|
||||
uni = 0;
|
||||
utf8 = state->dmr_pdu_sf[0][k];
|
||||
c16b = (state->dmr_pdu_sf[0][k+0] << 8) | state->dmr_pdu_sf[0][k+1];
|
||||
|
||||
if (utf8 >= 0x20 && utf8 < 0x80)
|
||||
{
|
||||
uni = sjis_char_to_unicode(utf8);
|
||||
fprintf (stderr, "%lc", uni);
|
||||
k++;
|
||||
}
|
||||
else if (c16b != 0)
|
||||
{
|
||||
#ifdef BIG5_DECODE //BIG5 Tradional Chinese
|
||||
uni = big5_char_to_unicode(c16b);
|
||||
#elif SJIS_DECODE //SJIS for Japanese
|
||||
uni = sjis_char_to_unicode(c16b);
|
||||
#elif UTF16_DECODE //try as UTF16BE (untested through Unicode to UTF8 decoder)
|
||||
uni = c16b; //UTF16BE
|
||||
// uni = (state->dmr_pdu_sf[0][k+1] << 8) | state->dmr_pdu_sf[0][k+0]; //UTF16LE (for test only)
|
||||
#endif
|
||||
fprintf (stderr, "%lc", uni);
|
||||
k+=2;
|
||||
}
|
||||
else if (utf8 == 0 && c16b == 0)
|
||||
break;
|
||||
|
||||
//Encode Unicode to UTF-8 if not a pass-through ASCII control character or 0
|
||||
if (uni >= 0x0020)
|
||||
ptr += utf8_encode(uni, alias+ptr);
|
||||
|
||||
}
|
||||
|
||||
//terminate string failsafe
|
||||
alias[ptr++] = 0x00;
|
||||
alias[ptr++] = 0x00;
|
||||
|
||||
sprintf (state->generic_talker_alias[0], "%s", alias);
|
||||
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", alias);
|
||||
|
||||
//reset PDU superframe afterwards
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
}
|
||||
|
||||
} /* End NXDN_decode_Alias16 */
|
||||
|
||||
void NXDN_decode_Prop(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
{
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
|
||||
//Octet 1 is MFID value
|
||||
uint8_t mfid = (uint8_t)convert_bits_into_output(Message+8, 8);
|
||||
|
||||
//check this message to see if it is an alias, or other unknown PROP_FORM
|
||||
uint32_t alias_check = (uint16_t)convert_bits_into_output(Message+16, 16);
|
||||
|
||||
if (mfid == 0x68 && alias_check == 0x8204)
|
||||
{
|
||||
fprintf(stderr, " ALIAS");
|
||||
#ifdef BIG5_DECODE
|
||||
NXDN_decode_Alias16(opts, state, Message); //ASCII and 16-bit SJIS or BIG5 (depends on cmake define)
|
||||
#elif SJIS_DECODE
|
||||
NXDN_decode_Alias16(opts, state, Message); //ASCII and 16-bit SJIS or BIG5 (depends on cmake define)
|
||||
#elif UTF16_DECODE
|
||||
NXDN_decode_Alias16(opts, state, Message); //ASCII and 16-bit UTF16BE (untested) (depends on cmake define)
|
||||
#elif ASCII_DECODE
|
||||
NXDN_decode_Alias(opts, state, Message); //ASCII only version
|
||||
#else
|
||||
NXDN_decode_Alias(opts, state, Message); //ASCII only version (fallback if none selected by cmake)
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, " PROP_FORM");
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " MFID: %02X; Message: ", mfid);
|
||||
for (int i = 2; i < 9; i++)
|
||||
fprintf (stderr, "%02X", (uint8_t)convert_bits_into_output(Message+(i*8), 8));
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
//This version works well with ASCII, but no support for other encodings
|
||||
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
{
|
||||
UNUSED(opts);
|
||||
|
||||
uint8_t Alias1 = 0x20;
|
||||
uint8_t Alias2 = 0x20;
|
||||
uint8_t Alias3 = 0x20;
|
||||
uint8_t Alias4 = 0x20;
|
||||
uint8_t blocknumber = 0;
|
||||
uint8_t total = 0;
|
||||
uint8_t unk1 = 0;
|
||||
uint8_t unk2 = 0;
|
||||
uint8_t CrcCorrect = 0;
|
||||
|
||||
//alias can also hit on a facch1 -- do we still need this checkdown?
|
||||
if (state->nxdn_sacch_non_superframe == FALSE)
|
||||
{
|
||||
CrcCorrect = state->NxdnElementsContent.VCallCrcIsGood;
|
||||
}
|
||||
else CrcCorrect = 1; //FACCH1 with bad CRC won't make it this far anyways, so set as 1
|
||||
|
||||
// uint8_t mfid = (uint8_t)ConvertBitIntoBytes(&Message[8], 8); //PROP_FORM MFID value
|
||||
// uint8_t unk1 = (uint8_t)ConvertBitIntoBytes(&Message[16], 8); //82
|
||||
// uint8_t unk2 = (uint8_t)ConvertBitIntoBytes(&Message[24], 8); //04
|
||||
|
||||
//FACCH Payload [3F][68][82][04][2 <- block number4] "[69][6F][6E][20]" <- 4 alias octets [00][7F][1C]
|
||||
blocknumber = (uint8_t)ConvertBitIntoBytes(&Message[32], 4) & 0x7; // & 0x7, might just be three bits, unsure
|
||||
total = (uint8_t)ConvertBitIntoBytes(&Message[36], 4) & 0x7; //second value seems to be total number of blocks? or len of alias in this segment?
|
||||
unk1 = (uint8_t)ConvertBitIntoBytes(&Message[8], 8); //unknown values in first two bytes after the opcode
|
||||
unk2 = (uint8_t)ConvertBitIntoBytes(&Message[16], 8); //could be related to the format of these (Iso8? etc)
|
||||
UNUSED4(unk1, unk2, total, blocknumber);
|
||||
Alias1 = (uint8_t)ConvertBitIntoBytes(&Message[40], 8);
|
||||
Alias2 = (uint8_t)ConvertBitIntoBytes(&Message[48], 8);
|
||||
Alias3 = (uint8_t)ConvertBitIntoBytes(&Message[56], 8);
|
||||
Alias4 = (uint8_t)ConvertBitIntoBytes(&Message[64], 8);
|
||||
|
||||
char str_a[120]; char str_b[50];
|
||||
|
||||
//TODO: Revisit the debug here and see if anythign comes out of it,
|
||||
//maybe go on the assumptiont his works very similar to DMR talker alias
|
||||
//debug/test
|
||||
// fprintf (stderr, " U1: %02X U2: %02X;", unk1, unk2); //these always appear to be the same two byte values, even across different systems and languages
|
||||
// fprintf (stderr, " A:%d/%d; ", blocknumber, total); //this is accurate info here
|
||||
|
||||
//sanity check to prevent OOB array assignment
|
||||
if (blocknumber > 0 && blocknumber < 4) //last 'block' may have been assigning garbage name values -- I'm honestly not sure block'4' contains Alias data, but other data or something
|
||||
{
|
||||
//assign to index -1, since block number conveyed here is 1,2,3,4, and index values are 0,1,2,3
|
||||
//only assign if within valid range of ascii characters (not including diacritical extended alphabet)
|
||||
//else assign "space" ascii character
|
||||
|
||||
//since we are zeroing out the blocks on tx_rel and other conditions, better to just set nothing to bad Alias bytes
|
||||
//tends to zero out otherwise already good blocks set in a previous repitition.
|
||||
if (Alias1 > 0x19 && Alias1 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][0], "%c", Alias1);
|
||||
// else sprintf (state->nxdn_alias_block_segment[blocknumber-1][0], "%c", 0x20); //space
|
||||
|
||||
if (Alias2 > 0x19 && Alias2 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][1], "%c", Alias2);
|
||||
// else sprintf (state->nxdn_alias_block_segment[blocknumber-1][1], "%c", 0x20); //space
|
||||
|
||||
if (Alias3 > 0x19 && Alias3 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][2], "%c", Alias3);
|
||||
// else sprintf (state->nxdn_alias_block_segment[blocknumber-1][2], "%c", 0x20); //space
|
||||
|
||||
if (Alias4 > 0x19 && Alias4 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][3], "%c", Alias4);
|
||||
// else sprintf (state->nxdn_alias_block_segment[blocknumber-1][3], "%c", 0x20); //space
|
||||
|
||||
sprintf (str_a, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
state->nxdn_alias_block_segment[0][0], state->nxdn_alias_block_segment[0][1], state->nxdn_alias_block_segment[0][2], state->nxdn_alias_block_segment[0][3],
|
||||
state->nxdn_alias_block_segment[1][0], state->nxdn_alias_block_segment[1][1], state->nxdn_alias_block_segment[1][2], state->nxdn_alias_block_segment[1][3],
|
||||
state->nxdn_alias_block_segment[2][0], state->nxdn_alias_block_segment[2][1], state->nxdn_alias_block_segment[2][2], state->nxdn_alias_block_segment[2][3],
|
||||
state->nxdn_alias_block_segment[3][0], state->nxdn_alias_block_segment[3][1], state->nxdn_alias_block_segment[3][2], state->nxdn_alias_block_segment[3][3]);
|
||||
|
||||
//juggle strings here so we don't get compiler warnings on assignment size
|
||||
memcpy (str_b, str_a, 48); str_b[49] = '\0';
|
||||
|
||||
sprintf (state->generic_talker_alias[0], "%s", str_b);
|
||||
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", str_b);
|
||||
|
||||
}
|
||||
uint8_t blocknumber = (uint8_t)ConvertBitIntoBytes(&Message[32], 4) & 0x7;
|
||||
uint8_t total = (uint8_t)ConvertBitIntoBytes(&Message[36], 4) & 0x7;
|
||||
|
||||
//crc errs in one repitition may occlude an otherwise good alias, so test and change if needed
|
||||
//completed alias should still appear in ncurses terminal regardless, so this may be okay
|
||||
if (CrcCorrect)
|
||||
{
|
||||
fprintf (stderr, " "); //spacer
|
||||
fprintf (stderr, " (%d/%d) ", blocknumber, total);
|
||||
|
||||
blocknumber--;
|
||||
total--;
|
||||
|
||||
int ptr = blocknumber*4;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
state->generic_talker_alias[0][ptr++] = (char)ConvertBitIntoBytes(&Message[40+(i*8)], 8);
|
||||
|
||||
//on block 4, the last two (14, and 15) will not be an alias value, but a CRC value (seemingly)
|
||||
state->generic_talker_alias[0][14] = '\0';
|
||||
state->generic_talker_alias[0][15] = '\0';
|
||||
|
||||
//comb alias so far, if characters greater than ASCII 0x7F, this may be a UTF16 encoded alias
|
||||
//May be more beneficial in long run to use the ARIB alias with SJIS and/or UTF16 to UTF8 in it.
|
||||
//NOTE: This will not prevent 'splicing' issues if signal issue occurs and resumes with another alias
|
||||
//this alias string is cleared on TX_REL, TX_REL_EXT, noCarrier, and few other items
|
||||
for (int i = 0; i < 14; i++)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
fprintf (stderr, "%s", state->nxdn_alias_block_segment[i][j]);
|
||||
}
|
||||
//substitute with a space if early terminator (partial decode) or greater than end of ASCII
|
||||
if (state->generic_talker_alias[0][i] < 0x20 || state->generic_talker_alias[0][i] > 0x7F)
|
||||
state->generic_talker_alias[0][i] = 0x20;
|
||||
}
|
||||
fprintf (stderr, " ");
|
||||
|
||||
//walk backwards, remove any ending spaces with a terminator
|
||||
for (int i = 13; i >= 1; i--)
|
||||
{
|
||||
if (state->generic_talker_alias[0][i] == 0x20)
|
||||
state->generic_talker_alias[0][i] = '\0';
|
||||
|
||||
if (state->generic_talker_alias[0][i-1] != 0x20)
|
||||
break;
|
||||
}
|
||||
|
||||
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", state->generic_talker_alias[0]);
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
// if (blocknumber == total)
|
||||
{
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n Talker Alias: %s", state->generic_talker_alias[0]);
|
||||
else fprintf (stderr, "%s", state->generic_talker_alias[0]);
|
||||
}
|
||||
|
||||
}
|
||||
else fprintf (stderr, " CRC ERR "); //redundant print? or okay?
|
||||
}
|
||||
@@ -939,6 +1064,64 @@ void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
{
|
||||
|
||||
char station_id_string[26]; //FACCH2 maxes out at 26 (largest message carrier)
|
||||
memset(station_id_string, 0, sizeof(station_id_string));
|
||||
|
||||
uint8_t start = Message[8];
|
||||
uint8_t end = Message[9];
|
||||
uint8_t option = convert_bits_into_output(Message+8, 8);
|
||||
uint8_t num_chars = convert_bits_into_output(Message+10, 6);
|
||||
|
||||
//arbitrary value to stop on if not the first (not sure why first and not last? probably should be last)
|
||||
if (!start)
|
||||
num_chars = 24;
|
||||
|
||||
for (int i = 0; i < num_chars+1; i++)
|
||||
{
|
||||
uint8_t c = convert_bits_into_output(Message+16+(i*8), 8);
|
||||
if (c >= 0x20 && c <= 0x7F) //in range of ASCII characters
|
||||
station_id_string[i] = c;
|
||||
}
|
||||
|
||||
station_id_string[25] = '\0';
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
|
||||
fprintf (stderr, "\n Station Identification Information - ");
|
||||
|
||||
if (start && end)
|
||||
fprintf (stderr, "Full ");
|
||||
else if (start)
|
||||
fprintf (stderr, "First ");
|
||||
else if (end)
|
||||
fprintf (stderr, "Last ");
|
||||
else fprintf (stderr, "Next ");
|
||||
|
||||
fprintf (stderr, "ID: %s ", station_id_string);
|
||||
|
||||
//push as a data event string
|
||||
if (start && end)
|
||||
{
|
||||
char event_string[55]; memset(event_string, 0, sizeof(event_string));
|
||||
sprintf (event_string, "NXDN Digital Station ID: %s", station_id_string);
|
||||
watchdog_event_datacall (opts, state, 65520, 0, event_string, 0);
|
||||
}
|
||||
|
||||
//debug
|
||||
if (opts->payload == 1)
|
||||
fprintf (stderr, "\n Option: %02X; Start: %d; End %d; Characters: %d or Sequence: %02X;", option, start, end, num_chars, option & 0x3F);
|
||||
|
||||
//This message has a CRC, but it appears that it is carried out across
|
||||
//multiple segments if this is a multi-part message, but CRC type is not
|
||||
//indicated, so will just rely on the overall CRC from each message prior
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
{
|
||||
uint32_t location_id = 0;
|
||||
@@ -955,7 +1138,8 @@ void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
nxdn_location_id_handler (state, location_id, 0);
|
||||
|
||||
//run the srv info
|
||||
nxdn_srv_info_handler (state, svc_info);
|
||||
if (opts->payload == 1)
|
||||
nxdn_srv_info_handler (state, svc_info);
|
||||
|
||||
//run the rst info, if not zero
|
||||
if (rst_info) nxdn_rst_info_handler (state, rst_info);
|
||||
@@ -1024,7 +1208,8 @@ void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message
|
||||
nxdn_location_id_handler(state, location_id, 0);
|
||||
|
||||
//run the srv info
|
||||
nxdn_srv_info_handler (state, svc_info);
|
||||
if (opts->payload == 1)
|
||||
nxdn_srv_info_handler (state, svc_info);
|
||||
|
||||
//run the rst info, if not zero
|
||||
if (rst_info) nxdn_rst_info_handler (state, rst_info);
|
||||
@@ -1410,8 +1595,8 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
state->nxdn_last_rid = 0;
|
||||
state->nxdn_last_tg = 0;
|
||||
state->gi[0] = -1;
|
||||
memset (state->generic_talker_alias[0], 0, sizeof(state->generic_talker_alias[0]));
|
||||
sprintf (state->generic_talker_alias[0], "%s", "");
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
}
|
||||
|
||||
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
|
||||
@@ -2085,8 +2270,8 @@ void NXDN_decode_VCALL_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Messag
|
||||
state->nxdn_last_rid = 0;
|
||||
state->nxdn_last_tg = 0;
|
||||
state->gi[0] = -1;
|
||||
memset (state->generic_talker_alias[0], 0, sizeof(state->generic_talker_alias[0]));
|
||||
sprintf (state->generic_talker_alias[0], "%s", "");
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
}
|
||||
|
||||
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
|
||||
@@ -2170,7 +2355,7 @@ void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Messag
|
||||
utf8 = state->dmr_pdu_sf[0][k];
|
||||
sjis = (state->dmr_pdu_sf[0][k] << 8) | state->dmr_pdu_sf[0][k+1];
|
||||
|
||||
if (utf8 >= 0x20 && utf8 < 0x7F)
|
||||
if (utf8 >= 0x20 && utf8 < 0x80)
|
||||
{
|
||||
c16 = sjis_char_to_unicode(utf8);
|
||||
fprintf (stderr, "%lc", c16);
|
||||
|
||||
407
src/nxdn_frame.c
407
src/nxdn_frame.c
@@ -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-01 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 2 //Threshold for lich dibit "dividing" errors
|
||||
// #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,9 @@ 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];
|
||||
|
||||
//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 +74,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));
|
||||
@@ -111,14 +103,13 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
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 +118,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 +130,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->payload == 1)
|
||||
fprintf(stderr, " Lich Parity Error %02X / %04X\n", lich_full, lich_bits_hex);
|
||||
#endif
|
||||
state->lastsynctype = -1;
|
||||
goto END;
|
||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||
}
|
||||
|
||||
voice = 0;
|
||||
@@ -156,150 +148,168 @@ 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);
|
||||
@@ -340,13 +350,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 +361,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 +372,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 +546,30 @@ 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);
|
||||
|
||||
//TODO: Redo duplicate facch check inside decoder? storage for facch message?
|
||||
if (facch & 1) nxdn_facch1(opts, state, facch_bits_a);
|
||||
if (facch & 2) nxdn_facch1(opts, state, facch_bits_b);
|
||||
|
||||
//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);
|
||||
if (udch) nxdn_facch2_udch(opts, state, facch2_bits, 0);
|
||||
if (facch2) nxdn_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)
|
||||
{
|
||||
if (memcmp (facch_bits_a, facch_bits_b, 144) != 0)
|
||||
nxdn_deperm_facch(opts, state, facch_bits_b);
|
||||
}
|
||||
//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 +625,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1629,9 +1629,53 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
}
|
||||
|
||||
//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: %02X; ", MAC[1+len_a] & 0x3F); //mask, or not?
|
||||
for (int i = 4; i < len+1; i++)
|
||||
fprintf (stderr, "%02X", 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: %02X; ", MAC[1+len_a] & 0x3F); //mask, or not?
|
||||
for (int i = 4; i < len+1; i++)
|
||||
fprintf (stderr, "%02X", 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
|
||||
@@ -1775,6 +1819,12 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
|
||||
int unk2 = MAC[len_a+2]; //This field is observed as 0xAA, unknown if this is an opcode, or other MFID
|
||||
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
|
||||
|
||||
//bugfix observed on random errant second MAC message on Phase 2
|
||||
//although can't 100% confirm, pretty sure these all have same len value
|
||||
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
|
||||
|
||||
3415
src/sjis_unicode.c
3415
src/sjis_unicode.c
File diff suppressed because it is too large
Load Diff
205
src/ysf.c
205
src/ysf.c
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user