/**
* 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