mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 17:27:21 -05:00
NXDN FSW and Lich Based Decoding;
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
}
|
||||
|
||||
124
src/nxdn96.c
124
src/nxdn96.c
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,10 +24,8 @@
|
||||
|
||||
|
||||
/* Include ------------------------------------------------------------------*/
|
||||
//#include "nxdnconvolution.h"
|
||||
|
||||
#include <stdio.h>
|
||||
//#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
127
src/nxdn_data.c
127
src/nxdn_data.c
@@ -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
516
src/nxdn_deperm.c
Normal 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
523
src/nxdn_element.c
Normal 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
260
src/nxdn_frame.c
Normal 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
|
||||
}
|
||||
1692
src/nxdn_lib.c
1692
src/nxdn_lib.c
File diff suppressed because it is too large
Load Diff
220
src/nxdn_voice.c
220
src/nxdn_voice.c
@@ -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() */
|
||||
}
|
||||
Reference in New Issue
Block a user