public Decode ( byte DataBlockBuffer, string Time, int &NumOfMessagesDecoded ) : string[] | ||
DataBlockBuffer | byte | |
Time | string | |
NumOfMessagesDecoded | int | |
return | string[] |
public string[] Decode(byte[] DataBlockBuffer, string Time, out int NumOfMessagesDecoded)
{
// Define output data buffer
string[] DataOut = new string[2000];
// Determine the size of the datablock
int LengthOfDataBlockInBytes = DataBlockBuffer.Length;
// Index into the array of record strings
int DataOutIndex = 0;
// Reset buffer indexes
CurrentDataBufferOctalIndex = 0;
int DataBufferIndexForThisExtraction = 0;
// SIC/SAC Indexes
int SIC_Index = 0;
int SAC_Index = 0;
// Lenght of the current record's FSPECs
int FSPEC_Length = 0;
// The first four possible FSPEC octets
BitVector32 FourFSPECOctets = new BitVector32();
// The fifth FSPEC octet in the case RE or SP field is present
BitVector32 TheFifthFSPECOctet = new BitVector32();
while (DataBufferIndexForThisExtraction < LengthOfDataBlockInBytes)
{
// Assume that there will be no more than 200 bytes in one record
byte[] LocalSingleRecordBuffer = new byte[3000];
Array.Copy(DataBlockBuffer, DataBufferIndexForThisExtraction, LocalSingleRecordBuffer, 0, (LengthOfDataBlockInBytes - DataBufferIndexForThisExtraction));
// Get all four data words, but use only the number specifed
// by the length of FSPEC words
FourFSPECOctets = ASTERIX.GetFourFSPECOctets(LocalSingleRecordBuffer);
TheFifthFSPECOctet = ASTERIX.GetFifthFSPECOctet(LocalSingleRecordBuffer);
// Determine Length of FSPEC fields in bytes
FSPEC_Length = ASTERIX.DetermineLenghtOfFSPEC(LocalSingleRecordBuffer);
// Check wether 010 is present
if (FourFSPECOctets[Bit_Ops.Bit7] == true)
{
// Determine SIC/SAC Index
SIC_Index = FSPEC_Length;
SAC_Index = SIC_Index + 1;
// Extract SIC/SAC Indexes.
DataOut[DataOutIndex] = LocalSingleRecordBuffer[SIC_Index].ToString() + '/' + LocalSingleRecordBuffer[SAC_Index].ToString();
// Save of the current data buffer index so it can be used by
// Decoder
CurrentDataBufferOctalIndex = SAC_Index + 1;
}
else
{
// Extract SIC/SAC Indexes.
DataOut[DataOutIndex] = "---" + '/' + "---";
}
///////////////////////////////////////////////////////////////////////////
// Populate the current SIC/SAC and Time stamp for this meesage
//
I062DataItems[ItemIDToIndex("010")].value =
new ASTERIX.SIC_SAC_Time(LocalSingleRecordBuffer[SIC_Index], LocalSingleRecordBuffer[SAC_Index], ASTERIX.TimeOfReception);
// Loop for each FSPEC and determine what data item is present
for (int FSPEC_Index = 1; FSPEC_Index <= FSPEC_Length; FSPEC_Index++)
{
switch (FSPEC_Index)
{
case 1:
// 010 Data Source Identifier
if (FourFSPECOctets[Bit_Ops.Bit7] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 010:T";
I062DataItems[ItemIDToIndex("010")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("010")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 010:F";
I062DataItems[ItemIDToIndex("010")].CurrentlyPresent = false;
}
// Spare bit
if (FourFSPECOctets[Bit_Ops.Bit6] == true)
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " SPR:T";
else
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " SPR:F";
// 015 Service Identification
if (FourFSPECOctets[Bit_Ops.Bit5] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 015:T";
I062DataItems[ItemIDToIndex("015")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("015")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 015:F";
I062DataItems[ItemIDToIndex("015")].CurrentlyPresent = false;
}
// 070 Time Of Track Information
if (FourFSPECOctets[Bit_Ops.Bit4] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 070:T";
I062DataItems[ItemIDToIndex("070")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("070")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 070:F";
I062DataItems[ItemIDToIndex("070")].CurrentlyPresent = false;
}
// 105 Calculated Track Position (WGS-84)
if (FourFSPECOctets[Bit_Ops.Bit3] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 105:T";
I062DataItems[ItemIDToIndex("105")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("105")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 105:F";
I062DataItems[ItemIDToIndex("105")].CurrentlyPresent = false;
}
// 100 Calculated Track Position (Cartesian)
if (FourFSPECOctets[Bit_Ops.Bit2] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 100:T";
I062DataItems[ItemIDToIndex("100")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("100")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 100:F";
I062DataItems[ItemIDToIndex("100")].CurrentlyPresent = false;
}
// 185 Calculated Track Velocity (Cartesian)
if (FourFSPECOctets[Bit_Ops.Bit1] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 185:T";
I062DataItems[ItemIDToIndex("185")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("185")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 185:F";
I062DataItems[ItemIDToIndex("185")].CurrentlyPresent = false;
}
break;
case 2:
// 210 Calculated Acceleration (Cartesian)
if (FourFSPECOctets[Bit_Ops.Bit15] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 210:T";
I062DataItems[ItemIDToIndex("210")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("210")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 210:F";
I062DataItems[ItemIDToIndex("210")].CurrentlyPresent = false;
}
// 060 Track Mode 3/A Code
if (FourFSPECOctets[Bit_Ops.Bit14] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 060:T";
I062DataItems[ItemIDToIndex("060")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("060")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 060:F";
I062DataItems[ItemIDToIndex("060")].CurrentlyPresent = false;
}
// 245 Target Identification
if (FourFSPECOctets[Bit_Ops.Bit13] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 245:T";
I062DataItems[ItemIDToIndex("245")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("245")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 245:F";
I062DataItems[ItemIDToIndex("245")].CurrentlyPresent = false;
}
// 380 Aircraft Derived Data
if (FourFSPECOctets[Bit_Ops.Bit12] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 380:T";
I062DataItems[ItemIDToIndex("380")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("380")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 380:F";
I062DataItems[ItemIDToIndex("380")].CurrentlyPresent = false;
}
// 040 Track Number
if (FourFSPECOctets[Bit_Ops.Bit11] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 040:T";
I062DataItems[ItemIDToIndex("040")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("040")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 040:F";
I062DataItems[ItemIDToIndex("040")].CurrentlyPresent = false;
}
// 080 Track Status
if (FourFSPECOctets[Bit_Ops.Bit10] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 080:T";
I062DataItems[ItemIDToIndex("080")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("080")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 080:F";
I062DataItems[ItemIDToIndex("080")].CurrentlyPresent = false;
}
// 290 System Track Update Ages
if (FourFSPECOctets[Bit_Ops.Bit9] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 290:T";
I062DataItems[ItemIDToIndex("290")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("290")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 290:F";
I062DataItems[ItemIDToIndex("290")].CurrentlyPresent = false;
}
break;
case 3:
// 200 Mode of Movement
if (FourFSPECOctets[Bit_Ops.Bit23] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 200:T";
I062DataItems[ItemIDToIndex("200")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("200")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 200:F";
I062DataItems[ItemIDToIndex("200")].CurrentlyPresent = false;
}
// 295 Track Data Ages
if (FourFSPECOctets[Bit_Ops.Bit22] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 295:T";
I062DataItems[ItemIDToIndex("295")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("295")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 295:F";
I062DataItems[ItemIDToIndex("295")].CurrentlyPresent = false;
}
// 136 Measured Flight Level
if (FourFSPECOctets[Bit_Ops.Bit21] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 136:T";
I062DataItems[ItemIDToIndex("136")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("136")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 136:F";
I062DataItems[ItemIDToIndex("136")].CurrentlyPresent = false;
}
// 130 Calculated Track Geometric Altitude
if (FourFSPECOctets[Bit_Ops.Bit20] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 130:T";
I062DataItems[ItemIDToIndex("130")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("130")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 130:F";
I062DataItems[ItemIDToIndex("130")].CurrentlyPresent = false;
}
// 135 Calculated Track Barometric Altitude
if (FourFSPECOctets[Bit_Ops.Bit19] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 135:T";
I062DataItems[ItemIDToIndex("135")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("135")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 135:F";
I062DataItems[ItemIDToIndex("135")].CurrentlyPresent = false;
}
// 220 Calculated Rate Of Climb/Descent
if (FourFSPECOctets[Bit_Ops.Bit18] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 220:T";
I062DataItems[ItemIDToIndex("220")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("220")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 220:F";
I062DataItems[ItemIDToIndex("220")].CurrentlyPresent = false;
}
// 390 Flight Plan Related Data
if (FourFSPECOctets[Bit_Ops.Bit17] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 390:T";
I062DataItems[ItemIDToIndex("390")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("390")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 390:F";
I062DataItems[ItemIDToIndex("390")].CurrentlyPresent = false;
}
break;
case 4:
// 270 Target Size & Orientation
if (FourFSPECOctets[Bit_Ops.Bit31] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 270:T";
I062DataItems[ItemIDToIndex("270")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("270")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 270:F";
I062DataItems[ItemIDToIndex("270")].CurrentlyPresent = false;
}
// 300 Vehicle Fleet Identification
if (FourFSPECOctets[Bit_Ops.Bit30] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 300:T";
I062DataItems[ItemIDToIndex("300")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("300")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 300:F";
I062DataItems[ItemIDToIndex("300")].CurrentlyPresent = false;
}
// 110 Mode 5 Data reports & Extended Mode 1 Code
if (FourFSPECOctets[Bit_Ops.Bit29] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 110:T";
I062DataItems[ItemIDToIndex("110")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("110")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 110:F";
I062DataItems[ItemIDToIndex("110")].CurrentlyPresent = false;
}
// 120 Track Mode 2 Code
if (FourFSPECOctets[Bit_Ops.Bit28] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 120:T";
I062DataItems[ItemIDToIndex("120")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("120")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 120:F";
I062DataItems[ItemIDToIndex("120")].CurrentlyPresent = false;
}
// 510 Composed Track Number
if (FourFSPECOctets[Bit_Ops.Bit27] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 510:T";
I062DataItems[ItemIDToIndex("510")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("510")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 510:F";
I062DataItems[ItemIDToIndex("510")].CurrentlyPresent = false;
}
// 500 Estimated Accuracies
if (FourFSPECOctets[Bit_Ops.Bit26] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 500:T";
I062DataItems[ItemIDToIndex("500")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("500")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 500:F";
I062DataItems[ItemIDToIndex("500")].CurrentlyPresent = false;
}
// 340 Measured Information
if (FourFSPECOctets[Bit_Ops.Bit25] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 340:T";
I062DataItems[ItemIDToIndex("340")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("340")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " 340:F";
I062DataItems[ItemIDToIndex("340")].CurrentlyPresent = false;
}
break;
// These are Reserved Expansion and Special Purpose fileds.
case 5:
// RE Reserved Expansion Field
if (TheFifthFSPECOctet[Bit_Ops.Bit2] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " REF:T";
I062DataItems[ItemIDToIndex("REF")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("REF")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " REF:F";
I062DataItems[ItemIDToIndex("REF")].CurrentlyPresent = false;
}
// SP Special Purpose Indicator
if (TheFifthFSPECOctet[Bit_Ops.Bit1] == true)
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " SPI:T";
I062DataItems[ItemIDToIndex("SPI")].HasBeenPresent = true;
I062DataItems[ItemIDToIndex("SPI")].CurrentlyPresent = true;
}
else
{
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " SPI:F";
I062DataItems[ItemIDToIndex("SPI")].CurrentlyPresent = false;
}
break;
// Handle errors
default:
DataOut[DataOutIndex] = DataOut[DataOutIndex] + " UKN:T";
break;
}
}
DataOutIndex++;
CAT62DecodeAndStore.Do(LocalSingleRecordBuffer);
DataBufferIndexForThisExtraction = DataBufferIndexForThisExtraction + CurrentDataBufferOctalIndex;
}
// Return decoded data
NumOfMessagesDecoded = DataOutIndex;
return DataOut;
}
private static void ExtractAndDecodeASTERIX_CAT_DataBlock(byte[] DataBlock, bool Is_Live_Data) { // First thing is to store the time of the reception regardless of the category received string Time = DateTime.Now.Hour.ToString().PadLeft(2, '0') + ":" + DateTime.Now.Minute.ToString().PadLeft(2, '0') + ":" + DateTime.Now.Second.ToString().PadLeft(2, '0') + ":" + DateTime.Now.Millisecond.ToString().PadLeft(3, '0'); // Save off the time of reception so decoders can use it TimeOfReception = DateTime.Now; // Extract ASTERIX category string Category = ASTERIX.ExtractCategory(DataBlock); // Extract lenght in Bytes, as indicated by the ASTERIX string LengthOfDataBlockInBytes = ASTERIX.ExtractLengthOfDataBlockInBytes(DataBlock); // Here format the lenght of bytes // to always use 3 characters for better alignement if (LengthOfDataBlockInBytes.Length < 3) { LengthOfDataBlockInBytes = "0" + LengthOfDataBlockInBytes; } else if (LengthOfDataBlockInBytes.Length < 2) { LengthOfDataBlockInBytes = "00" + LengthOfDataBlockInBytes; } // Define a string to store data not specific for all messages and add commond data // 1. TIME of reception // 2. Source IP address // 3. Multicast IP address // 4. Length of data block in bytes // 5. Asterix Category // // 6. Append Category specifc data, done just below string Common_Message_Data_String; if (Is_Live_Data == true) Common_Message_Data_String = Time + " " + iep.ToString() + " " + SharedData.CurrentMulticastAddress + ':' + SharedData.Current_Port.ToString() + " " + LengthOfDataBlockInBytes.ToString() + " " + Category + " "; else Common_Message_Data_String = Time + " " + "Recorded" + " " + "Recorded" + ':' + "Recorded" + " " + LengthOfDataBlockInBytes.ToString() + " " + Category + " "; // Hold individual records of the messages // from an individual data block string[] MessageData = new string[3000]; byte[] DataBlockNoCATandLEN = new byte[DataBlock.Length - 3]; // Now after we extracted Category and Lenght of the Data Block lets remove the first three octets from the data // buffer and pass it on to individual message handlers to do message decoding Array.Copy(DataBlock, 3, DataBlockNoCATandLEN, 0, (DataBlock.Length - 3)); DataBlock = DataBlockNoCATandLEN; // Now do a switch based on the category received int NumOfMsgsDecoded = 0; switch (Category) { // Monoradar Data Target Reports, from a Radar Surveillance System to an SDPS // (plots and tracks from PSRs, SSRs, MSSRs, excluding Mode S and ground surveillance) case "001": if (Properties.Settings.Default.CAT_001_Enabled == true) { CAT01 MyCAT01 = new CAT01(); MessageData = MyCAT01.Decode(DataBlock, Time, out NumOfMsgsDecoded); } break; // Monoradar Service Messages (status, North marker, sector crossing messages) case "002": if (Properties.Settings.Default.CAT_002_Enabled == true) { CAT02 MyCAT02 = new CAT02(); MessageData = MyCAT02.Decode(DataBlock, Time, out NumOfMsgsDecoded); } break; // Monoradar Derived Weather Information case "008": if (Properties.Settings.Default.CAT_008_Enabled == true) { CAT08 MyCAT08 = new CAT08(); } break; // Next version of Category 002: PSR Radar, M-SSR Radar, Mode-S Station case "034": if (Properties.Settings.Default.CAT_034_Enabled == true) { CAT34 MyCAT34 = new CAT34(); MessageData = MyCAT34.Decode(DataBlock, Time, out NumOfMsgsDecoded); } break; // Next version of Category 001: PSR Radar, M-SSR Radar, Mode-S Station case "048": if (Properties.Settings.Default.CAT_048_Enabled == true) { CAT48 MyCAT48 = new CAT48(); MessageData = MyCAT48.Decode(DataBlock, Time, out NumOfMsgsDecoded); } break; // System Track Data(next version of Category 030 & 011, also applicable to non-ARTAS systems) case "062": if (Properties.Settings.Default.CAT_062_Enabled == true) { CAT62 MyCAT62 = new CAT62(); MessageData = MyCAT62.Decode(DataBlock, Time, out NumOfMsgsDecoded); } break; // Sensor Status Messages (SPDS) case "063": if (Properties.Settings.Default.CAT_063_Enabled == true) { CAT63 MyCAT63 = new CAT63(); } break; // SDPS Service Status Messages (SDPS) case "065": if (Properties.Settings.Default.CAT_065_Enabled == true) { CAT65 MyCAT65 = new CAT65(); } break; // Transmission of Reference Trajectory State Vectors case "244": if (Properties.Settings.Default.CAT_244_Enabled == true) { CAT244 MyCAT244 = new CAT244(); } break; // Handle unsupported data/categories default: Common_Message_Data_String = Common_Message_Data_String + " Unsupported category " + Category + " has been received"; break; } if (Properties.Settings.Default.PopulateMainListBox == true) { for (int I = 0; I < NumOfMsgsDecoded; I++) SharedData.DataBox.Items.Add(Common_Message_Data_String + MessageData[I]); } }