/** * Digital Voice Modem - Fixed Network Equipment * 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 * */ // // Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) // Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) // /* * Copyright (C) 2022 by Bryan Biedenkapp N2PLL * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. */ using System; using fnecore.EDAC; namespace fnecore.DMR { /// /// Represents full DMR link control. /// public sealed class FullLC { private static BPTC19696 bptc = new BPTC19696(); private static readonly byte[] VOICE_LC_HEADER_CRC_MASK = new byte[3] { 0x96, 0x96, 0x96 }; private static readonly byte[] TERMINATOR_WITH_LC_CRC_MASK = new byte[3] { 0x99, 0x99, 0x99 }; private static readonly byte[] PI_HEADER_CRC_MASK = new byte[2] { 0x69, 0x69 }; /* ** Methods */ /// /// Decode DMR full-link control data. /// /// /// /// public static LC Decode(byte[] data, DMRDataType type) { if (data == null) throw new NullReferenceException("data"); // decode BPTC (196,96) FEC byte[] lcData = new byte[12]; bptc.Decode(data, out lcData); switch (type) { case DMRDataType.VOICE_LC_HEADER: lcData[9U] ^= VOICE_LC_HEADER_CRC_MASK[0U]; lcData[10U] ^= VOICE_LC_HEADER_CRC_MASK[1U]; lcData[11U] ^= VOICE_LC_HEADER_CRC_MASK[2U]; break; case DMRDataType.TERMINATOR_WITH_LC: lcData[9U] ^= TERMINATOR_WITH_LC_CRC_MASK[0U]; lcData[10U] ^= TERMINATOR_WITH_LC_CRC_MASK[1U]; lcData[11U] ^= TERMINATOR_WITH_LC_CRC_MASK[2U]; break; default: // unsupported LC type return null; } // check RS (12,9) FEC if (!RS129.Check(lcData)) return null; return new LC(lcData); } /// /// Encode DMR full-link control data. /// /// /// /// public static void Encode(LC lc, ref byte[] data, DMRDataType type) { if (lc == null) throw new NullReferenceException("lc"); if (data == null) throw new NullReferenceException("data"); byte[] lcData = new byte[12]; lc.GetData(ref lcData); // encode RS (12,9) FEC byte[] parity = new byte[4]; RS129.Encode(lcData, 9, ref parity); switch (type) { case DMRDataType.VOICE_LC_HEADER: lcData[9U] = (byte)(parity[2U] ^ VOICE_LC_HEADER_CRC_MASK[0U]); lcData[10U] = (byte)(parity[1U] ^ VOICE_LC_HEADER_CRC_MASK[1U]); lcData[11U] = (byte)(parity[0U] ^ VOICE_LC_HEADER_CRC_MASK[2U]); break; case DMRDataType.TERMINATOR_WITH_LC: lcData[9U] = (byte)(parity[2U] ^ TERMINATOR_WITH_LC_CRC_MASK[0U]); lcData[10U] = (byte)(parity[1U] ^ TERMINATOR_WITH_LC_CRC_MASK[1U]); lcData[11U] = (byte)(parity[0U] ^ TERMINATOR_WITH_LC_CRC_MASK[2U]); break; default: // unsupported LC type return; } // encode BPTC (196,96) FEC bptc.Encode(lcData, out data); } /// /// Decode DMR privacy control data. /// /// /// /// public static PrivacyLC DecodePI(byte[] data) { if (data == null) throw new NullReferenceException("data"); // decode BPTC (196,96) FEC byte[] lcData = new byte[12]; bptc.Decode(data, out lcData); // make sure the CRC-CCITT 16 was actually included (the network tends to zero the CRC) if (lcData[10U] != 0x00U && lcData[11U] != 0x00U) { // validate the CRC-CCITT 16 lcData[10U] ^= PI_HEADER_CRC_MASK[0U]; lcData[11U] ^= PI_HEADER_CRC_MASK[1U]; if (CRC.CheckCCITT162(lcData, 12)) return null; // restore the checksum lcData[10U] ^= PI_HEADER_CRC_MASK[0U]; lcData[11U] ^= PI_HEADER_CRC_MASK[1U]; } return new PrivacyLC(lcData); } /// /// Encode DMR privacy control data. /// /// /// /// public static void EncodePI(PrivacyLC lc, ref byte[] data) { if (lc == null) throw new NullReferenceException("lc"); if (data == null) throw new NullReferenceException("data"); byte[] lcData = new byte[12]; lc.GetData(ref lcData); // compute CRC-CCITT 16 lcData[10U] ^= PI_HEADER_CRC_MASK[0U]; lcData[11U] ^= PI_HEADER_CRC_MASK[1U]; CRC.AddCCITT162(ref lcData, 12); // restore the checksum lcData[10U] ^= PI_HEADER_CRC_MASK[0U]; lcData[11U] ^= PI_HEADER_CRC_MASK[1U]; // encode BPTC (196,96) FEC bptc.Encode(lcData, out data); } } // public class LC } // namespace fnecore.DMR