// SPDX-License-Identifier: AGPL-3.0-only /** * Digital Voice Modem - Fixed Network Equipment Core Library * AGPLv3 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * @package DVM / Fixed Network Equipment Core Library * @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0) * * Copyright (C) 2024 Bryan Biedenkapp, N2PLL * Copyright (C) 2024 Caleb, K4PHP * */ using System; using fnecore.EDAC; namespace fnecore.P25.LC { /// /// Base TSBK Encode/Decode class /// public abstract class TSBKBase { protected bool LastBlock; protected byte Lco; protected byte MfId; /// /// Creates an instance of /// protected TSBKBase() { MfId = P25Defines.P25_MFG_STANDARD; } /// /// Decode a TSBK /// /// /// /// /// public virtual bool Decode(byte[] data, ref byte[] payload, bool rawTSBK) { byte[] tsbk = new byte[P25Defines.P25_TSBK_LENGTH_BYTES]; FneUtils.Memset(tsbk, 0x00, tsbk.Length); if (rawTSBK) { Array.Copy(data, tsbk, P25Defines.P25_TSBK_LENGTH_BYTES); if (!CRC.CheckCCITT162(tsbk, P25Defines.P25_TSBK_LENGTH_BYTES)) { if ((tsbk[P25Defines.P25_TSBK_LENGTH_BYTES - 2U] != 0x00U) && (tsbk[P25Defines.P25_TSBK_LENGTH_BYTES - 1U] != 0x00U)) { Console.WriteLine("TSBK failed CRC CCITT-162 check"); return false; } } } else { byte[] raw = new byte[P25Defines.P25_TSBK_FEC_LENGTH_BYTES]; P25Interleaver.Decode(data, ref raw, 114, 318); EDAC.Trellis trellis = new EDAC.Trellis(); if (!trellis.Decode12(raw, ref tsbk)) { Console.WriteLine("TSBK Failed Trellis decode"); return false; } if (!CRC.CheckCCITT162(tsbk, P25Defines.P25_TSBK_LENGTH_BYTES)) { Console.WriteLine("TSBK Failed CRC check after Trellis"); return false; } } Lco = (byte)(tsbk[0] & 0x3F); // LCO LastBlock = (tsbk[0] & 0x80) == 0x80; // Last Block Marker MfId = tsbk[1]; // Manufacturer ID Array.Copy(tsbk, 1, payload, 0, P25Defines.P25_TSBK_LENGTH_BYTES - 4); return true; } /// /// Encode a TSBK /// /// /// /// /// public virtual void Encode(ref byte[] data, ref byte[] payload, bool rawTSBK, bool noTrellis) { byte[] tsbk = new byte[P25Defines.P25_TSBK_LENGTH_BYTES]; FneUtils.Memset(tsbk, 0x00, tsbk.Length); Array.Copy(payload, 0, tsbk, 2, P25Defines.P25_TSBK_LENGTH_BYTES - 4); tsbk[0] = Lco; // LCO tsbk[0] |= LastBlock ? (byte)0x80 : (byte)0x00; // Last Block Marker tsbk[1] = MfId; // Manufacturer ID CRC.AddCCITT162(ref tsbk, P25Defines.P25_TSBK_LENGTH_BYTES); byte[] raw = new byte[P25Defines.P25_TSBK_FEC_LENGTH_BYTES]; FneUtils.Memset(raw, 0x00, raw.Length); EDAC.Trellis trellis = new EDAC.Trellis(); trellis.Encode12(tsbk, ref raw); if (rawTSBK) { if (noTrellis) { Array.Copy(tsbk, 0, data, 0, P25Defines.P25_TSBK_LENGTH_BYTES); } else { Array.Copy(raw, 0, data, 0, P25Defines.P25_TSBK_FEC_LENGTH_BYTES); } } else { P25Interleaver.Encode(raw, ref data, 114, 318); } } /// /// /// /// public override string ToString() { return "UNKNOWN TSBK"; } } // public abstract class TSBKBase } // namespace fnecore.P25.LC