mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
P25 audio work in progress - started work on exposing 88-bit (11-byte) IMBE voice frames from LDU messages. Added deinterleave, derandomize and the Golay(23,12,7) error detection and correction to LDU voice messages. Hamming edac still remains to be implemented. Plan to create a separate project to produce an imbe FormatConversionProvider that will convert 88-bit imbe frames to PCM audio that is then in a standard format for audio playback, recording, or conversion to another format like shoutcast.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
******************************************************************************/
|
||||
package bits;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -359,7 +360,39 @@ public class BinaryMessage extends BitSet
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the integer value represented by the bit array
|
||||
* Returns the byte value represented by the bit array
|
||||
* @param bits - an array of bit positions that will be treated as if they
|
||||
* were contiguous bits, with index 0 being the MSB and index
|
||||
* length - 1 being the LSB
|
||||
* @return - byte value of the bit array
|
||||
*/
|
||||
public byte getByte( int[] bits )
|
||||
{
|
||||
if( bits.length != 8 )
|
||||
{
|
||||
throw new IllegalArgumentException( "Invalid - there must be 8"
|
||||
+ "indexes to form a proper byte" );
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
|
||||
for( int index: bits )
|
||||
{
|
||||
value = Integer.rotateLeft( value, 1 );
|
||||
|
||||
if( get( index ) )
|
||||
{
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
mLog.debug( "Value is:" + value );
|
||||
|
||||
return (byte)( value & 0xFF );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long value represented by the bit array
|
||||
* @param bits - an array of bit positions that will be treated as if they
|
||||
* were contiguous bits, with index 0 being the MSB and index
|
||||
* length - 1 being the LSB
|
||||
|
||||
@@ -322,10 +322,10 @@ public class P25Decoder extends Decoder
|
||||
mC4FMSlicer.removeListener( slicerTap );
|
||||
}
|
||||
|
||||
if( mCQPSKSlicer != null )
|
||||
{
|
||||
mCQPSKSlicer.removeListener( slicerTap );
|
||||
}
|
||||
// if( mCQPSKSlicer != null )
|
||||
// {
|
||||
// mCQPSKSlicer.removeListener( slicerTap );
|
||||
// }
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -348,6 +348,11 @@ public class P25Decoder extends Decoder
|
||||
{
|
||||
mSymbolFilter.addListener( listener );
|
||||
}
|
||||
|
||||
if( mCQPSKDemodulator != null )
|
||||
{
|
||||
mCQPSKDemodulator.addListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,7 @@ public class P25Interleave
|
||||
111,124,125,126,127,140,141,142,143,156,157,158,159,172,173,174,175,188,
|
||||
189,190,191 };
|
||||
|
||||
private static int[] VOICE_DEINTERLEAVE = new int[] { 0,24,48,72,96,120,25,
|
||||
private static int[] VOICE_INTERLEAVE = new int[] { 0,24,48,72,96,120,25,
|
||||
1,73,49,121,97,2,26,50,74,98,122,27,3,75,51,123,99,4,28,52,76,100,124,
|
||||
29,5,77,53,125,101,6,30,54,78,102,126,31,7,79,55,127,103,8,32,56,80,104,
|
||||
128,33,9,81,57,129,105,10,34,58,82,106,130,35,11,83,59,131,107,12,36,60,
|
||||
@@ -48,13 +48,13 @@ public class P25Interleave
|
||||
67,139,115,20,44,68,92,116,140,45,21,93,69,141,117,22,46,70,94,119,142,
|
||||
47,23,95,71,143,118 };
|
||||
|
||||
private static int[] VOICE_INTERLEAVE = new int[] { 0,7,12,19,24,31,36,43,
|
||||
private static int[] VOICE_DEINTERLEAVE = new int[] { 0,7,12,19,24,31,36,43,
|
||||
48,55,60,67,72,79,84,91,96,103,108,115,120,127,132,139,1,6,13,18,25,30,
|
||||
37,42,49,54,61,66,73,78,85,90,97,102,109,114,121,126,133,138,2,9,14,21,
|
||||
26,33,38,45,50,57,62,69,74,81,86,93,98,105,110,117,122,129,134,141,3,8,
|
||||
15,20,27,32,39,44,51,56,63,68,75,80,87,92,99,104,111,116,123,128,135,
|
||||
140,4,11,16,23,28,35,40,47,52,59,64,71,76,83,88,95,100,107,112,119,124,
|
||||
131,143,136,5,10,17,22,29,34,41,46,53,58,65,70,77,82,89,94,101,106,113,
|
||||
131,136,143,5,10,17,22,29,34,41,46,53,58,65,70,77,82,89,94,101,106,113,
|
||||
118,125,130,137,142 };
|
||||
/**
|
||||
* Deinterleaves the 196-bit block in message, identified by start and end
|
||||
@@ -140,31 +140,22 @@ public class P25Interleave
|
||||
|
||||
public static void main( String[] args )
|
||||
{
|
||||
Trellis_1_2_Rate halfrate = new Trellis_1_2_Rate();
|
||||
|
||||
int start = 0;
|
||||
int end = 196;
|
||||
String interleaved = "0010010100000000000000000000000000000000000010100011001011111010101010101010101010101010010111101010100011010000000000000000000000000000110001011010011010101010101010101010101010101010100101100101";
|
||||
|
||||
BinaryMessage b = new BinaryMessage( end );
|
||||
int end = 144;
|
||||
int set = 50;
|
||||
|
||||
for( int x = 0; x < end; x++ )
|
||||
{
|
||||
if( interleaved.substring( x, x + 1 ).contentEquals( "1" ) )
|
||||
{
|
||||
b.set( x );
|
||||
}
|
||||
}
|
||||
String interleaved = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
BinaryMessage b = BinaryMessage.load( interleaved );
|
||||
|
||||
mLog.debug( "Interleaved:" + interleaved );
|
||||
mLog.debug( " Buffer:" + b.toString() );
|
||||
b.set( set );
|
||||
|
||||
mLog.debug( "INT:" + b.toString() );
|
||||
|
||||
P25Interleave.deinterleaveData( b, start, end );
|
||||
|
||||
mLog.debug( "Deinterleav:" + b.toString() );
|
||||
P25Interleave.deinterleaveVoice( b, start, end );
|
||||
|
||||
mLog.debug( "DEI:" + b.toString() );
|
||||
|
||||
// halfrate.decode( b, start, end );
|
||||
//
|
||||
// mLog.debug( "Trellis Off:" + b.toString() );
|
||||
mLog.debug( "In: " + set + " Out:" + b.nextSetBit( 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ public class LDU1Message extends LDUMessage
|
||||
mCRC = new CRC[ 2 ];
|
||||
mCRC[ 0 ] = CRC.PASSED;
|
||||
|
||||
//TODO: edac on link control octets
|
||||
|
||||
checkCRC();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,18 +6,145 @@ import decode.p25.P25Interleave;
|
||||
import decode.p25.message.P25Message;
|
||||
import decode.p25.reference.DataUnitID;
|
||||
import edac.CRC;
|
||||
import edac.Galois24;
|
||||
|
||||
public class LDUMessage extends P25Message
|
||||
{
|
||||
public static final int IMBE_1 = 64;
|
||||
public static final int IMBE_2 = 208;
|
||||
public static final int IMBE_3 = 392;
|
||||
public static final int IMBE_4 = 576;
|
||||
public static final int IMBE_5 = 760;
|
||||
public static final int IMBE_6 = 944;
|
||||
public static final int IMBE_7 = 1128;
|
||||
public static final int IMBE_8 = 1312;
|
||||
public static final int IMBE_9 = 1488;
|
||||
public static final int IMBE_FRAME_1 = 64;
|
||||
public static final int IMBE_FRAME_2 = 208;
|
||||
public static final int IMBE_FRAME_3 = 392;
|
||||
public static final int IMBE_FRAME_4 = 576;
|
||||
public static final int IMBE_FRAME_5 = 760;
|
||||
public static final int IMBE_FRAME_6 = 944;
|
||||
public static final int IMBE_FRAME_7 = 1128;
|
||||
public static final int IMBE_FRAME_8 = 1312;
|
||||
public static final int IMBE_FRAME_9 = 1488;
|
||||
|
||||
public static final int[] PN_SEED_1 = { 64,65,66,67,68,69,70,71,72,73,74,75 };
|
||||
public static final int[] PN_SEED_2 = { 208,209,210,211,212,213,214,215,216,
|
||||
217,218,219 };
|
||||
public static final int[] PN_SEED_3 = { 392,393,394,395,396,397,398,399,400,
|
||||
401,402,403 };
|
||||
public static final int[] PN_SEED_4 = { 576,577,578,579,580,581,582,583,584,
|
||||
585,586,587 };
|
||||
public static final int[] PN_SEED_5 = { 760,761,762,763,764,765,766,767,768,
|
||||
769,770,771 };
|
||||
public static final int[] PN_SEED_6 = { 944,945,946,947,948,949,950,951,952,
|
||||
953,954,955 };
|
||||
public static final int[] PN_SEED_7 = { 1128,1129,1130,1131,1132,1133,1134,
|
||||
1135,1136,1137,1138,1139 };
|
||||
public static final int[] PN_SEED_8 = { 1312,1313,1314,1315,1316,1317,1318,
|
||||
1319,1320,1321,1322,1323 };
|
||||
public static final int[] PN_SEED_9 = { 1488,1489,1490,1491,1492,1493,1494,
|
||||
1495,1496,1497,1498,1499 };
|
||||
|
||||
public static final int[] FRAME1_1= { 64,65,66,67,68,69,70,71 };
|
||||
public static final int[] FRAME1_2= { 72,73,74,75,87,88,89,90 };
|
||||
public static final int[] FRAME1_3= { 91,92,93,94,95,96,97,98 };
|
||||
public static final int[] FRAME1_4= { 110,111,112,113,114,115,116,117 };
|
||||
public static final int[] FRAME1_5= { 118,119,120,121,133,134,135,136 };
|
||||
public static final int[] FRAME1_6= { 137,138,139,140,141,142,143,144 };
|
||||
public static final int[] FRAME1_7= { 156,157,158,159,160,161,162,163 };
|
||||
public static final int[] FRAME1_8= { 164,165,166,171,172,173,174,175 };
|
||||
public static final int[] FRAME1_9= { 176,177,178,179,180,181,186,187 };
|
||||
public static final int[] FRAME1_10= { 188,189,190,191,192,193,194,195 };
|
||||
public static final int[] FRAME1_11= { 196,201,202,203,204,205,206,207 };
|
||||
|
||||
public static final int[] FRAME2_1= { 208,209,210,211,212,213,214,215 };
|
||||
public static final int[] FRAME2_2= { 216,217,218,219,231,232,233,234 };
|
||||
public static final int[] FRAME2_3= { 235,236,237,238,239,240,241,242 };
|
||||
public static final int[] FRAME2_4= { 254,255,256,257,258,259,260,261 };
|
||||
public static final int[] FRAME2_5= { 262,263,264,265,277,278,279,280 };
|
||||
public static final int[] FRAME2_6= { 281,282,283,284,285,286,287,288 };
|
||||
public static final int[] FRAME2_7= { 300,301,302,303,304,305,306,307 };
|
||||
public static final int[] FRAME2_8= { 308,309,310,315,316,317,318,319 };
|
||||
public static final int[] FRAME2_9= { 320,321,322,323,324,325,330,331 };
|
||||
public static final int[] FRAME2_10= { 332,333,334,335,336,337,338,339 };
|
||||
public static final int[] FRAME2_11= { 340,345,346,347,348,349,350,351 };
|
||||
|
||||
public static final int[] FRAME3_1= { 392,393,394,395,396,397,398,399 };
|
||||
public static final int[] FRAME3_2= { 400,401,402,403,415,416,417,418 };
|
||||
public static final int[] FRAME3_3= { 419,420,421,422,423,424,425,426 };
|
||||
public static final int[] FRAME3_4= { 438,439,440,441,442,443,444,445 };
|
||||
public static final int[] FRAME3_5= { 446,447,448,449,461,462,463,464 };
|
||||
public static final int[] FRAME3_6= { 465,466,467,468,469,470,471,472 };
|
||||
public static final int[] FRAME3_7= { 484,485,486,487,488,489,490,491 };
|
||||
public static final int[] FRAME3_8= { 492,493,494,499,500,501,502,503 };
|
||||
public static final int[] FRAME3_9= { 504,505,506,507,508,509,514,515 };
|
||||
public static final int[] FRAME3_10= { 516,517,518,519,520,521,522,523 };
|
||||
public static final int[] FRAME3_11= { 524,529,530,531,532,533,534,535 };
|
||||
|
||||
public static final int[] FRAME4_1= { 576,577,578,579,580,581,582,583 };
|
||||
public static final int[] FRAME4_2= { 584,585,586,587,599,600,601,602 };
|
||||
public static final int[] FRAME4_3= { 603,604,605,606,607,608,609,610 };
|
||||
public static final int[] FRAME4_4= { 622,623,624,625,626,627,628,629 };
|
||||
public static final int[] FRAME4_5= { 630,631,632,633,645,646,647,648 };
|
||||
public static final int[] FRAME4_6= { 649,650,651,652,653,654,655,656 };
|
||||
public static final int[] FRAME4_7= { 668,669,670,671,672,673,674,675 };
|
||||
public static final int[] FRAME4_8= { 676,677,678,683,684,685,686,687 };
|
||||
public static final int[] FRAME4_9= { 688,689,690,691,692,693,698,699 };
|
||||
public static final int[] FRAME4_10= { 700,701,702,703,704,705,706,707 };
|
||||
public static final int[] FRAME4_11= { 708,713,714,715,716,717,718,719 };
|
||||
|
||||
public static final int[] FRAME5_1= { 760,761,762,763,764,765,766,767 };
|
||||
public static final int[] FRAME5_2= { 768,769,770,771,783,784,785,786 };
|
||||
public static final int[] FRAME5_3= { 787,788,789,790,791,792,793,794 };
|
||||
public static final int[] FRAME5_4= { 806,807,808,809,810,811,812,813 };
|
||||
public static final int[] FRAME5_5= { 814,815,816,817,829,830,831,832 };
|
||||
public static final int[] FRAME5_6= { 833,834,835,836,837,838,839,840 };
|
||||
public static final int[] FRAME5_7= { 852,853,854,855,856,857,858,859 };
|
||||
public static final int[] FRAME5_8= { 860,861,862,867,868,869,870,871 };
|
||||
public static final int[] FRAME5_9= { 872,873,874,875,876,877,882,883 };
|
||||
public static final int[] FRAME5_10= { 884,885,886,887,888,889,890,891 };
|
||||
public static final int[] FRAME5_11= { 892,897,898,899,900,901,902,903 };
|
||||
|
||||
public static final int[] FRAME6_1= { 944,945,946,947,948,949,950,951 };
|
||||
public static final int[] FRAME6_2= { 952,953,954,955,967,968,969,970 };
|
||||
public static final int[] FRAME6_3= { 971,972,973,974,975,976,977,978 };
|
||||
public static final int[] FRAME6_4= { 990,991,992,993,994,995,996,997 };
|
||||
public static final int[] FRAME6_5= { 998,999,1000,1001,1013,1014,1015,1016 };
|
||||
public static final int[] FRAME6_6= { 1017,1018,1019,1020,1021,1022,1023,1024 };
|
||||
public static final int[] FRAME6_7= { 1036,1037,1038,1039,1040,1041,1042,1043 };
|
||||
public static final int[] FRAME6_8= { 1044,1045,1046,1051,1052,1053,1054,1055 };
|
||||
public static final int[] FRAME6_9= { 1056,1057,1058,1059,1060,1061,1066,1067 };
|
||||
public static final int[] FRAME6_10= { 1068,1069,1070,1071,1072,1073,1074,1075 };
|
||||
public static final int[] FRAME6_11= { 1076,1081,1082,1083,1084,1085,1086,1087 };
|
||||
|
||||
public static final int[] FRAME7_1= { 1128,1129,1130,1131,1132,1133,1134,1135 };
|
||||
public static final int[] FRAME7_2= { 1136,1137,1138,1139,1151,1152,1153,1154 };
|
||||
public static final int[] FRAME7_3= { 1155,1156,1157,1158,1159,1160,1161,1162 };
|
||||
public static final int[] FRAME7_4= { 1174,1175,1176,1177,1178,1179,1180,1181 };
|
||||
public static final int[] FRAME7_5= { 1182,1183,1184,1185,1197,1198,1199,1200 };
|
||||
public static final int[] FRAME7_6= { 1201,1202,1203,1204,1205,1206,1207,1208 };
|
||||
public static final int[] FRAME7_7= { 1220,1221,1222,1223,1224,1225,1226,1227 };
|
||||
public static final int[] FRAME7_8= { 1228,1229,1230,1235,1236,1237,1238,1239 };
|
||||
public static final int[] FRAME7_9= { 1240,1241,1242,1243,1244,1245,1250,1251 };
|
||||
public static final int[] FRAME7_10= { 1252,1253,1254,1255,1256,1257,1258,1259 };
|
||||
public static final int[] FRAME7_11= { 1260,1265,1266,1267,1268,1269,1270,1271 };
|
||||
|
||||
public static final int[] FRAME8_1= { 1312,1313,1314,1315,1316,1317,1318,1319 };
|
||||
public static final int[] FRAME8_2= { 1320,1321,1322,1323,1335,1336,1337,1338 };
|
||||
public static final int[] FRAME8_3= { 1339,1340,1341,1342,1343,1344,1345,1346 };
|
||||
public static final int[] FRAME8_4= { 1358,1359,1360,1361,1362,1363,1364,1365 };
|
||||
public static final int[] FRAME8_5= { 1366,1367,1368,1369,1381,1382,1383,1384 };
|
||||
public static final int[] FRAME8_6= { 1385,1386,1387,1388,1389,1390,1391,1392 };
|
||||
public static final int[] FRAME8_7= { 1404,1405,1406,1407,1408,1409,1410,1411 };
|
||||
public static final int[] FRAME8_8= { 1412,1413,1414,1419,1420,1421,1422,1423 };
|
||||
public static final int[] FRAME8_9= { 1424,1425,1426,1427,1428,1429,1434,1435 };
|
||||
public static final int[] FRAME8_10= { 1436,1437,1438,1439,1440,1441,1442,1443 };
|
||||
public static final int[] FRAME8_11= { 1444,1449,1450,1451,1452,1453,1454,1455 };
|
||||
|
||||
public static final int[] FRAME9_1= { 1488,1489,1490,1491,1492,1493,1494,1495 };
|
||||
public static final int[] FRAME9_2= { 1496,1497,1498,1499,1511,1512,1513,1514 };
|
||||
public static final int[] FRAME9_3= { 1515,1516,1517,1518,1519,1520,1521,1522 };
|
||||
public static final int[] FRAME9_4= { 1534,1535,1536,1537,1538,1539,1540,1541 };
|
||||
public static final int[] FRAME9_5= { 1542,1543,1544,1545,1557,1558,1559,1560 };
|
||||
public static final int[] FRAME9_6= { 1561,1562,1563,1564,1565,1566,1567,1568 };
|
||||
public static final int[] FRAME9_7= { 1580,1581,1582,1583,1584,1585,1586,1587 };
|
||||
public static final int[] FRAME9_8= { 1588,1589,1590,1595,1596,1597,1598,1599 };
|
||||
public static final int[] FRAME9_9= { 1600,1601,1602,1603,1604,1605,1610,1611 };
|
||||
public static final int[] FRAME9_10= { 1612,1613,1614,1615,1616,1617,1618,1619 };
|
||||
public static final int[] FRAME9_11= { 1620,1625,1626,1627,1628,1629,1630,1631 };
|
||||
|
||||
public static final int[] LOW_SPEED_DATA = { 1456,1457,1458,1459,1460,1461,
|
||||
1462,1463,1472,1473,1474,1475,1476,1477,1478,1479 };
|
||||
@@ -27,10 +154,21 @@ public class LDUMessage extends P25Message
|
||||
{
|
||||
super( message, duid, aliasList );
|
||||
|
||||
deinterleaveVoiceFrames();
|
||||
deinterleave();
|
||||
|
||||
/* Perform error detection & correction on coset zero of each frame */
|
||||
errorCorrectCosetZero();
|
||||
|
||||
/* Remove randomizer from coset words c1 - c6 */
|
||||
derandomize();
|
||||
|
||||
/* Perform edac against remaining info vectors */
|
||||
errorCorrectInformationVectors();
|
||||
|
||||
/* NID CRC is checked in the message framer, thus a constructed message
|
||||
* means it passed the CRC */
|
||||
* means it passed the CRC */
|
||||
|
||||
//TODO: update crc status for voice frames
|
||||
mCRC = new CRC[ 2 ];
|
||||
mCRC[ 0 ] = CRC.PASSED;
|
||||
}
|
||||
@@ -61,22 +199,240 @@ public class LDUMessage extends P25Message
|
||||
return mMessage.getHex( LOW_SPEED_DATA, 4 );
|
||||
}
|
||||
|
||||
private void deinterleaveVoiceFrames()
|
||||
/**
|
||||
* Deinterleaves 144 message bits of each of the 9 imbe voice frames
|
||||
*/
|
||||
private void deinterleave()
|
||||
{
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_1, IMBE_1 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_2, IMBE_2 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_3, IMBE_3 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_4, IMBE_4 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_5, IMBE_5 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_6, IMBE_6 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_7, IMBE_7 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_8, IMBE_8 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_9, IMBE_9 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_1, IMBE_FRAME_1 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_2, IMBE_FRAME_2 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_3, IMBE_FRAME_3 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_4, IMBE_FRAME_4 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_5, IMBE_FRAME_5 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_6, IMBE_FRAME_6 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_7, IMBE_FRAME_7 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_8, IMBE_FRAME_8 + 144 );
|
||||
P25Interleave.deinterleaveVoice( mMessage, IMBE_FRAME_9, IMBE_FRAME_9 + 144 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs error detection and correction on coset work zero of each of the
|
||||
* 9 imbe voice frames.
|
||||
*/
|
||||
private void errorCorrectCosetZero()
|
||||
{
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_1 );
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_2 );
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_3 );
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_4 );
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_5 );
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_6 );
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_7 );
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_8 );
|
||||
Galois24.checkAndCorrect( mMessage, IMBE_FRAME_9 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a pseudo-random noise randomizer mask and applies (xor) the
|
||||
* mask against coset words c1 through c6 of each of the 9 imbe voice frames
|
||||
*/
|
||||
private void derandomize()
|
||||
{
|
||||
derandomize( PN_SEED_1 );
|
||||
derandomize( PN_SEED_2 );
|
||||
derandomize( PN_SEED_3 );
|
||||
derandomize( PN_SEED_4 );
|
||||
derandomize( PN_SEED_5 );
|
||||
derandomize( PN_SEED_6 );
|
||||
derandomize( PN_SEED_7 );
|
||||
derandomize( PN_SEED_8 );
|
||||
derandomize( PN_SEED_9 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform error detection and correction against information vectors 1 - 6
|
||||
*/
|
||||
private void errorCorrectInformationVectors()
|
||||
{
|
||||
errorCorrectInformationVectors( IMBE_FRAME_1 );
|
||||
errorCorrectInformationVectors( IMBE_FRAME_2 );
|
||||
errorCorrectInformationVectors( IMBE_FRAME_3 );
|
||||
errorCorrectInformationVectors( IMBE_FRAME_4 );
|
||||
errorCorrectInformationVectors( IMBE_FRAME_5 );
|
||||
errorCorrectInformationVectors( IMBE_FRAME_6 );
|
||||
errorCorrectInformationVectors( IMBE_FRAME_7 );
|
||||
errorCorrectInformationVectors( IMBE_FRAME_8 );
|
||||
errorCorrectInformationVectors( IMBE_FRAME_9 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs Golay(23,12,7) error detection and correction against information
|
||||
* vectors 1 - 3 and Hamming(15,11,3) against vectors 4 - 6.
|
||||
*
|
||||
* @param frameStart - bit position of the imbe frame start
|
||||
*/
|
||||
private void errorCorrectInformationVectors( int frameStart )
|
||||
{
|
||||
Galois24.checkAndCorrect( mMessage, frameStart + 23 );
|
||||
Galois24.checkAndCorrect( mMessage, frameStart + 46 );
|
||||
Galois24.checkAndCorrect( mMessage, frameStart + 69 );
|
||||
|
||||
//TODO: hamming edac for iv 4,5,6 ...
|
||||
}
|
||||
|
||||
|
||||
/* Temporary override */
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes randomizer by generating a pseudo-random noise sequence from the
|
||||
* first 12 bits of coset word c0 and applies (xor) that sequence against
|
||||
* message coset words c1 through c6.
|
||||
*
|
||||
* @param seedBits - first 12 bit indexes of coset word c0
|
||||
*/
|
||||
public void derandomize( int[] seedBits )
|
||||
{
|
||||
/* Set the offset to the first seed bit plus 23 to point to coset c1 */
|
||||
int offset = seedBits[ 0 ] + 23;
|
||||
|
||||
/* Get seed value from first 12 bits of coset c0 */
|
||||
int seed = mMessage.getInt( seedBits );
|
||||
|
||||
/* Left shift 4 places (multiply by 16) */
|
||||
seed <<= 4;
|
||||
|
||||
for( int x = 0; x < 114; x++ )
|
||||
{
|
||||
seed = ( ( 173 * seed ) + 13849 ) & 65536;
|
||||
|
||||
/* If seed bit 15 is set, toggle the corresponding message bit */
|
||||
if( ( seed & 32768 ) == 32768 )
|
||||
{
|
||||
mMessage.flip( x + offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts 9 IMBE voice frames of 11-bytes (88-bit) each into a 99 byte array
|
||||
*/
|
||||
public byte[] getIMBEFrameData()
|
||||
{
|
||||
byte[] data = new byte[ 99 ];
|
||||
|
||||
data[ 0 ] = mMessage.getByte( FRAME1_1 );
|
||||
data[ 1 ] = mMessage.getByte( FRAME1_2 );
|
||||
data[ 2 ] = mMessage.getByte( FRAME1_3 );
|
||||
data[ 3 ] = mMessage.getByte( FRAME1_4 );
|
||||
data[ 4 ] = mMessage.getByte( FRAME1_5 );
|
||||
data[ 5 ] = mMessage.getByte( FRAME1_6 );
|
||||
data[ 6 ] = mMessage.getByte( FRAME1_7 );
|
||||
data[ 7 ] = mMessage.getByte( FRAME1_8 );
|
||||
data[ 8 ] = mMessage.getByte( FRAME1_9 );
|
||||
data[ 9 ] = mMessage.getByte( FRAME1_10 );
|
||||
data[ 10 ] = mMessage.getByte( FRAME1_11 );
|
||||
|
||||
data[ 11 ] = mMessage.getByte( FRAME2_1 );
|
||||
data[ 12 ] = mMessage.getByte( FRAME2_2 );
|
||||
data[ 13 ] = mMessage.getByte( FRAME2_3 );
|
||||
data[ 14 ] = mMessage.getByte( FRAME2_4 );
|
||||
data[ 15 ] = mMessage.getByte( FRAME2_5 );
|
||||
data[ 16 ] = mMessage.getByte( FRAME2_6 );
|
||||
data[ 17 ] = mMessage.getByte( FRAME2_7 );
|
||||
data[ 18 ] = mMessage.getByte( FRAME2_8 );
|
||||
data[ 19 ] = mMessage.getByte( FRAME2_9 );
|
||||
data[ 20 ] = mMessage.getByte( FRAME2_10 );
|
||||
data[ 21 ] = mMessage.getByte( FRAME2_11 );
|
||||
|
||||
data[ 22 ] = mMessage.getByte( FRAME3_1 );
|
||||
data[ 23 ] = mMessage.getByte( FRAME3_2 );
|
||||
data[ 24 ] = mMessage.getByte( FRAME3_3 );
|
||||
data[ 25 ] = mMessage.getByte( FRAME3_4 );
|
||||
data[ 26 ] = mMessage.getByte( FRAME3_5 );
|
||||
data[ 27 ] = mMessage.getByte( FRAME3_6 );
|
||||
data[ 28 ] = mMessage.getByte( FRAME3_7 );
|
||||
data[ 29 ] = mMessage.getByte( FRAME3_8 );
|
||||
data[ 30 ] = mMessage.getByte( FRAME3_9 );
|
||||
data[ 31 ] = mMessage.getByte( FRAME3_10 );
|
||||
data[ 32 ] = mMessage.getByte( FRAME3_11 );
|
||||
|
||||
data[ 33 ] = mMessage.getByte( FRAME4_1 );
|
||||
data[ 34 ] = mMessage.getByte( FRAME4_2 );
|
||||
data[ 35 ] = mMessage.getByte( FRAME4_3 );
|
||||
data[ 36 ] = mMessage.getByte( FRAME4_4 );
|
||||
data[ 37 ] = mMessage.getByte( FRAME4_5 );
|
||||
data[ 38 ] = mMessage.getByte( FRAME4_6 );
|
||||
data[ 39 ] = mMessage.getByte( FRAME4_7 );
|
||||
data[ 40 ] = mMessage.getByte( FRAME4_8 );
|
||||
data[ 41 ] = mMessage.getByte( FRAME4_9 );
|
||||
data[ 42 ] = mMessage.getByte( FRAME4_10 );
|
||||
data[ 43 ] = mMessage.getByte( FRAME4_11 );
|
||||
|
||||
data[ 44 ] = mMessage.getByte( FRAME5_1 );
|
||||
data[ 45 ] = mMessage.getByte( FRAME5_2 );
|
||||
data[ 46 ] = mMessage.getByte( FRAME5_3 );
|
||||
data[ 47 ] = mMessage.getByte( FRAME5_4 );
|
||||
data[ 48 ] = mMessage.getByte( FRAME5_5 );
|
||||
data[ 49 ] = mMessage.getByte( FRAME5_6 );
|
||||
data[ 50 ] = mMessage.getByte( FRAME5_7 );
|
||||
data[ 51 ] = mMessage.getByte( FRAME5_8 );
|
||||
data[ 52 ] = mMessage.getByte( FRAME5_9 );
|
||||
data[ 53 ] = mMessage.getByte( FRAME5_10 );
|
||||
data[ 54 ] = mMessage.getByte( FRAME5_11 );
|
||||
|
||||
data[ 55 ] = mMessage.getByte( FRAME6_1 );
|
||||
data[ 56 ] = mMessage.getByte( FRAME6_2 );
|
||||
data[ 57 ] = mMessage.getByte( FRAME6_3 );
|
||||
data[ 58 ] = mMessage.getByte( FRAME6_4 );
|
||||
data[ 59 ] = mMessage.getByte( FRAME6_5 );
|
||||
data[ 60 ] = mMessage.getByte( FRAME6_6 );
|
||||
data[ 61 ] = mMessage.getByte( FRAME6_7 );
|
||||
data[ 62 ] = mMessage.getByte( FRAME6_8 );
|
||||
data[ 63 ] = mMessage.getByte( FRAME6_9 );
|
||||
data[ 64 ] = mMessage.getByte( FRAME6_10 );
|
||||
data[ 65 ] = mMessage.getByte( FRAME6_11 );
|
||||
|
||||
data[ 66 ] = mMessage.getByte( FRAME7_1 );
|
||||
data[ 67 ] = mMessage.getByte( FRAME7_2 );
|
||||
data[ 68 ] = mMessage.getByte( FRAME7_3 );
|
||||
data[ 69 ] = mMessage.getByte( FRAME7_4 );
|
||||
data[ 70 ] = mMessage.getByte( FRAME7_5 );
|
||||
data[ 71 ] = mMessage.getByte( FRAME7_6 );
|
||||
data[ 72 ] = mMessage.getByte( FRAME7_7 );
|
||||
data[ 73 ] = mMessage.getByte( FRAME7_8 );
|
||||
data[ 74 ] = mMessage.getByte( FRAME7_9 );
|
||||
data[ 75 ] = mMessage.getByte( FRAME7_10 );
|
||||
data[ 76 ] = mMessage.getByte( FRAME7_11 );
|
||||
|
||||
data[ 77 ] = mMessage.getByte( FRAME8_1 );
|
||||
data[ 78 ] = mMessage.getByte( FRAME8_2 );
|
||||
data[ 79 ] = mMessage.getByte( FRAME8_3 );
|
||||
data[ 80 ] = mMessage.getByte( FRAME8_4 );
|
||||
data[ 81 ] = mMessage.getByte( FRAME8_5 );
|
||||
data[ 82 ] = mMessage.getByte( FRAME8_6 );
|
||||
data[ 83 ] = mMessage.getByte( FRAME8_7 );
|
||||
data[ 84 ] = mMessage.getByte( FRAME8_8 );
|
||||
data[ 85 ] = mMessage.getByte( FRAME8_9 );
|
||||
data[ 86 ] = mMessage.getByte( FRAME8_10 );
|
||||
data[ 87 ] = mMessage.getByte( FRAME8_11 );
|
||||
|
||||
data[ 88 ] = mMessage.getByte( FRAME9_1 );
|
||||
data[ 89 ] = mMessage.getByte( FRAME9_2 );
|
||||
data[ 90 ] = mMessage.getByte( FRAME9_3 );
|
||||
data[ 91 ] = mMessage.getByte( FRAME9_4 );
|
||||
data[ 92 ] = mMessage.getByte( FRAME9_5 );
|
||||
data[ 93 ] = mMessage.getByte( FRAME9_6 );
|
||||
data[ 94 ] = mMessage.getByte( FRAME9_7 );
|
||||
data[ 95 ] = mMessage.getByte( FRAME9_8 );
|
||||
data[ 96 ] = mMessage.getByte( FRAME9_9 );
|
||||
data[ 97 ] = mMessage.getByte( FRAME9_10 );
|
||||
data[ 98 ] = mMessage.getByte( FRAME9_11 );
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public enum DataUnitID
|
||||
NID ( -1, 64, false, "NID ", "Network and Data Unit ID" ),
|
||||
HDU ( 0, 712, false, "HDU ", "Header Data Unit" ),
|
||||
TDU ( 3, 64, false, "TDU ", "Simple Terminator Data Unit" ),
|
||||
LDU1 ( 5, 1630, true, "LDU1 ", "Logical Link Data Unit 1" ),
|
||||
LDU1 ( 5, 1632, true, "LDU1 ", "Logical Link Data Unit 1" ),
|
||||
TSBK1( 7, 260, false, "TSBK1", "Trunking Signaling Block" ),
|
||||
TSBK2( 7, 260, false, "TSBK2", "Trunking Signaling Block" ),
|
||||
TSBK3( 7, 260, false, "TSBK3", "Trunking Signaling Block" ),
|
||||
|
||||
@@ -71,8 +71,6 @@ public class DirectGainControl implements GainController, RealSampleListener
|
||||
{
|
||||
mGain = mMaximum;
|
||||
}
|
||||
|
||||
mLog.debug( "Gain Increase: " + mGain );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,7 +82,6 @@ public class DirectGainControl implements GainController, RealSampleListener
|
||||
{
|
||||
mGain = mMinimum;
|
||||
}
|
||||
mLog.debug( "Gain Decrease: " + mGain );
|
||||
}
|
||||
|
||||
public void setListener( RealSampleListener listener )
|
||||
|
||||
@@ -36,6 +36,10 @@ import org.slf4j.LoggerFactory;
|
||||
import sample.Listener;
|
||||
import sample.Provider;
|
||||
import sample.complex.ComplexSample;
|
||||
import source.tuner.FrequencyChangeEvent;
|
||||
import source.tuner.FrequencyChangeEvent.Attribute;
|
||||
import source.tuner.FrequencyChangeListener;
|
||||
import buffer.FloatAveragingBuffer;
|
||||
import dsp.filter.interpolator.QPSKInterpolator;
|
||||
|
||||
/**
|
||||
@@ -51,11 +55,13 @@ public class CQPSKDemodulator implements Listener<ComplexSample>,
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( CQPSKDemodulator.class );
|
||||
|
||||
|
||||
private Listener<ComplexSample> mListener;
|
||||
|
||||
private GardnerDetector mGardnerDetector = new GardnerDetector();
|
||||
|
||||
private FrequencyChangeListener mFrequencyChangeListener;
|
||||
|
||||
public CQPSKDemodulator()
|
||||
{
|
||||
}
|
||||
@@ -82,6 +88,75 @@ public class CQPSKDemodulator implements Listener<ComplexSample>,
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void addListener( FrequencyChangeListener listener )
|
||||
{
|
||||
mFrequencyChangeListener = listener;
|
||||
}
|
||||
|
||||
public void removeListener( FrequencyChangeListener listener )
|
||||
{
|
||||
mFrequencyChangeListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the current loop frequency of the costas loop and broadcasts
|
||||
* tuner frequency offset adjustments 10 times a second as needed to keep
|
||||
* the frequency offset below half of the maximum frequency
|
||||
*/
|
||||
public class FrequencyControl
|
||||
{
|
||||
/* Set the trigger threshold at half of the maximum costas loop control
|
||||
* frequency */
|
||||
private static final float CORRECTION_THRESHOLD_FREQUENCY =
|
||||
( 2.0f * (float)Math.PI * 600.0f ) / 48000.0f;
|
||||
|
||||
private FloatAveragingBuffer mBuffer = new FloatAveragingBuffer( 40 );
|
||||
|
||||
private long mFrequencyError = 0;
|
||||
|
||||
private int mCounter = 0;
|
||||
|
||||
public void receive( float frequency )
|
||||
{
|
||||
// mCounter++;
|
||||
//
|
||||
// if( mCounter >= 480 )
|
||||
// {
|
||||
// float average = mBuffer.get( frequency );
|
||||
// mLog.debug( "Avg:" + average + " Threshold:" + CORRECTION_THRESHOLD_FREQUENCY);
|
||||
//
|
||||
// boolean correctionNeeded = false;
|
||||
//
|
||||
// if( average > CORRECTION_THRESHOLD_FREQUENCY )
|
||||
// {
|
||||
// mFrequencyError--;
|
||||
//
|
||||
// correctionNeeded = true;
|
||||
// }
|
||||
// else if( average < -CORRECTION_THRESHOLD_FREQUENCY )
|
||||
// {
|
||||
// mFrequencyError++;
|
||||
//
|
||||
// correctionNeeded = true;
|
||||
// }
|
||||
//
|
||||
// if( correctionNeeded && mFrequencyChangeListener != null )
|
||||
// {
|
||||
// mLog.debug( "Issuing Frequency Correction: " + mFrequencyError );
|
||||
// mFrequencyChangeListener.frequencyChanged(
|
||||
// new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR,
|
||||
// mFrequencyError ) );
|
||||
// }
|
||||
//
|
||||
// mCounter = 0;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// mBuffer.get( frequency );
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gardner symbol timing detector
|
||||
@@ -209,10 +284,15 @@ public class CQPSKDemodulator implements Listener<ComplexSample>,
|
||||
}
|
||||
|
||||
/**
|
||||
* Costas Loop - phase locked loop synchronized to the incoming signal.
|
||||
* Costas Loop - phase locked loop synchronized to the frequency offset of
|
||||
* the incoming signal to enable the signal to be mixed down to zero offset
|
||||
* for proper synchronization. The mFrequency value indicates the detected
|
||||
* frequency offset. We attempt to keep that value close to zero by issuing
|
||||
* frequency adjustments to the tuner channel source.
|
||||
*
|
||||
* Ported from gnuradio/control_loop and gnuradio/mpsk_receiver_cc
|
||||
* using initialization values from KA1RBI's OP25/cqpsk.py
|
||||
* Most of the costas loop code was ported from gnuradio/control_loop and
|
||||
* gnuradio/mpsk_receiver_cc using initialization values from KA1RBI's
|
||||
* OP25/cqpsk.py
|
||||
*/
|
||||
public class CostasLoop implements Listener<ComplexSample>
|
||||
{
|
||||
@@ -221,6 +301,9 @@ public class CQPSKDemodulator implements Listener<ComplexSample>,
|
||||
/* 45 degree rotation angle */
|
||||
public static final float THETA = (float)( Math.PI / 4.0d );
|
||||
|
||||
private static final float MAXIMUM_FREQUENCY = ( 2.0f * (float)Math.PI *
|
||||
1200.0f ) / 48000.0f;
|
||||
|
||||
/* http://www.trondeau.com/blog/2011/8/13/control-loop-gain-values.html */
|
||||
private float mDamping = (float)Math.sqrt( 2.0 ) / 2.0f;
|
||||
|
||||
@@ -238,9 +321,8 @@ public class CQPSKDemodulator implements Listener<ComplexSample>,
|
||||
private float mLoopPhase = 0.0f;
|
||||
|
||||
private float mLoopFrequency = 0.0f;
|
||||
|
||||
private float mMaximumFrequency = ( 2.0f * (float)Math.PI * 1200.0f ) /
|
||||
48000.0f;
|
||||
|
||||
private FrequencyControl mFrequencyControl = new FrequencyControl();
|
||||
|
||||
public CostasLoop()
|
||||
{
|
||||
@@ -289,12 +371,6 @@ public class CQPSKDemodulator implements Listener<ComplexSample>,
|
||||
|
||||
/* Adjust for phase error */
|
||||
adjust( phaseError );
|
||||
|
||||
/* Maintain phase between +/- 2 * PI */
|
||||
unwrapPhase();
|
||||
|
||||
/* Limit frequency to +/- maximum frequency */
|
||||
limitFrequency();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,18 +383,27 @@ public class CQPSKDemodulator implements Listener<ComplexSample>,
|
||||
{
|
||||
mLoopFrequency += mBetaGain * phase_error;
|
||||
mLoopPhase += mLoopFrequency + mAlphaGain * phase_error;
|
||||
|
||||
/* Maintain phase between +/- 2 * PI */
|
||||
unwrapPhase();
|
||||
|
||||
/* Limit frequency to +/- maximum frequency */
|
||||
limitFrequency();
|
||||
}
|
||||
|
||||
private void limitFrequency()
|
||||
{
|
||||
if( mLoopFrequency > mMaximumFrequency )
|
||||
/* Check for and issue tuner offset correction */
|
||||
mFrequencyControl.receive( mLoopFrequency );
|
||||
|
||||
if( mLoopFrequency > MAXIMUM_FREQUENCY )
|
||||
{
|
||||
mLoopFrequency = mMaximumFrequency;
|
||||
mLoopFrequency = MAXIMUM_FREQUENCY;
|
||||
}
|
||||
|
||||
if( mLoopFrequency < -mMaximumFrequency )
|
||||
if( mLoopFrequency < -MAXIMUM_FREQUENCY )
|
||||
{
|
||||
mLoopFrequency = -mMaximumFrequency;
|
||||
mLoopFrequency = -MAXIMUM_FREQUENCY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,21 +161,16 @@ public class Galois24
|
||||
|
||||
public static void main( String[] args )
|
||||
{
|
||||
/* NID + TDULC message */
|
||||
String msg = "0010011000001111100100110011000100111010000111010001000000000000000000000000000000000000000000000100101010010111000000000000000000000000000010010111101111111111000000000000000000000000000000000000000000000000000110001011100001111101010110111001001011010010101111010010000010110011101111111100100000010001111100011010010100001101111001111001100000111000";
|
||||
String msg = "01001111011100100011011";
|
||||
|
||||
BinaryMessage message = BinaryMessage.load( msg );
|
||||
|
||||
mLog.debug( "MSG: " + message.toString() );
|
||||
|
||||
int x = 64;
|
||||
int x = 0;
|
||||
|
||||
while( x < msg.length() )
|
||||
{
|
||||
message = checkAndCorrect( message, x );
|
||||
message = checkAndCorrect( message, x );
|
||||
|
||||
mLog.debug( "X:" + x + " Pass:" + ( message.getCRC()== CRC.PASSED ) );
|
||||
x += 24;
|
||||
}
|
||||
mLog.debug( "MSG: " + message.toString() + " Pass:" + ( message.getCRC()== CRC.PASSED ) );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user