NXDN FSW and Lich Based Decoding;

This commit is contained in:
lwvmobile
2022-11-11 22:12:37 -05:00
parent 8141ea2e78
commit 7af5ffe9fa
11 changed files with 1471 additions and 2441 deletions

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* NXDN AMBE interleave schedule
*/
#ifndef _MAIN
extern const int nW[36];
extern const int nX[36];
extern const int nY[36];
extern const int nZ[36];
#else
const int nW[36] = { 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 2,
0, 2, 0, 2, 0, 2,
0, 2, 0, 2, 0, 2
};
const int nX[36] = { 23, 10, 22, 9, 21, 8,
20, 7, 19, 6, 18, 5,
17, 4, 16, 3, 15, 2,
14, 1, 13, 0, 12, 10,
11, 9, 10, 8, 9, 7,
8, 6, 7, 5, 6, 4
};
const int nY[36] = { 0, 2, 0, 2, 0, 2,
0, 2, 0, 3, 0, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3
};
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
2, 0, 1, 13, 0, 12,
22, 11, 21, 10, 20, 9,
19, 8, 18, 7, 17, 6,
16, 5, 15, 4, 14, 3,
13, 2, 12, 1, 11, 0
};
#endif

View File

@@ -1,257 +1,82 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
// Portions Below from Osmocom OP25
// NXDN Encoder (C) Copyright 2019 Max H. Parke KA1RBI
// thx gr-ysf fr_vch_decoder_bb_impl.cc * Copyright 2015 Mathias Weyland *
// OP25 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.
//
// OP25 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 OP25; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
// 02110-1301, USA.
#ifndef _MAIN
extern const int nW[36];
extern const int nX[36];
extern const int nY[36];
extern const int nZ[36];
extern const char nxdnpr[145];
extern const char nxdnpr2[145]; //why is this 145 but referenced later as 182 or 384
//borrow from LEH
extern const unsigned char nsW[72];
extern const int DSDNXDN_SACCH_m_Interleave[60];
extern const int DSDNXDN_SACCH_m_PunctureList[12];
extern const int DSDNXDN_FACCH1_m_Interleave[144];
extern const int DSDNXDN_FACCH1_m_PunctureList[48];
extern const int DSDNXDN_UDCH_m_Interleave[348];
extern const int DSDNXDN_UDCH_m_PunctureList[58];
//
//enum
//{
#define NXDN_VCALL 0b000001 /* VCALL = VCALL_REQ = VCALL_RESP */
#define NXDN_VCALL_IV 0b000011
#define NXDN_DCALL_HDR 0b001001 /* Header Format - NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
#define NXDN_DCALL_USR 0b001011 /* User Data Format */
#define NXDN_DCALL_ACK 0b001100
#define NXDN_REL_EX 0b000111
#define NXDN_HEAD_DLY 0b001111
#define NXDN_SDCALL_REQ_HDR 0b111000 /* Header Format */
#define NXDN_SDCALL_REQ_USR 0b111001 /* User Data Format */
#define NXDN_SDCALL_RESP 0b111011
#define NXDN_SDCALL_IV 0b111010
#define NXDN_STAT_INQ_REQ 0b110000
#define NXDN_STAT_INQ_RESP 0b110001
#define NXDN_STAT_REQ 0b110010
#define NXDN_STAT_RESP 0b110011
#define NXDN_REM_CON_REQ 0b110100
#define NXDN_REM_CON_RESP 0b110101
#define NXDN_REM_CON_E_REQ 0b110110
#define NXDN_REM_CON_E_RESP 0b110111
//NXDN_VCALL_REQ = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
//NXDN_VCALL_RESP = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
#define NXDN_VCALL_REC_REQ = 0b000010 /* NXDN_VCALL_REC_REQ = NXDN_VCALL_REC_RESP */
//NXDN_VCALL_REC_RESP = 0b000010,
#define NXDN_VCALL_CONN_REQ = 0b000011 /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
//NXDN_VCALL_CONN_RESP = 0b000011, /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
#define NXDN_VCALL_ASSGN_DUP = 0b000101
//NXDN_DCALL_REQ = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
//NXDN_DCALL_RESP = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
#define NXDN_DCALL_REC_REQ = 0b001010 /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
//NXDN_DCALL_REC_RESP = 0b001010, /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
#define NXDN_DCALL_ASSGN = 0b001110
#define NXDN_DCALL_ASSGN_DUP = 0b001101
#define NXDN_IDLE 0b010000
#define NXDN_DISC_REQ = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
#define NXDN_DISC = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
#else
/*
* pseudorandom bit sequence
*/
const char nxdnpr[145] = { 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1 };
//const unsigned char nxdnpr2[384] =
const unsigned char nxdnpr2[182] = //generated from default in manual with the x^9+x^4+1 poly?
{
0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0,
0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1,
0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1,
1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1,
0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1,
1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1,
1, 1, 0, 0, 0, 1//, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
// 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0,
// 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
// 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
// 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1,
// 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0,
// 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1,
// 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1,
// 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1,
// 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0,
// 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
// 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
// 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1
};
/*
* NXDN AMBE interleave schedule
*/
const int nW[36] = { 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 2,
0, 2, 0, 2, 0, 2,
0, 2, 0, 2, 0, 2
static const uint8_t PERM_12_5[] = {
0,12,24,36,48,
1,13,25,37,49,
2,14,26,38,50,
3,15,27,39,51,
4,16,28,40,52,
5,17,29,41,53,
6,18,30,42,54,
7,19,31,43,55,
8,20,32,44,56,
9,21,33,45,57,
10,22,34,46,58,
11,23,35,47,59
};
const int nX[36] = { 23, 10, 22, 9, 21, 8,
20, 7, 19, 6, 18, 5,
17, 4, 16, 3, 15, 2,
14, 1, 13, 0, 12, 10,
11, 9, 10, 8, 9, 7,
8, 6, 7, 5, 6, 4
static const uint8_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,
3, 19, 35, 51, 67, 83, 99, 115, 131,
4, 20, 36, 52, 68, 84, 100, 116, 132,
5, 21, 37, 53, 69, 85, 101, 117, 133,
6, 22, 38, 54, 70, 86, 102, 118, 134,
7, 23, 39, 55, 71, 87, 103, 119, 135,
8, 24, 40, 56, 72, 88, 104, 120, 136,
9, 25, 41, 57, 73, 89, 105, 121, 137,
10, 26, 42, 58, 74, 90, 106, 122, 138,
11, 27, 43, 59, 75, 91, 107, 123, 139,
12, 28, 44, 60, 76, 92, 108, 124, 140,
13, 29, 45, 61, 77, 93, 109, 125, 141,
14, 30, 46, 62, 78, 94, 110, 126, 142,
15, 31, 47, 63, 79, 95, 111, 127, 143
};
const int nY[36] = { 0, 2, 0, 2, 0, 2,
0, 2, 0, 3, 0, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3
static const uint16_t PERM_12_25[] = {
0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228,240,252,264,276,288,
1,13,25,37,49,61,73,85,97,109,121,133,145,157,169,181,193,205,217,229,241,253,265,277,289,
2,14,26,38,50,62,74,86,98,110,122,134,146,158,170,182,194,206,218,230,242,254,266,278,290,
3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183,195,207,219,231,243,255,267,279,291,
4,16,28,40,52,64,76,88,100,112,124,136,148,160,172,184,196,208,220,232,244,256,268,280,292,
5,17,29,41,53,65,77,89,101,113,125,137,149,161,173,185,197,209,221,233,245,257,269,281,293,
6,18,30,42,54,66,78,90,102,114,126,138,150,162,174,186,198,210,222,234,246,258,270,282,294,
7,19,31,43,55,67,79,91,103,115,127,139,151,163,175,187,199,211,223,235,247,259,271,283,295,
8,20,32,44,56,68,80,92,104,116,128,140,152,164,176,188,200,212,224,236,248,260,272,284,296,
9,21,33,45,57,69,81,93,105,117,129,141,153,165,177,189,201,213,225,237,249,261,273,285,297,
10,22,34,46,58,70,82,94,106,118,130,142,154,166,178,190,202,214,226,238,250,262,274,286,298,
11,23,35,47,59,71,83,95,107,119,131,143,155,167,179,191,203,215,227,239,251,263,275,287,299
};
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
2, 0, 1, 13, 0, 12,
22, 11, 21, 10, 20, 9,
19, 8, 18, 7, 17, 6,
16, 5, 15, 4, 14, 3,
13, 2, 12, 1, 11, 0
static const uint16_t PERM_12_29[] = {
0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228,240,252,264,276,288,300,312,324,336,
1,13,25,37,49,61,73,85,97,109,121,133,145,157,169,181,193,205,217,229,241,253,265,277,289,301,313,325,337,
2,14,26,38,50,62,74,86,98,110,122,134,146,158,170,182,194,206,218,230,242,254,266,278,290,302,314,326,338,
3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183,195,207,219,231,243,255,267,279,291,303,315,327,339,
4,16,28,40,52,64,76,88,100,112,124,136,148,160,172,184,196,208,220,232,244,256,268,280,292,304,316,328,340,
5,17,29,41,53,65,77,89,101,113,125,137,149,161,173,185,197,209,221,233,245,257,269,281,293,305,317,329,341,
6,18,30,42,54,66,78,90,102,114,126,138,150,162,174,186,198,210,222,234,246,258,270,282,294,306,318,330,342,
7,19,31,43,55,67,79,91,103,115,127,139,151,163,175,187,199,211,223,235,247,259,271,283,295,307,319,331,343,
8,20,32,44,56,68,80,92,104,116,128,140,152,164,176,188,200,212,224,236,248,260,272,284,296,308,320,332,344,
9,21,33,45,57,69,81,93,105,117,129,141,153,165,177,189,201,213,225,237,249,261,273,285,297,309,321,333,345,
10,22,34,46,58,70,82,94,106,118,130,142,154,166,178,190,202,214,226,238,250,262,274,286,298,310,322,334,346,
11,23,35,47,59,71,83,95,107,119,131,143,155,167,179,191,203,215,227,239,251,263,275,287,299,311,323,335,347
};
/*
* NXDN SACCH interleave
*/
const unsigned char nsW[72] = {
0, 12, 24, 36, 48, 60,
1, 13, 25, 37, 49, 61,
2, 14, 26, 38, 50, 62,
3, 15, 27, 39, 51, 63,
4, 16, 28, 40, 52, 64,
5, 17, 29, 41, 53, 65,
6, 18, 30, 42, 54, 66,
7, 19, 31, 43, 55, 67,
8, 20, 32, 44, 56, 68,
9, 21, 33, 45, 57, 69,
10,22, 34, 46, 58, 70,
11,23, 35, 47, 59, 71
};
const int DSDNXDN_SACCH_m_Interleave[60] =
{
0, 12, 24, 36, 48,
1, 13, 25, 37, 49,
2, 14, 26, 38, 50,
3, 15, 27, 39, 51,
4, 16, 28, 40, 52,
5, 17, 29, 41, 53,
6, 18, 30, 42, 54,
7, 19, 31, 43, 55,
8, 20, 32, 44, 56,
9, 21, 33, 45, 57,
10, 22, 34, 46, 58,
11, 23, 35, 47, 59,
};
const int DSDNXDN_SACCH_m_PunctureList[12] = { 5, 11, 17, 23, 29, 35, 41, 47, 53, 59, 65, 71 };
const unsigned int DSDNXDN_FACCH1_m_Interleave[144] =
{
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,
3, 19, 35, 51, 67, 83, 99, 115, 131,
4, 20, 36, 52, 68, 84, 100, 116, 132,
5, 21, 37, 53, 69, 85, 101, 117, 133,
6, 22, 38, 54, 70, 86, 102, 118, 134,
7, 23, 39, 55, 71, 87, 103, 119, 135,
8, 24, 40, 56, 72, 88, 104, 120, 136,
9, 25, 41, 57, 73, 89, 105, 121, 137,
10, 26, 42, 58, 74, 90, 106, 122, 138,
11, 27, 43, 59, 75, 91, 107, 123, 139,
12, 28, 44, 60, 76, 92, 108, 124, 140,
13, 29, 45, 61, 77, 93, 109, 125, 141,
14, 30, 46, 62, 78, 94, 110, 126, 142,
15, 31, 47, 63, 79, 95, 111, 127, 143,
};
const unsigned int DSDNXDN_FACCH1_m_PunctureList[48] =
{
1U, 5U, 9U, 13U, 17U, 21U, 25U, 29U, 33U, 37U,
41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, 73U, 77U,
81U, 85U, 89U, 93U, 97U, 101U, 105U, 109U, 113U, 117U,
121U, 125U, 129U, 133U, 137U, 141U, 145U, 149U, 153U, 157U,
161U, 165U, 169U, 173U, 177U, 181U, 185U, 189U
};
const int DSDNXDN_UDCH_m_Interleave[348] = {
0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180, 192, 204, 216, 228, 240, 252, 264, 276, 288, 300, 312, 324, 336,
1, 13, 25, 37, 49, 61, 73, 85, 97, 109, 121, 133, 145, 157, 169, 181, 193, 205, 217, 229, 241, 253, 265, 277, 289, 301, 313, 325, 337,
2, 14, 26, 38, 50, 62, 74, 86, 98, 110, 122, 134, 146, 158, 170, 182, 194, 206, 218, 230, 242, 254, 266, 278, 290, 302, 314, 326, 338,
3, 15, 27, 39, 51, 63, 75, 87, 99, 111, 123, 135, 147, 159, 171, 183, 195, 207, 219, 231, 243, 255, 267, 279, 291, 303, 315, 327, 339,
4, 16, 28, 40, 52, 64, 76, 88, 100, 112, 124, 136, 148, 160, 172, 184, 196, 208, 220, 232, 244, 256, 268, 280, 292, 304, 316, 328, 340,
5, 17, 29, 41, 53, 65, 77, 89, 101, 113, 125, 137, 149, 161, 173, 185, 197, 209, 221, 233, 245, 257, 269, 281, 293, 305, 317, 329, 341,
6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 126, 138, 150, 162, 174, 186, 198, 210, 222, 234, 246, 258, 270, 282, 294, 306, 318, 330, 342,
7, 19, 31, 43, 55, 67, 79, 91, 103, 115, 127, 139, 151, 163, 175, 187, 199, 211, 223, 235, 247, 259, 271, 283, 295, 307, 319, 331, 343,
8, 20, 32, 44, 56, 68, 80, 92, 104, 116, 128, 140, 152, 164, 176, 188, 200, 212, 224, 236, 248, 260, 272, 284, 296, 308, 320, 332, 344,
9, 21, 33, 45, 57, 69, 81, 93, 105, 117, 129, 141, 153, 165, 177, 189, 201, 213, 225, 237, 249, 261, 273, 285, 297, 309, 321, 333, 345,
10, 22, 34, 46, 58, 70, 82, 94, 106, 118, 130, 142, 154, 166, 178, 190, 202, 214, 226, 238, 250, 262, 274, 286, 298, 310, 322, 334, 346,
11, 23, 35, 47, 59, 71, 83, 95, 107, 119, 131, 143, 155, 167, 179, 191, 203, 215, 227, 239, 251, 263, 275, 287, 299, 311, 323, 335, 347,
};
const int DSDNXDN_UDCH_m_PunctureList[58] = {
3, 11,
17, 25,
31, 39,
45, 53,
59, 67,
73, 81,
87, 95,
101, 109,
115, 123,
129, 137,
143, 151,
157, 165,
171, 179,
185, 193,
199, 207,
213, 221,
227, 235,
241, 249,
255, 263,
269, 277,
283, 291,
297, 305,
311, 319,
325, 333,
339, 347,
353, 361,
367, 375,
381, 389,
395, 403,
};
//#endif /* _MAIN */
#endif

View File

@@ -109,8 +109,8 @@ char * SyncTypes[44] = {
"dPMR",
"dPMR",
"dPMR",
"NXDN (sync only)",
"NXDN (sync only)",
"NXDN",
"NXDN",
"YSF", //30
"YSF",
"DMR MS VOICE",
@@ -2107,11 +2107,25 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
//NXDN
if (lls == 8 || lls == 9 || lls == 16 || lls == 17)
//if (lls == 8 || lls == 9 || lls == 16 || lls == 17 || lls == 28 || lls == 29)
if (lls == 28 || lls == 29)
{
printw ("| RAN: [%2d] ", rn);
printw ("TID: [%4d] ", tgn);
printw ("RID: [%4d] \n| ALG: [0x%02X] KEY: [0x%02X] ", src, state->nxdn_cipher_type, state->nxdn_key);
printw ("TGT: [%4d] ", tgn);
printw ("SRC: [%4d] ", src);
if (state->nxdn_alias_block_segment[0][0] > 0) //could not display if this segment is bad
{
printw ("[");
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
printw ("%s", state->nxdn_alias_block_segment[i][j]);
}
}
printw ("]");
}
printw ("\n| ALG: [0x%02X] KEY: [0x%02X] ", state->nxdn_cipher_type, state->nxdn_key);
if (state->carrier == 1)
{
printw("%s ", state->nxdn_call_type);
@@ -2152,42 +2166,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
attroff(COLOR_PAIR(2));
attron(COLOR_PAIR(3));
}
//printw("%s ", state->nxdn_call_type);
printw("\n");
}
//P25-P1
// if (lls == 0 || lls == 1)
// {
// // printw("| TID:[%8i] RID:[%8i] ", tg, rd);
// printw("| TID:[%8i] RID:[%8i] ", state->lasttg, state->lastsrc);
// printw(" MFID:[0x%02X] ", state->payload_mfid);
// printw("NAC: [0x%3X] \n", nc);
// printw("| ALG:[0x%02X] ", state->payload_algid);
// printw(" KID:[0x%04X] ", state->payload_keyid);
// printw(" MI:[0x%016llX] ", state->payload_miP);
//
// if (state->payload_algid != 0x80 && state->payload_algid != 0x0 && state->carrier == 1 && state->R == 0)
// {
// attron(COLOR_PAIR(2));
// printw("**ENC**");
// attroff(COLOR_PAIR(2));
// attron(COLOR_PAIR(3));
// if (state->payload_algid == 0xAA)
// {
// attron(COLOR_PAIR(1));
// printw(" RC4");
// attron(COLOR_PAIR(3));
// }
// if (state->payload_algid == 0x81)
// {
// attron(COLOR_PAIR(1));
// printw(" DES-OFB");
// attron(COLOR_PAIR(3));
// }
// }
// printw("\n");
// }
//DMR BS/MS Voice and Data Types
if ( lls == 0 || lls == 1 || lls == 12 || lls == 13 || lls == 10 ||
lls == 11 || lls == 32 || lls == 33 || lls == 34 || lls == 35 || lls == 36)
@@ -2676,7 +2658,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
if ( ((time(NULL) - call_matrix[9-j][5]) < 9999) && call_matrix[9-j][0] != 14 && call_matrix[9-j][0] != 15 && call_matrix[9-j][0] != 37 && call_matrix[9-j][0] != 38 )
{
printw ("| %s ", SyncTypes[call_matrix[9-j][0]]);
if (lls == 8 || lls == 9 || lls == 16 || lls == 17)
if (lls == 28 || lls == 29)
{
printw ("RAN [%2lld] ", call_matrix[9-j][1]);
printw ("TG [%4lld] ", call_matrix[9-j][4]);
@@ -2748,6 +2730,5 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
void ncursesClose ()
{
endwin();
//printf("Press CTRL+C again to close. Thanks.\n");
//printf("Run 'reset' in your terminal to clean up if necessary.\n");
}

View File

@@ -1,124 +0,0 @@
#include "dsd.h"
#include "nxdn96_const.h"
void
processNXDN96 (dsd_opts * opts, dsd_state * state)
{
int i, j, k, dibit;
char ambe_fr[4][24];
const int *w, *x, *y, *z;
if (opts->errorbars == 1)
{
fprintf (stderr, "VOICE e:");
}
#ifdef NXDN_DUMP
fprintf (stderr, "\n");
#endif
for (k = 0; k < 4; k++)
{
for (i = 0; i < 222; i++)
{
dibit = getDibit (opts, state);
#ifdef NXDN_DUMP
fprintf (stderr, "%c", dibit + 48);
#endif
}
#ifdef NXDN_DUMP
fprintf (stderr, " ");
#endif
if (k < 3)
{
for (j = 0; j < 4; j++)
{
w = nW;
x = nX;
y = nY;
z = nZ;
for (i = 0; i < 36; i++)
{
dibit = getDibit (opts, state);
#ifdef NXDN_DUMP
fprintf (stderr, "%c", dibit + 48);
#endif
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
#ifdef NXDN_DUMP
fprintf (stderr, " ");
#endif
}
}
else
{
for (j = 0; j < 3; j++) // we skip the last voice frame until frame sync can work with < 24 symbols
{
w = nW;
x = nX;
y = nY;
z = nZ;
for (i = 0; i < 36; i++)
{
dibit = getDibit (opts, state);
#ifdef NXDN_DUMP
fprintf (stderr, "%c", dibit + 48);
#endif
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
#ifdef NXDN_DUMP
fprintf (stderr, " ");
#endif
}
}
if (k < 3)
{
for (i = 0; i < 18; i++)
{
dibit = getDibit (opts, state);
#ifdef NXDN_DUMP
fprintf (stderr, "%c", dibit + 48);
#endif
}
#ifdef NXDN_DUMP
fprintf (stderr, " ");
#endif
}
else
{
for (i = 0; i < 30; i++)
{
dibit = getDibit (opts, state);
#ifdef NXDN_DUMP
fprintf (stderr, "%c", dibit + 48);
#endif
}
}
}
#ifdef NXDN_DUMP
fprintf (stderr, "\n");
#endif
if (opts->errorbars == 1)
{
printf ("\n");
}
}

View File

@@ -24,10 +24,8 @@
/* Include ------------------------------------------------------------------*/
//#include "nxdnconvolution.h"
#include <stdio.h>
//#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

View File

@@ -1,127 +0,0 @@
#include "dsd.h"
#include "nxdn_const.h"
void processNXDNData (dsd_opts * opts, dsd_state * state)
{
int i, dibit = 0;
unsigned char sacch_raw[72] = {0};
unsigned char sacch_decoded[32] = {0}; /* 26 bit + 6 bit CRC */
unsigned char *pr;
uint8_t CrcIsGood = 0;
uint8_t StructureField = 0;
uint8_t RAN = 0;
uint8_t PartOfFrame = 0;
/* Remove compiler warning */
//UNUSED_VARIABLE(dibit);
//if using NXDN Scrambling, run LFSRN (dummy ambe values) to continue seed
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
{
char ambe_temp[49] = {0};
char ambe_d[49] = {0};
for (i = 0; i < 4; i++)
{
LFSRN(ambe_temp, ambe_d, state);
}
}
if (opts->errorbars == 1)
{
fprintf(stderr, "DATA - ");
}
/* Start pseudo-random NXDN sequence after
* LICH = 16 bit = 8 dibit
* ==> Index 8 */
pr = (unsigned char *)(&nxdnpr2[8]);
for (i = 0; i < 30; i++)
{
dibit = getDibit (opts, state);
/* Store and de-interleave directly SACCH bits */
sacch_raw[(2*i)] = ((*pr) & 1) ^ (1 & (dibit >> 1)); // bit 1
sacch_raw[(2*i)+1] = (1 & dibit); // bit 0
pr++;
#ifdef NXDN_DUMP
fprintf(stderr, "%c", dibit + 48);
#endif
}
#ifdef NXDN_DUMP
fprintf(stderr, " ");
#endif
/* Decode the SACCH fields */
CrcIsGood = NXDN_SACCH_raw_part_decode(sacch_raw, sacch_decoded);
StructureField = (sacch_decoded[0]<<1 | sacch_decoded[1]<<0) & 0x03; /* Compute the Structure Field (remove 2 first bits of the SR Information in the SACCH) */
RAN = (sacch_decoded[2]<<5 | sacch_decoded[3]<<4 | sacch_decoded[4]<<3 | sacch_decoded[5]<<2 | sacch_decoded[6]<<1 | sacch_decoded[7]<<0) & 0x3F; /* Compute the RAN (6 last bits of the SR Information in the SACCH) */
/* Compute the Part of Frame according to the structure field */
if (StructureField == 3) PartOfFrame = 0;
else if(StructureField == 2) PartOfFrame = 1;
else if(StructureField == 1) PartOfFrame = 2;
else if(StructureField == 0) PartOfFrame = 3;
else PartOfFrame = 0; /* Normally we should never go here */
/* Fill the SACCH structure */
state->NxdnSacchRawPart[PartOfFrame].CrcIsGood = CrcIsGood;
state->NxdnSacchRawPart[PartOfFrame].StructureField = StructureField;
state->NxdnSacchRawPart[PartOfFrame].RAN = RAN;
memcpy(state->NxdnSacchRawPart[PartOfFrame].Data, &sacch_decoded[8], 18); /* Copy the 18 bits of SACCH content */
//fprintf(stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
if(CrcIsGood)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
fprintf (stderr, "%s", KNRM);
//fprintf (stderr, "(CRC OK) ");
state->nxdn_last_ran = RAN; //disable, try to grab this in voice instead
}
else
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "(CRC ERR) ");
fprintf (stderr, "%s", KNRM);
}
/* Decode the SACCH only when all 4 voice frame
* SACCH spare parts have been received */
if(PartOfFrame == 3)
{
/* Decode the entire SACCH content */
NXDN_SACCH_Full_decode(opts, state);
}
for (i = 0; i < 144; i++)
{
dibit = getDibit (opts, state);
#ifdef NXDN_DUMP
fprintf(stderr, "%c", dibit + 48);
#endif
}
if (opts->errorbars == 1)
{
fprintf(stderr, "\n");
}
/* Reset the SACCH CRCs when all 4 voice frame
* SACCH spare parts have been received */
if(PartOfFrame == 3)
{
/* Reset all CRCs of the SACCH */
for(i = 0; i < 4; i++) state->NxdnSacchRawPart[i].CrcIsGood = 0;
//reset Scrambler seed
if (state->nxdn_cipher_type == 0x1)
{
state->payload_miN = 0;
}
}
} /* End processNXDNData() */

516
src/nxdn_deperm.c Normal file
View File

@@ -0,0 +1,516 @@
//NXDN descramble/deperm/depuncture and 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[92];
uint16_t crc = 0; //crc calculated by function
uint16_t check = 0; //crc from payload for comparison
int out;
char buf[128];
uint8_t answer[12];
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; //0
depunc[out++] = deperm[i+1];
depunc[out++] = deperm[i+2];
}
trellis_decode(trellis_buf, depunc, 92);
//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);
cfill(answer, trellis_buf, 12); //<-92 bits in 12 bytes, leaves 4 bits on tail to scrub off
if (opts->payload == 1)
{
fprintf (stderr, "\n");
fprintf (stderr, " FACCH Payload ");
answer[11] = answer[11] & 0xF0; //scrub the garbage 4 bits from cfill on the end for ppayload print
for (int i = 0; i < 12; i++)
{
fprintf (stderr, "[%02X]", answer[i]);
}
fprintf (stderr, " - %03X %03X", crc, check);
}
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
else NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
}
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; //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; //0
}
trellis_decode(trellis_buf, depunc, 32);
crc = crc6(trellis_buf, 32);
for (int i = 0; i < 6; i++)
{
check = check << 1;
check = check + trellis_buf[i+26];
}
//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;
//gives unusual results when non_superframe SF, not sure why, or if its accurate yet
if (state->nxdn_sacch_non_superframe == FALSE)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
//fprintf (stderr, " RAN %02d - SF %01d - POF %02d", ran, sf, part_of_frame);
fprintf (stderr, "%s", KNRM);
}
else fprintf (stderr, " ");
//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_sacch_non_superframe == FALSE) //only set these when superframe type, or get odd results (can't verify correct)
{
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 handling
if (part_of_frame == 3 && state->nxdn_sacch_non_superframe == FALSE)
{
NXDN_SACCH_Full_decode (opts, state);
}
if (opts->payload == 1)
{
fprintf (stderr, "\n");
fprintf (stderr, " SACCH Segment #%d - %05X", part_of_frame+1, sacch_segment);
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];
}
trellis_decode(trellis_buf, depunc, 199);
crc = crc15(trellis_buf, 199);
uint8_t MessageType = 0;
for (int i = 0; i < 6; i++)
{
MessageType = MessageType << 1;
MessageType = MessageType + trellis_buf[i+2]; //on +2 on any facch1
}
fprintf (stderr, " FACCH2/UDCH ");
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
else
{
fprintf (stderr, "CRC ERR ");
//NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
}
}
void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
{
uint8_t deperm[300];
uint8_t depunc[350];
uint8_t decode[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];
}
trellis_decode(decode, depunc, 171);
crc = crc16cac(decode, 171);
fprintf (stderr, " CAC ");
//these messages can also be double depending on CAC type, so need to check that first, and split the
//decode first, then we can run each message type and element decode if necessary
uint8_t MessageType = 0;
for (int i = 0; i < 6; i++)
{
MessageType = MessageType << 1;
MessageType = MessageType + decode[i+2]; //double check this on CAC
}
//nxdn_message_type (opts, state, MessageType);
//not sure if CAC will contain same type of 'elements' as a FACCH or SACCH
//or if its purely for Control Signalling
//need to check CAC type, CAC can contain two messages, or one long message,
//then we need to break up the messages and process them individually
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, decode);
else
{
fprintf (stderr, " CRC ERR ");
//NXDN_Elements_Content_decode(opts, state, 0, decode);
}
}
//its own function now to consolidate all the message types to one area to keep track of
//may need to add a 'source' of message so we don't call messages not associated with cac, facch, sacch, etc
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
{
//if (opts->payload == 1) fprintf (stderr, " M%02X ", MessageType);
fprintf (stderr, "%s", KYEL);
if (MessageType == 0x10) fprintf(stderr, " IDLE");
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");
//CAC only? valid in all, or just CAC?
else if (MessageType == 0x04) fprintf(stderr, " VCALL ASSGN");
else if (MessageType == 0x0E) fprintf(stderr, " DCALL ASSGN");
else if (MessageType == 0x18) fprintf(stderr, " SITE_INFO");
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO"); //per Op25
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO"); //per the damn manual
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");
//end CAC only?
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS"); //observed from DSDPlus and OP25 dumps
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK"); //need to verify these for accuracy vs DSDPlus
else fprintf(stderr, " Unknown M%02X", MessageType);
fprintf (stderr, "%s", KNRM);
//zero out stale values so they won't persist after a transmit release
if (MessageType == 0x08 || MessageType == 0x10)
{
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;
}
}
//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;
}
//utility functions here, consider moving to its own file later
static inline void cfill(uint8_t result[], const uint8_t src[], int len)
{
for (int i=0; i<len; i++)
result[i] = load_i(src+i*8, 8);
}
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;
}
// trellis_1_2 encode: source is in bits, result in bits
static inline 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)
static inline 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;
}
// fprintf (stderr, "stats\t%d %d %d %d\n", dstats[0], dstats[1], dstats[2], dstats[3]);
}
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;
}

523
src/nxdn_element.c Normal file
View File

@@ -0,0 +1,523 @@
/*
============================================================================
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
============================================================================
*/
#include "dsd.h"
#define NXDN_VCALL 0b000001 /* VCALL = VCALL_REQ = VCALL_RESP */
#define NXDN_VCALL_IV 0b000011
#define NXDN_DCALL_HDR 0b001001 /* Header Format - NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
#define NXDN_DCALL_USR 0b001011 /* User Data Format */
#define NXDN_DCALL_ACK 0b001100
#define NXDN_REL_EX 0b000111
#define NXDN_HEAD_DLY 0b001111
#define NXDN_SDCALL_REQ_HDR 0b111000 /* Header Format */
#define NXDN_SDCALL_REQ_USR 0b111001 /* User Data Format */
#define NXDN_SDCALL_RESP 0b111011
#define NXDN_SDCALL_IV 0b111010
#define NXDN_STAT_INQ_REQ 0b110000
#define NXDN_STAT_INQ_RESP 0b110001
#define NXDN_STAT_REQ 0b110010
#define NXDN_STAT_RESP 0b110011
#define NXDN_REM_CON_REQ 0b110100
#define NXDN_REM_CON_RESP 0b110101
#define NXDN_REM_CON_E_REQ 0b110110
#define NXDN_REM_CON_E_RESP 0b110111
//NXDN_VCALL_REQ = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
//NXDN_VCALL_RESP = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
#define NXDN_VCALL_REC_REQ = 0b000010 /* NXDN_VCALL_REC_REQ = NXDN_VCALL_REC_RESP */
//NXDN_VCALL_REC_RESP = 0b000010,
#define NXDN_VCALL_CONN_REQ = 0b000011 /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
//NXDN_VCALL_CONN_RESP = 0b000011, /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
#define NXDN_VCALL_ASSGN_DUP = 0b000101
//NXDN_DCALL_REQ = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
//NXDN_DCALL_RESP = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
#define NXDN_DCALL_REC_REQ = 0b001010 /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
//NXDN_DCALL_REC_RESP = 0b001010, /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
#define NXDN_DCALL_ASSGN = 0b001110
#define NXDN_DCALL_ASSGN_DUP = 0b001101
#define NXDN_IDLE 0b010000
#define NXDN_DISC_REQ = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
#define NXDN_DISC = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
/*
* @brief : This function decodes the full SACCH (when 4 voice frame parts
* have been successfully received)
*
* @param opts : Option structure parameters pointer
*
* @param state : State structure parameters pointer
*
* @return None
*
*/
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state)
{
uint8_t SACCH[72];
uint32_t i;
uint8_t CrcCorrect = 1;
/* Consider all SACCH CRC parts as corrects */
CrcCorrect = 1;
/* Reconstitute the full 72 bits SACCH */
for(i = 0; i < 4; i++)
{
memcpy(&SACCH[i * 18], state->nxdn_sacch_frame_segment[i], 18);
/* Check CRC */
if (state->nxdn_sacch_frame_segcrc[i] != 0) CrcCorrect = 0;
}
/* Decodes the element content */
//if (CrcCorrect == 1) NXDN_Elements_Content_decode(opts, state, CrcCorrect, SACCH);
//run it, and let each element handler decide what is okay for console, internal, and terminal assignment
if (1 == 1) NXDN_Elements_Content_decode(opts, state, CrcCorrect, SACCH);
} /* End NXDN_SACCH_Full_decode() */
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
uint8_t CrcCorrect, uint8_t * ElementsContent)
{
uint32_t i;
uint8_t MessageType;
uint64_t CurrentIV = 0;
unsigned long long int FullMessage = 0;
/* 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;
//only run message type if good CRC?
if (CrcCorrect == 1) 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;
/* Decode the right "Message Type" */
switch(MessageType)
{
//Alias 0x3F
case 0x3F:
{
NXDN_decode_Alias(opts, state, ElementsContent);
}
break;
//Idle
case NXDN_IDLE:
{
//sprintf (state->nxdn_call_type, "NXDN IDLE ");
break;
}
/* VCALL */
case NXDN_VCALL:
{
/* Set the CRC state */
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
/* Decode the "VCALL" message */
NXDN_decode_VCALL(opts, state, ElementsContent);
/* Check the "Cipher Type" and the "Key ID" validity */
if(CrcCorrect)
{
state->NxdnElementsContent.CipherParameterValidity = 1;
}
else state->NxdnElementsContent.CipherParameterValidity = 0;
break;
} /* End case NXDN_VCALL: */
/* VCALL_IV */
case NXDN_VCALL_IV:
{
/* Set the CRC state */
state->NxdnElementsContent.VCallIvCrcIsGood = CrcCorrect;
/* Decode the "VCALL_IV" message */
NXDN_decode_VCALL_IV(opts, state, ElementsContent);
if(CrcCorrect)
{
/* CRC is correct, copy the next theorical IV to use directly from the
* received VCALL_IV */
memcpy(state->NxdnElementsContent.NextIVComputed, state->NxdnElementsContent.IV, 8);
}
else
{
/* CRC is incorrect, compute the next IV to use */
CurrentIV = 0;
/* Convert the 8 bytes buffer into a 64 bits integer */
for(i = 0; i < 8; i++)
{
CurrentIV |= state->NxdnElementsContent.NextIVComputed[i];
CurrentIV = CurrentIV << 8;
}
}
break;
} /* End case NXDN_VCALL_IV: */
/* Unknown Message Type */
default:
{
//fprintf(stderr, "Unknown Message type ");
break;
}
} /* End switch(MessageType) */
} /* End NXDN_Elements_Content_decode() */
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint8_t Alias1 = 0x0; //value of an ascii 'NULL' character
uint8_t Alias2 = 0x0;
uint8_t Alias3 = 0x0;
uint8_t Alias4 = 0x0;
uint8_t blocknumber = 0;
uint8_t CrcCorrect = 1;
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
//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
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);
//sanity check to prevent OOB array assignment
if (blocknumber > 0 && blocknumber < 5)
{
//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 "null" 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", 0);
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", 0);
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", 0);
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", 0);
}
if (CrcCorrect) //was by blocknumber being 4, but wasn't as frequent as I would like in the console
{
fprintf (stderr, " "); //probably just want a space instead
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
fprintf (stderr, "%s", state->nxdn_alias_block_segment[i][j]);
}
}
fprintf (stderr, " ");
}
else fprintf (stderr, " CRC ERR "); //redundant print? or okay?
}
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint8_t location_id = 0;
uint8_t channel_ifno = 0;
}
/*
* @brief : This function decodes the VCALL message
*
* @param opts : Option structure parameters pointer
*
* @param state : State structure parameters pointer
*
* @param Message : A 64 bit buffer containing the VCALL message to decode
*
* @return None
*
*/
void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
//uint32_t i;
uint8_t CCOption = 0;
uint8_t CallType = 0;
uint8_t VoiceCallOption = 0;
uint16_t SourceUnitID = 0;
uint16_t DestinationID = 0;
uint8_t CipherType = 0;
uint8_t KeyID = 0;
uint8_t DuplexMode[32] = {0};
uint8_t TransmissionMode[32] = {0};
unsigned long long int FullMessage = 0;
/* Message[0..7] contains :
* - The "F1" and "F2" flags
* - The "Message Type" (already decoded before calling this function)
*
* So no need to decode it a second time
*/
/* Decode "CC Option" */
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
state->NxdnElementsContent.CCOption = CCOption;
/* Decode "Call Type" */
CallType = (uint8_t)ConvertBitIntoBytes(&Message[16], 3);
state->NxdnElementsContent.CallType = CallType;
/* Decode "Voice Call Option" */
VoiceCallOption = (uint8_t)ConvertBitIntoBytes(&Message[19], 5);
state->NxdnElementsContent.VoiceCallOption = VoiceCallOption;
/* Decode "Source Unit ID" */
SourceUnitID = (uint16_t)ConvertBitIntoBytes(&Message[24], 16);
state->NxdnElementsContent.SourceUnitID = SourceUnitID;
/* Decode "Destination ID" */
DestinationID = (uint16_t)ConvertBitIntoBytes(&Message[40], 16);
state->NxdnElementsContent.DestinationID = DestinationID;
/* Decode the "Cipher Type" */
CipherType = (uint8_t)ConvertBitIntoBytes(&Message[56], 2);
state->NxdnElementsContent.CipherType = CipherType;
/* Decode the "Key ID" */
KeyID = (uint8_t)ConvertBitIntoBytes(&Message[58], 6);
state->NxdnElementsContent.KeyID = KeyID;
/* Print the "CC Option" */
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
if(CCOption & 0x20) fprintf(stderr, "Priority Paging ");
if((CipherType == 2) || (CipherType == 3))
{
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 1;
state->NxdnElementsContent.PartOfNextEncryptedFrame = 2;
}
else
{
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 1;
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
}
/* Print the "Call Type" */
fprintf (stderr, "%s", KGRN);
fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType));
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
/* Print the "Voice Call Option" */
NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
//fprintf (stderr, "%s", KGRN);
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
fprintf (stderr, "%s", KNRM);
/* Print the "Cipher Type" */
if(CipherType != 0)
{
fprintf (stderr, "\n %s", KYEL);
fprintf(stderr, "%s - ", NXDN_Cipher_Type_To_Str(CipherType));
//state->nxdn_cipher_type = CipherType;
}
/* Print the Key ID */
if(CipherType != 0)
{
fprintf(stderr, "Key ID %u - ", KeyID & 0xFF);
fprintf (stderr, "%s", KNRM);
//state->nxdn_key = (KeyID & 0xFF);
}
if (state->nxdn_cipher_type == 0x01 && state->R > 0) //scrambler key value
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, "Value: %05lld", state->R);
fprintf (stderr, "%s", KNRM);
}
if(state->NxdnElementsContent.VCallCrcIsGood)
{
if ( (SourceUnitID & 0xFFFF) > 0 )
{
state->nxdn_last_rid = SourceUnitID & 0xFFFF; //only grab if CRC is okay
state->nxdn_last_tg = (DestinationID & 0xFFFF);
state->nxdn_key = (KeyID & 0xFF);
state->nxdn_cipher_type = CipherType; //will this set to zero if no enc?
}
}
else
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, "(CRC ERR) ");
fprintf (stderr, "%s", KNRM);
}
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
if (state->nxdn_cipher_type != 0)
{
state->dmr_encL = 1;
}
if (state->nxdn_cipher_type == 0 || state->R != 0)
{
state->dmr_encL = 0;
}
} /* End NXDN_decode_VCALL() */
/*
* @brief : This function decodes the VCALL_IV message
*
* @param opts : Option structure parameters pointer
*
* @param state : State structure parameters pointer
*
* @param Message : A 72 bit buffer containing the VCALL_IV message to decode
*
* @return None
*
*/
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint32_t i;
/* Message[0..7] contains :
* - The "F1" and "F2" flags
* - The "Message Type" (already decoded before calling this function)
*
* So no need to decode it a second time
*
* Message[8..71] contains : The 64 bits IV
*/
state->payload_miN = 0; //zero out
/* Extract the IV from the VCALL_IV message */
for(i = 0; i < 8; i++)
{
state->NxdnElementsContent.IV[i] = (uint8_t)ConvertBitIntoBytes(&Message[(i + 1) * 8], 8);
state->payload_miN = state->payload_miN << 8 | state->NxdnElementsContent.IV[i];
}
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 2;
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
} /* End NXDN_decode_VCALL_IV() */
/*
* @brief : This function decodes the "Call Type" and return the
* ASCII string corresponding to it.
*
* @param CallType : The call type parameter to decode
*
* @return An ASCII string of the "Call Type"
*
*/
char * NXDN_Call_Type_To_Str(uint8_t CallType)
{
char * Ptr = NULL;
switch(CallType)
{
case 0: Ptr = "Broadcast Call"; break;
case 1: Ptr = "Group Call"; break;
case 2: Ptr = "Unspecified Call"; break;
case 3: Ptr = "Reserved"; break;
case 4: Ptr = "Individual Call"; break;
case 5: Ptr = "Reserved"; break;
case 6: Ptr = "Interconnect Call"; break;
case 7: Ptr = "Speed Dial Call"; break;
default: Ptr = "Unknown Call Type"; break;
}
return Ptr;
} /* End NXDN_Call_Type_To_Str() */
/*
* @brief : This function decodes the "Voice Call Option" and return the
* ASCII string corresponding to it.
*
* @param VoiceCallOption : The call type parameter to decode
*
* @param Duplex : A 32 bytes ASCII buffer pointer where store
* the Duplex/Half duplex mode
*
* @param TransmissionMode : A 32 bytes ASCII buffer pointer where store
* the transmission mode (bit rate)
*
* @return An ASCII string of the "Voice Call Option"
*
*/
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode)
{
char * Ptr = NULL;
Duplex[0] = 0;
TransmissionMode[0] = 0;
if(VoiceCallOption & 0x10) strcpy((char *)Duplex, "Duplex");
else strcpy((char *)Duplex, "Half Duplex");
switch(VoiceCallOption & 0x17)
{
case 0: Ptr = "4800bps/EHR"; break;
case 2: Ptr = "9600bps/EHR"; break;
case 3: Ptr = "9600bps/EFR"; break;
default: Ptr = "Reserved Voice Call Option"; break;
}
strcpy((char *)TransmissionMode, Ptr);
} /* End NXDN_Voice_Call_Option_To_Str() */
/*
* @brief : This function decodes the "Cipher Type" and return the
* ASCII string corresponding to it.
*
* @param CipherType : The cipher type parameter to decode
*
* @return An ASCII string of the "Cipher Type"
*
*/
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType)
{
char * Ptr = NULL;
switch(CipherType)
{
case 0: Ptr = ""; break; /* Non-ciphered mode / clear call */
case 1: Ptr = "Scrambler"; break;
case 2: Ptr = "DES"; break;
case 3: Ptr = "AES"; break;
default: Ptr = "Unknown Cipher Type"; break;
}
return Ptr;
} /* End NXDN_Cipher_Type_To_Str() */

260
src/nxdn_frame.c Normal file
View File

@@ -0,0 +1,260 @@
//NXDN frame handler
//Reworked portions from Osmocom OP25
//from Osmocom OP25 rx_sync.cc
/* -*- 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"
#include <assert.h>
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;
int answer_len=0;
uint8_t answer[32];
uint8_t sacch_answer[32];
int lich_parity_received;
int lich_parity_computed;
int voice=0;
int facch=0;
int facch2=0;
int sacch=0;
int cac=0;
int sr_structure;
int sr_ran;
uint8_t lich_dibits[8] = {0};
uint8_t sacch_bits[60] = {0};
uint8_t facch_bits_a[144] = {0};
uint8_t facch_bits_b[144] = {0};
uint8_t cac_bits[300] = {0};
uint8_t facch2_bits[348] = {0}; //facch2 or udch, same amount of bits
//nxdn bit buffer, for easy assignment handling
int nxdn_bit_buffer[364] = {0};
int nxdn_dibit_buffer[182] = {0};
memset (dbuf, 0, sizeof(dbuf));
//collect lich bits first, if they are good, then we can collect the rest of them
//otherwise we will either exit, or skip and then exit (depends on performance)
for (int i = 0; i < 8; i++)
{
lich_dibits[i] = dbuf[i] = getDibit(opts, state);
}
nxdn_descramble (lich_dibits, 8);
lich = 0;
for (int i=0; i<8; i++)
{
lich |= (lich_dibits[i] >> 1) << (7-i);
}
lich_parity_received = lich & 1;
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1;
lich = lich >> 1;
if (lich_parity_received != lich_parity_computed)
{
//fprintf(stderr, "NXDN lich parity error, ignoring frame \n");
return;
}
voice = 0;
facch = 0;
facch2 = 0;
sacch = 0;
cac = 0;
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 0x2e:
case 0x2f:
case 0x48:
case 0x49:
case 0x4e:
case 0x4f:
case 0x69:
case 0x6f:
facch2 = 1;
break;
case 0x32: //facch in 1, vch in 2
case 0x33:
case 0x52:
case 0x53:
case 0x73:
voice = 2;
facch = 1;
sacch = 1;
break;
case 0x34: //vch in 1, facch in 2
case 0x35:
case 0x54:
case 0x55:
case 0x75:
voice = 1;
facch = 2;
sacch = 1;
break;
case 0x36: //vch in both
case 0x37:
case 0x56:
case 0x57:
case 0x77:
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:
case 0x61:
case 0x71:
voice = 0;
facch = 3;
sacch = 1;
break;
case 0x38: //sacch only (NULL?)
case 0x39:
sacch = 1;
break;
default:
if (opts->payload == 1) fprintf(stderr, "unsupported NXDN lich type 0x%02X\n", lich);
//fprintf(stderr, " unsupported NXDN lich 0x%02X\n", lich);
voice = 0;
goto END;
break;
} // end of switch(lich)
//printframesync after determining we have a good lich and it has something in it
if (voice || facch || sacch || facch2 || cac)
{
// if (state->synctype == 28) fprintf (stderr, " +");
// else fprintf (stderr, " -");
if (opts->frame_nxdn48 == 1)
{
printFrameSync (opts, state, "NXDN48 ", 0, "-");
}
else printFrameSync (opts, state, "NXDN96 ", 0, "-");
if (opts->payload == 1) fprintf (stderr, "L%02X -", lich);
}
//now that we have a good LICH, we can collect all of our dibits
//and push them to the proper places for decoding (if LICH calls for that type)
for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
{
dbuf[i+8] = getDibit(opts, state);
}
nxdn_descramble (dbuf, 182); //sizeof(dbuf)
//seperate our dbuf (dibit_buffer) into individual bit array
for (int i = 0; i < 182; i++)
{
nxdn_bit_buffer[i*2] = dbuf[i] >> 1;
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
}
//sacch
for (int i = 0; i < 60; i++)
{
sacch_bits[i] = nxdn_bit_buffer[i+16];
}
//facch
for (int i = 0; i < 144; i++)
{
facch_bits_a[i] = nxdn_bit_buffer[i+16+60];
facch_bits_b[i] = nxdn_bit_buffer[i+16+60+144];
}
//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];
}
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
//sacch needs extra handling depending on superframe or non-superframe variety
if (voice)
{
fprintf (stderr, "%s", KGRN);
fprintf (stderr, " Voice ");
fprintf (stderr, "%s", KNRM);
}
else
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Data ");
fprintf (stderr, "%s", KNRM);
//roll the voice scrambler LFSR here if key available to advance seed (usually just needed on NXDN96)
if (state->nxdn_cipher_type == 0x1 && state->R != 0) //!voice, or voice == 0
{
char ambe_temp[49] = {0};
char ambe_d[49] = {0};
for (int i = 0; i < 4; i++)
{
LFSRN(ambe_temp, ambe_d, state);
}
}
}
if (sacch)
{
state->nxdn_sacch_non_superframe = (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) ? true : false;
nxdn_deperm_sacch(opts, state, sacch_bits);
}
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits);
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
if (facch & 2)
{
//don't run if duplicate of facch in first, may still run both if one has bad CRC
//hopefully can mitigate that later when we having the CRC function working properly for FACCH
if (facch_bits_a != facch_bits_b) nxdn_deperm_facch(opts, state, facch_bits_b); //don't think this check works correctly
}
if (voice) nxdn_voice (opts, state, voice, dbuf); //working great now with new simplified method!
fprintf (stderr, "\n");
END: ; //do nothing
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,169 +1,101 @@
#include "dsd.h"
#include "nxdn_const.h"
//a new and simplified voice handling routine for NXDN based on lich signalling
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
void processNXDNVoice (dsd_opts * opts, dsd_state * state)
//Originally found at - https://github.com/LouisErigHerve/dsd
//Modified for use in DSD-FME
#include "dsd.h"
const int nW[36] = { 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 2,
0, 2, 0, 2, 0, 2,
0, 2, 0, 2, 0, 2
};
const int nX[36] = { 23, 10, 22, 9, 21, 8,
20, 7, 19, 6, 18, 5,
17, 4, 16, 3, 15, 2,
14, 1, 13, 0, 12, 10,
11, 9, 10, 8, 9, 7,
8, 6, 7, 5, 6, 4
};
const int nY[36] = { 0, 2, 0, 2, 0, 2,
0, 2, 0, 3, 0, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3
};
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
2, 0, 1, 13, 0, 12,
22, 11, 21, 10, 20, 9,
19, 8, 18, 7, 17, 6,
16, 5, 15, 4, 14, 3,
13, 2, 12, 1, 11, 0
};
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182])
{
int i, j, dibit;
int start, stop;
char ambe_fr[4][24];
unsigned char sacch_raw[72] = {0};
unsigned char sacch_decoded[32] = {0}; /* 26 bit + 6 bit CRC */
const int *w, *x, *y, *z;
unsigned char *pr;
uint8_t CrcIsGood = 0;
uint8_t StructureField = 0;
uint8_t RAN = 0;
uint8_t PartOfFrame = 0;
char ambe7bytesArray[7] = {0};
int PartOfEncryptedSuperFrame = 0;
const int *w, *x, *y, *z;
if (opts->errorbars == 1)
{
fprintf(stderr, "VOICE - ");
}
//these conditions will determine our starting and stopping value for voice
//i.e. voice in first, voice in second, or voice in both
if (voice == 1 || voice == 3) start = 0;
if (voice == 1) stop = 2;
if (voice == 2) start = 2;
if (voice == 2 || voice == 3) stop = 4;
/* Start pseudo-random NXDN sequence after
* LICH = 16 bit = 8 dibit
* ==> Index 8 */
pr = (unsigned char *)(&nxdnpr2[8]);
for (i = 0; i < 30; i++)
{
dibit = getDibit (opts, state);
/* Store and de-interleave directly SACCH bits */
sacch_raw[(2*i)] = ((*pr) & 1) ^ (1 & (dibit >> 1)); // bit 1
sacch_raw[(2*i)+1] = (1 & dibit); // bit 0
// TODO : Deinterleave is done in the "NXDN_SACCH_decode" function
// /* Store and de-interleave directly SACCH bits */
// sacch_raw[nsW[2*i]] = *pr ^ (1 & (dibit >> 1)); // bit 1
// sacch_raw[nsW[2*i+1]] = (1 & dibit); // bit 0
pr++;
#ifdef NXDN_DUMP
fprintf(stderr, "%c", dibit + 48);
#endif
}
#ifdef NXDN_DUMP
fprintf(stderr, " ");
#endif
// TODO : Unpuncture is done in the "NXDN_SACCH_decode" function
// /* For the sake of simplicity, handle the re-insertion of punctured bits here.
// * This makes the actual Viterbi decoder code a LOT simpler. */
// for (i = 30; i < 36; i++)
// {
// sacch_raw[nsW[2*i]] = 0;
// sacch_raw[nsW[2*i+1]] = 0;
// }
/* Decode the SACCH fields */
CrcIsGood = NXDN_SACCH_raw_part_decode(sacch_raw, sacch_decoded);
StructureField = (sacch_decoded[0]<<1 | sacch_decoded[1]<<0) & 0x03; /* Compute the Structure Field (remove 2 first bits of the SR Information in the SACCH) */
RAN = (sacch_decoded[2]<<5 | sacch_decoded[3]<<4 | sacch_decoded[4]<<3 | sacch_decoded[5]<<2 | sacch_decoded[6]<<1 | sacch_decoded[7]<<0) & 0x3F; /* Compute the RAN (6 last bits of the SR Information in the SACCH) */
/* Compute the Part of Frame according to the structure field */
if (StructureField == 3) PartOfFrame = 0;
else if(StructureField == 2) PartOfFrame = 1;
else if(StructureField == 1) PartOfFrame = 2;
else if(StructureField == 0) PartOfFrame = 3;
else PartOfFrame = 0; /* Normally we should never go here */
/* Fill the SACCH structure */
state->NxdnSacchRawPart[PartOfFrame].CrcIsGood = CrcIsGood;
state->NxdnSacchRawPart[PartOfFrame].StructureField = StructureField;
state->NxdnSacchRawPart[PartOfFrame].RAN = RAN;
memcpy(state->NxdnSacchRawPart[PartOfFrame].Data, &sacch_decoded[8], 18); /* Copy the 18 bits of SACCH content */
//fprintf(stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
//if(CrcIsGood) fprintf(stderr, " (OK) - ");
if(CrcIsGood)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, "RAN=%02d - Part %d/4 ", RAN, PartOfFrame + 1);
fprintf (stderr, "%s", KNRM);
//fprintf (stderr, "(CRC OK) ");
state->nxdn_last_ran = RAN; //disable, try to grab this in voice instead
}
else
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, "(CRC ERR) ");
fprintf (stderr, "%s", KNRM);
}
/* Determine the current part of superframe
* (in an AES or DES encrypted frame, two superframes
* are used with the same IV, so to decipher the sound
* correctly we must know the current superframe part) */
if(state->NxdnElementsContent.PartOfNextEncryptedFrame == 1) PartOfEncryptedSuperFrame = 0;
else if(state->NxdnElementsContent.PartOfNextEncryptedFrame == 2) PartOfEncryptedSuperFrame = 1;
else PartOfEncryptedSuperFrame = 0;
/* Decode the SACCH only when all 4 voice frame
* SACCH spare parts have been received */
if(PartOfFrame == 3)
{
/* Decode the entire SACCH content */
NXDN_SACCH_Full_decode(opts, state);
}
if (opts->errorbars == 1)
{
//fprintf(stderr, "e:");
}
/* Start pseudo-random NXDN sequence after
* LICH = 16 bit = 8 dibit +
* SACCH = 60 bit = 30 dibit
* = 76 bit = 38 dibit
* ==> Index 38 */
pr = (unsigned char *)(&nxdnpr2[38]);
for (j = 0; j < 4; j++)
for (start; start < stop; start++)
{
w = nW;
x = nX;
y = nY;
z = nZ;
for (i = 0; i < 36; i++)
{
dibit = getDibit (opts, state);
#ifdef NXDN_DUMP
fprintf(stderr, "%c", dibit + 48);
#endif
ambe_fr[*w][*x] = *pr ^ (1 & (dibit >> 1)); // bit 1
pr++;
ambe_fr[*y][*z] = (1 & dibit); // bit 0
//skip 8 lich and 30 sacch dibits already in buffer plus 36 times start position
ambe_fr[*w][*x] = dbuf[i+38+start*36] >> 1;
ambe_fr[*y][*z] = dbuf[i+38+start*36] & 1;
w++;
x++;
y++;
z++;
}
if (opts->payload == 1)
{
//fprintf(stderr, "\n");
}
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
state->p25vc++; //increment counter by one here?
} /* End for (j = 0; j < 4; j++) */
if (opts->errorbars == 1)
}
if (opts->payload == 1)
{
fprintf(stderr, "\n");
}
/* Reset the SACCH CRCs when all 4 voice frame
* SACCH spare parts have been received */
if(PartOfFrame == 3)
{
/* Reset all CRCs of the SACCH */
if (state->R > 0 && state->nxdn_cipher_type == 0x1)
{
state->payload_miN = state->R;
}
for(i = 0; i < 4; i++) state->NxdnSacchRawPart[i].CrcIsGood = 0;
}
} /* End processNXDNVoice() */
}