681 lines
18 KiB
C
681 lines
18 KiB
C
//NXDN descramble/deperm/depuncture and crc/utility functions
|
|
//Reworked portions from Osmocom OP25
|
|
|
|
/* -*- c++ -*- */
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include "dsd.h"
|
|
#include "nxdn_const.h"
|
|
|
|
static const uint8_t scramble_t[] = { //values are the position values we need to invert in the descramble
|
|
2, 5, 6, 7, 10, 12, 14, 16, 17, 22, 23, 25, 26, 27, 28, 30, 33, 34, 36, 37, 38, 41, 45, 47,
|
|
52, 54, 56, 57, 59, 62, 63, 64, 65, 66, 67, 69, 70, 73, 76, 79, 81, 82, 84, 85, 86, 87, 88,
|
|
89, 92, 95, 96, 98, 100, 103, 104, 107, 108, 116, 117, 121, 122, 125, 127, 131, 132, 134,
|
|
137, 139, 140, 141, 142, 143, 144, 145, 147, 151, 153, 154, 158, 159, 160, 162, 164, 165,
|
|
168, 170, 171, 174, 175, 176, 177, 181
|
|
};
|
|
|
|
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
|
|
};
|
|
|
|
//decoding functions here
|
|
void nxdn_descramble(uint8_t dibits[], int len)
|
|
{
|
|
for (int i=0; i<sizeof(scramble_t); i++) {
|
|
if (scramble_t[i] >= len)
|
|
break;
|
|
dibits[scramble_t[i]] ^= 0x2; // invert sign of scrambled dibits
|
|
}
|
|
}
|
|
|
|
void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
|
|
{
|
|
uint8_t deperm[144];
|
|
uint8_t depunc[192];
|
|
uint8_t trellis_buf[96];
|
|
uint16_t crc = 0; //crc calculated by function
|
|
uint16_t check = 0; //crc from payload for comparison
|
|
int out;
|
|
char buf[128];
|
|
|
|
for (int i=0; i<144; i++)
|
|
deperm[PERM_16_9[i]] = bits[i];
|
|
out = 0;
|
|
for (int i=0; i<144; i+=3) {
|
|
depunc[out++] = deperm[i+0];
|
|
depunc[out++] = 0;
|
|
depunc[out++] = deperm[i+1];
|
|
depunc[out++] = deperm[i+2];
|
|
}
|
|
|
|
//switch to the convolutional decoder
|
|
uint8_t temp[210];
|
|
uint8_t s0;
|
|
uint8_t s1;
|
|
uint8_t m_data[13];
|
|
memset (temp, 0, sizeof(temp));
|
|
memset (m_data, 0, sizeof(m_data));
|
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
|
|
|
for (int i = 0; i < 192; i++)
|
|
{
|
|
temp[i] = depunc[i] << 1;
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
temp[i+192] = 0;
|
|
}
|
|
|
|
CNXDNConvolution_start();
|
|
for (int i = 0U; i < 100U; i++)
|
|
{
|
|
s0 = temp[(2*i)];
|
|
s1 = temp[(2*i)+1];
|
|
|
|
CNXDNConvolution_decode(s0, s1);
|
|
}
|
|
|
|
CNXDNConvolution_chainback(m_data, 96U);
|
|
|
|
for(int i = 0; i < 12; i++)
|
|
{
|
|
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
|
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
|
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
|
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
|
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
|
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
|
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
|
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
|
}
|
|
|
|
//load tail 16 bits into check variable to compare vs computed crc value
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
check = check << 1;
|
|
check = check + trellis_buf[80+i];
|
|
}
|
|
|
|
crc = crc12f (trellis_buf, 80);
|
|
|
|
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
|
//else if (opts->payload == 1) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
|
|
|
|
if (opts->payload == 1)
|
|
{
|
|
fprintf (stderr, "\n");
|
|
fprintf (stderr, " FACCH Payload ");
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
fprintf (stderr, "[%02X]", m_data[i]);
|
|
}
|
|
fprintf (stderr, " - %03X %03X", crc, check);
|
|
}
|
|
|
|
}
|
|
|
|
//new sacch
|
|
void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
|
{
|
|
uint8_t deperm[60];
|
|
uint8_t depunc[72];
|
|
uint8_t trellis_buf[32];
|
|
uint8_t answer[26];
|
|
int o = 0;
|
|
uint8_t crc = 0; //value computed by crc6 on payload
|
|
uint8_t check = 0; //value pulled from last 6 bits
|
|
int sf = 0;
|
|
int ran = 0;
|
|
int part_of_frame = 0;
|
|
|
|
for (int i=0; i<60; i++)
|
|
deperm[PERM_12_5[i]] = bits[i];
|
|
for (int p=0; p<60; p+= 10) {
|
|
depunc[o++] = deperm[p+0];
|
|
depunc[o++] = deperm[p+1];
|
|
depunc[o++] = deperm[p+2];
|
|
depunc[o++] = deperm[p+3];
|
|
depunc[o++] = deperm[p+4];
|
|
depunc[o++] = 0;
|
|
depunc[o++] = deperm[p+5];
|
|
depunc[o++] = deperm[p+6];
|
|
depunc[o++] = deperm[p+7];
|
|
depunc[o++] = deperm[p+8];
|
|
depunc[o++] = deperm[p+9];
|
|
depunc[o++] = 0;
|
|
}
|
|
|
|
//switch to the convolutional decoder
|
|
uint8_t temp[90];
|
|
uint8_t s0;
|
|
uint8_t s1;
|
|
uint8_t m_data[5];
|
|
|
|
memset (temp, 0, sizeof (temp));
|
|
memset (m_data, 0, sizeof (m_data));
|
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
|
|
|
for (int i = 0; i < 72; i++)
|
|
{
|
|
temp[i] = depunc[i] << 1;
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
temp[i+72] = 0;
|
|
}
|
|
|
|
CNXDNConvolution_start();
|
|
for (int i = 0U; i < 40U; i++)
|
|
{
|
|
s0 = temp[(2*i)];
|
|
s1 = temp[(2*i)+1];
|
|
|
|
CNXDNConvolution_decode(s0, s1);
|
|
}
|
|
|
|
//stored as 5 bytes, will need to convert to trellis_buf after running
|
|
CNXDNConvolution_chainback(m_data, 36U);
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
|
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
|
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
|
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
|
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
|
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
|
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
|
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
|
}
|
|
|
|
crc = crc6(trellis_buf, 32);
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
check = check << 1;
|
|
check = check + trellis_buf[i+26];
|
|
}
|
|
|
|
//FIRST! If part of a non_superframe, and CRC is good, send directly to NXDN_Elements_Content_decode
|
|
if (state->nxdn_sacch_non_superframe == TRUE)
|
|
{
|
|
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
|
else fprintf (stderr, " ");
|
|
|
|
uint8_t nsf_sacch[26];
|
|
memset (nsf_sacch, 0, sizeof(nsf_sacch));
|
|
for (int i = 0; i < 26; i++)
|
|
{
|
|
nsf_sacch[i] = trellis_buf[i+8];
|
|
}
|
|
|
|
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch);
|
|
|
|
if (opts->payload == 1)
|
|
{
|
|
fprintf (stderr, "\n SACCH NSF ");
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
fprintf (stderr, "[%02X]", m_data[i]);
|
|
}
|
|
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
|
}
|
|
|
|
}
|
|
|
|
//If part of superframe, collect the fragments and send to NXDN_SACCH_Full_decode instead
|
|
else if (state->nxdn_sacch_non_superframe == FALSE)
|
|
{
|
|
//sf and ran together are denoted as SR in the manual (more confusing acronyms)
|
|
//sf (structure field) and RAN will always exist in first 8 bits of each SACCH, then the next 18 bits are the fragment of the superframe
|
|
sf = (trellis_buf[0] << 1) | trellis_buf[1];
|
|
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
|
if (sf == 3) part_of_frame = 0;
|
|
else if (sf == 2) part_of_frame = 1;
|
|
else if (sf == 1) part_of_frame = 2;
|
|
else if (sf == 0) part_of_frame = 3;
|
|
else part_of_frame = 0;
|
|
|
|
fprintf (stderr, "%s", KCYN);
|
|
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
|
else fprintf (stderr, " ");
|
|
fprintf (stderr, "%s", KNRM);
|
|
|
|
//reset scrambler seed to key value on new superframe
|
|
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
|
|
|
|
if (crc == 0)
|
|
{
|
|
state->nxdn_ran = state->nxdn_last_ran = ran;
|
|
state->nxdn_sf = sf;
|
|
state->nxdn_part_of_frame = part_of_frame;
|
|
state->nxdn_sacch_frame_segcrc[part_of_frame] = 0; //zero indicates good check
|
|
}
|
|
else state->nxdn_sacch_frame_segcrc[part_of_frame] = 1; //1 indicates bad check
|
|
|
|
int sacch_segment = 0;
|
|
|
|
for (int i = 0; i < 18; i++)
|
|
{
|
|
sacch_segment = sacch_segment << 1;
|
|
sacch_segment = sacch_segment + trellis_buf[i+8];
|
|
state->nxdn_sacch_frame_segment[part_of_frame][i] = trellis_buf[i+8];
|
|
}
|
|
|
|
//Hand off to LEH NXDN_SACCH_Full_decode
|
|
if (part_of_frame == 3)
|
|
{
|
|
NXDN_SACCH_Full_decode (opts, state);
|
|
}
|
|
|
|
if (opts->payload == 1)
|
|
{
|
|
fprintf (stderr, "\n");
|
|
fprintf (stderr, " SACCH SF Segment #%d ", part_of_frame+1);
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
fprintf (stderr, "[%02X]", m_data[i]);
|
|
}
|
|
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348])
|
|
{
|
|
|
|
uint8_t deperm[348];
|
|
uint8_t depunc[406];
|
|
uint8_t trellis_buf[199];
|
|
int id = 0;
|
|
uint16_t crc = 0;
|
|
|
|
for (int i=0; i<348; i++) {
|
|
deperm[PERM_12_29[i]] = bits[i];
|
|
}
|
|
for (int i=0; i<29; i++) {
|
|
depunc[id++] = deperm[i*12];
|
|
depunc[id++] = deperm[i*12+1];
|
|
depunc[id++] = deperm[i*12+2];
|
|
depunc[id++] = 0;
|
|
depunc[id++] = deperm[i*12+3];
|
|
depunc[id++] = deperm[i*12+4];
|
|
depunc[id++] = deperm[i*12+5];
|
|
depunc[id++] = deperm[i*12+6];
|
|
depunc[id++] = deperm[i*12+7];
|
|
depunc[id++] = deperm[i*12+8];
|
|
depunc[id++] = deperm[i*12+9];
|
|
depunc[id++] = 0;
|
|
depunc[id++] = deperm[i*12+10];
|
|
depunc[id++] = deperm[i*12+11];
|
|
}
|
|
|
|
//switch to the convolutional decoder
|
|
uint8_t temp[220];
|
|
uint8_t s0;
|
|
uint8_t s1;
|
|
uint8_t m_data[26];
|
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
|
memset (temp, 0, sizeof (temp));
|
|
memset (m_data, 0, sizeof (m_data));
|
|
|
|
for (int i = 0; i < 406; i++)
|
|
{
|
|
temp[i] = depunc[i] << 1;
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
temp[i+203] = 0;
|
|
}
|
|
|
|
CNXDNConvolution_start();
|
|
for (int i = 0U; i < 207U; i++)
|
|
{
|
|
s0 = temp[(2*i)];
|
|
s1 = temp[(2*i)+1];
|
|
|
|
CNXDNConvolution_decode(s0, s1);
|
|
}
|
|
|
|
//numerals seem okay now
|
|
CNXDNConvolution_chainback(m_data, 203U);
|
|
|
|
for(int i = 0; i < 26; i++)
|
|
{
|
|
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
|
|
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
|
|
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
|
|
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
|
|
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
|
|
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
|
|
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
|
|
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
|
|
}
|
|
|
|
crc = crc15(trellis_buf, 199);
|
|
|
|
uint8_t f2u_message_buffer[199];
|
|
memset (f2u_message_buffer, 0, sizeof(f2u_message_buffer));
|
|
|
|
//just going to leave this all here in case its needed, like in cac,
|
|
//don't have any samples with f2 or udch data in it
|
|
for (int i = 0; i < 199-8; i++)
|
|
{
|
|
f2u_message_buffer[i] = trellis_buf[i+8];
|
|
}
|
|
|
|
if (crc == 0)
|
|
{
|
|
fprintf (stderr, " F2/U ");
|
|
//NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
|
|
NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
|
|
}
|
|
|
|
if (opts->payload == 1)
|
|
{
|
|
fprintf (stderr, "\n");
|
|
fprintf (stderr, " F2/U Payload\n ");
|
|
for (int i = 0; i < 26; i++)
|
|
{
|
|
fprintf (stderr, "[%02X]", m_data[i]);
|
|
if (i == 12) fprintf (stderr, "\n ");
|
|
}
|
|
if (crc != 0) fprintf (stderr, " CRC ERR ");
|
|
}
|
|
|
|
}
|
|
|
|
void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
|
{
|
|
|
|
uint8_t deperm[300];
|
|
uint8_t depunc[350];
|
|
uint8_t trellis_buf[171];
|
|
int id = 0;
|
|
uint16_t crc = 0;
|
|
|
|
for (int i=0; i<300; i++) {
|
|
deperm[PERM_12_25[i]] = bits[i];
|
|
}
|
|
for (int i=0; i<25; i++) {
|
|
depunc[id++] = deperm[i*12];
|
|
depunc[id++] = deperm[i*12+1];
|
|
depunc[id++] = deperm[i*12+2];
|
|
depunc[id++] = 0;
|
|
depunc[id++] = deperm[i*12+3];
|
|
depunc[id++] = deperm[i*12+4];
|
|
depunc[id++] = deperm[i*12+5];
|
|
depunc[id++] = deperm[i*12+6];
|
|
depunc[id++] = deperm[i*12+7];
|
|
depunc[id++] = deperm[i*12+8];
|
|
depunc[id++] = deperm[i*12+9];
|
|
depunc[id++] = 0;
|
|
depunc[id++] = deperm[i*12+10];
|
|
depunc[id++] = deperm[i*12+11];
|
|
}
|
|
|
|
//switch to the convolutional decoder
|
|
uint8_t temp[360];
|
|
uint8_t s0;
|
|
uint8_t s1;
|
|
uint8_t m_data[26];
|
|
memset (trellis_buf, 0, sizeof(trellis_buf));
|
|
memset (temp, 0, sizeof (temp));
|
|
memset (m_data, 0, sizeof (m_data));
|
|
|
|
for (int i = 0; i < 350; i++)
|
|
{
|
|
temp[i] = depunc[i] << 1;
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
temp[i+350] = 0;
|
|
}
|
|
|
|
CNXDNConvolution_start();
|
|
for (int i = 0U; i < 179U; i++)
|
|
{
|
|
s0 = temp[(2*i)];
|
|
s1 = temp[(2*i)+1];
|
|
|
|
CNXDNConvolution_decode(s0, s1);
|
|
}
|
|
|
|
CNXDNConvolution_chainback(m_data, 175U);
|
|
|
|
for(int 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;
|
|
}
|
|
|
|
crc = crc16cac(trellis_buf, 171);
|
|
|
|
//message type will probably be neccesary beforehand on single/dual meessage runs
|
|
//run message, check len, load into a seperate buffer, send to element, do same with other piece?
|
|
uint8_t MessageType = 0;
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
MessageType = MessageType << 1;
|
|
MessageType = MessageType + trellis_buf[i+2]; //double check this on CAC
|
|
}
|
|
|
|
uint8_t cac_message_buffer[171];
|
|
memset (cac_message_buffer, 0, sizeof(cac_message_buffer));
|
|
|
|
//shift the cac_message into the appropriate byte arrangement for element_decoder
|
|
for (int i = 0; i < 160; i++) //in future, we can use this to send multiple messages to decoder, if present
|
|
{
|
|
cac_message_buffer[i] = trellis_buf[i+8];
|
|
}
|
|
|
|
if (crc == 0)
|
|
{
|
|
fprintf (stderr, " CAC ");
|
|
NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
|
|
}
|
|
|
|
if (opts->payload == 1)
|
|
{
|
|
fprintf (stderr, "\n");
|
|
fprintf (stderr, " CAC Payload\n ");
|
|
for (int i = 0; i < 22; i++)
|
|
{
|
|
fprintf (stderr, "[%02X]", m_data[i]);
|
|
if (i == 10) fprintf (stderr, "\n ");
|
|
}
|
|
if (crc != 0) fprintf (stderr, " CRC ERR ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
|
{
|
|
|
|
fprintf (stderr, "%s", KYEL);
|
|
if (MessageType == 0x10) fprintf(stderr, " IDLE");
|
|
else if (MessageType == 0x11) fprintf(stderr, " DISC"); //disconnect
|
|
else if (MessageType == 0x01) fprintf(stderr, " VCALL");
|
|
else if (MessageType == 0x03) fprintf(stderr, " VCALL IV");
|
|
|
|
else if (MessageType == 0x07) fprintf(stderr, " TX_REL_EX");
|
|
else if (MessageType == 0x08) fprintf(stderr, " TX_REL");
|
|
|
|
else if (MessageType == 0x04) fprintf(stderr, " VCALL_ASSGN");
|
|
else if (MessageType == 0x05) fprintf(stderr, " VCALL_ASSGN_DUP");
|
|
else if (MessageType == 0x0E) fprintf(stderr, " DCALL_ASSGN");
|
|
else if (MessageType == 0x18) fprintf(stderr, " SITE_INFO");
|
|
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
|
|
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO");
|
|
else if (MessageType == 0x1A) fprintf(stderr, " CCH_INFO");
|
|
else if (MessageType == 0x1B) fprintf(stderr, " ADJ_SITE_INFO");
|
|
else if (MessageType == 0x20) fprintf(stderr, " REG_RESP");
|
|
else if (MessageType == 0x22) fprintf(stderr, " REG_C_RESP");
|
|
else if (MessageType == 0x24) fprintf(stderr, " GRP_REG_RESP");
|
|
else if (MessageType == 0x32) fprintf(stderr, " STAT_REQ");
|
|
else if (MessageType == 0x33) fprintf(stderr, " STAT_RESP");
|
|
else if (MessageType == 0x38) fprintf(stderr, " SDCALL_REQ_HEADER");
|
|
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
|
|
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
|
|
|
|
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
|
|
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
|
|
else fprintf(stderr, " Unknown M-%02X", MessageType);
|
|
fprintf (stderr, "%s", KNRM);
|
|
|
|
//zero out stale values so they won't persist after a transmit release
|
|
//disable if random messages wipe out the alias
|
|
if (MessageType == 0x08 || MessageType == 0x10) //idle, tx_rel
|
|
{
|
|
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
|
state->nxdn_last_rid = 0;
|
|
state->nxdn_last_tg = 0;
|
|
state->nxdn_cipher_type = 0;
|
|
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
|
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
|
sprintf (state->nxdn_call_type, "%s", "");
|
|
}
|
|
}
|
|
|
|
//voice descrambler
|
|
void LFSRN(char * BufferIn, char * BufferOut, dsd_state * state)
|
|
{
|
|
int i;
|
|
int lfsr;
|
|
int pN[49] = {0};
|
|
int bit = 0;
|
|
|
|
lfsr = state->payload_miN & 0x7FFF;
|
|
|
|
for (i = 0; i < 49; i++)
|
|
{
|
|
pN[i] = lfsr & 0x1;
|
|
bit = ( (lfsr >> 1) ^ (lfsr >> 0) ) & 1;
|
|
lfsr = ( (lfsr >> 1 ) | (bit << 14) );
|
|
BufferOut[i] = BufferIn[i] ^ pN[i];
|
|
}
|
|
|
|
state->payload_miN = lfsr & 0x7FFF;
|
|
}
|
|
|
|
static inline int load_i(const uint8_t val[], int len) {
|
|
int acc = 0;
|
|
for (int i=0; i<len; i++){
|
|
acc = (acc << 1) + (val[i] & 1);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
static uint8_t crc6(const uint8_t buf[], int len)
|
|
{
|
|
uint8_t s[6];
|
|
uint8_t a;
|
|
for (int i=0;i<6;i++)
|
|
s[i] = 1;
|
|
for (int i=0;i<len;i++) {
|
|
a = buf[i] ^ s[0];
|
|
s[0] = a ^ s[1];
|
|
s[1] = s[2];
|
|
s[2] = s[3];
|
|
s[3] = a ^ s[4];
|
|
s[4] = a ^ s[5];
|
|
s[5] = a;
|
|
}
|
|
return load_i(s, 6);
|
|
}
|
|
|
|
static uint16_t crc12f(const uint8_t buf[], int len)
|
|
{
|
|
uint8_t s[12];
|
|
uint8_t a;
|
|
for (int i=0;i<12;i++)
|
|
s[i] = 1;
|
|
for (int i=0;i<len;i++) {
|
|
a = buf[i] ^ s[0];
|
|
s[0] = a ^ s[1];
|
|
s[1] = s[2];
|
|
s[2] = s[3];
|
|
s[3] = s[4];
|
|
s[4] = s[5];
|
|
s[5] = s[6];
|
|
s[6] = s[7];
|
|
s[7] = s[8];
|
|
s[8] = a ^ s[9];
|
|
s[9] = a ^ s[10];
|
|
s[10] = a ^ s[11];
|
|
s[11] = a;
|
|
}
|
|
return load_i(s, 12);
|
|
}
|
|
|
|
static uint16_t crc15(const uint8_t buf[], int len)
|
|
{
|
|
uint8_t s[15];
|
|
uint8_t a;
|
|
for (int i=0;i<15;i++)
|
|
s[i] = 1;
|
|
for (int i=0;i<len;i++) {
|
|
a = buf[i] ^ s[0];
|
|
s[0] = a ^ s[1];
|
|
s[1] = s[2];
|
|
s[2] = s[3];
|
|
s[3] = a ^ s[4];
|
|
s[4] = a ^ s[5];
|
|
s[5] = s[6];
|
|
s[6] = s[7];
|
|
s[7] = a ^ s[8];
|
|
s[8] = a ^ s[9];
|
|
s[9] = s[10];
|
|
s[10] = s[11];
|
|
s[11] = s[12];
|
|
s[12] = a ^ s[13];
|
|
s[13] = s[14];
|
|
s[14] = a;
|
|
}
|
|
return load_i(s, 15);
|
|
}
|
|
|
|
static uint16_t crc16cac(const uint8_t buf[], int len)
|
|
{
|
|
int crc = 0xc3ee;
|
|
int poly = (1<<12) + (1<<5) + 1;
|
|
for (int i=0;i<len;i++) {
|
|
crc = ((crc << 1) | buf[i]) & 0x1ffff;
|
|
if(crc & 0x10000)
|
|
crc = (crc & 0xffff) ^ poly;
|
|
}
|
|
crc = crc ^ 0xffff;
|
|
return crc & 0xffff;
|
|
} |