BER-TLV的认识与原子数据解析

BER-TLV

BER-TLV中BER是Basic Encoding Rules的简写,是ASN.1的一种编码格式,它是将抽象信息编码成为具体数据流的最初规则。

相关知识及定义来自:X.690

BER编码

BER格式描述了自我描述与分界的数据结构。每个数据编码为一个由type标识,length描述,data数据的元素,并且在必要的位置添加结束标识。这个格式可以在不需要提前了解数据的大小,内容,及数据意义情况下允许接收方从数据流(data inputstream)中解码数据。

BER格式

BER元素结构

这是4个部分组成的一个单元组,末尾的八元组(字节)是可选的,只在长度形式未定义情况下使用。内容(Contents octets)部分也可能在为null的情况下被移除。

Tag/Type域

Type

这部分数据(尤其是序列(sequences),集合(sets),选项(choices)的成员)由唯一tag数标识,区别于整个data中的其他成员。这样的标记可以是隐式的(在这种情况下,它们被编码为值的TLV标记,而不是使用基类型作为TLV标记)或显式(其中标记用于包装基类型TLV的构造TLV中)。

编码可以是基本结构类型或是复杂类型的,取决于选择的类型。下边图描述了tag类型的选择,这些定义也是在ASN.1定义中声明的。

tag number值对应类型

tag域编码

识别部分将元素类型编码为ASN.1的tag值,由类型与数字组成,这里也定义了内容部分是基本结构(primitive)或是复杂结构(constructed)。

tag域结构

首个字节中,bit6编码指示了类型是基本类型或是复合类型,bit7, bit8指示了类型,而big1-bit5指示了tag值,也可以有后续字节一同标识tag部分。

bit7,bit8可表示值

bit6可选值

当tag数值太大,超出tag域5位bit(不超过31)可表达的范围时,需要更多字节表示tag。

当首字节中bit1-bit5均是1,tag数值由后续字节表示。当后续字节的bit8位置是1时,表明还有后续字节,bit1-bit7表示tag数值。

tag二进制是大端的(即最高位在左边)

Length域

length域有两个格式的定义:定义格式,未定义格式。

length域首字节

定义格式(Definite)

这部分编码的值表明了内容部分(Content octets)基本结构或复杂结构占的长度。不同长度值的表示范围由一个长格式或一个短格式进行定义。

  • 短格式:一个字节,bit8是0,bit1-bit7编码值表示content部分占用的字节数;
  • 长格式:若干字节,首字节bit8是1,表示后续还有1个或多个字节表示长度值。首字节bit8是1,bit1-bit7(排除0和127)编码值表示内容部分的长度。

长格式示例

未定义格式(Indefinite)

这个格式未编码长度值,但内容部分需要标记字节来结束。

这个格式有一个字节组成,其中bit8是1,bit1-bit7均是0。后续内容部分就是2个字节的EOC符号。

Content域

这个部分的编码值表示了元素的实际数据值。

注意的是值域(value field)也可能没有字节表示,即length域的编码值为0。

Java解析BER-TLV方法封装

Tlv

/**
 * Tlv接口定义,用以自定义TLV实体类进行实现。
 *
 * TLV(type-length-value or tag-length-value) is an encoding theme for optional information
 * element in a certain protocol.
 *
 * The type and length are fixed in size(typically 1-4 bytes) and the value field is of variable
 * size.
 *
 * Type: A binary code, often simply alphanumeric, which indicates the kind of field that this
 * part of the message represents.
 *
 * Length: The size of the value field(typically in bytes).
 *
 * Value: Variable-sized series of bytes which contains data for this part of the message.
 *
 * @author Nicholas Ni
 * @since 2020/8/3 16:37
 */
public class Tlv implements Serializable {
}

BerTlv

/**
 * 基本编码规则TLV格式,符合ASN.1标准。
 *
 * 若需要自定义相关的数据处理,可以自定义实体类集成Tlv,且解析类实现TlvParser接口并实现parseTlvList(byte[])方法。
 */
public class BerTlv extends Tlv {

    public int tag;

    public int length;

    public byte[] value;

    public BerTlv() {
    }

    public BerTlv(int tag, int length, byte[] value) {
        this.tag = tag;
        this.length = length;
        this.value = value;
    }
}

TlvParser

/**
 * TLVResolvable定义了TLV解析方法,提供默认的解析方式。
 * 由于TLV数据type,length字节数占1-4个字节,可以是变长的,因此在解析前需要设置具体的字节长度——依据约定。
 */
public interface TlvParser extends Resolvable {

    /**
     * 解析TLV列表。
     *
     * @param tlvByteArray TLV字节数组
     */
    default Map<Integer, ? extends Tlv> parseTlvList(byte[] tlvByteArray) {
        System.out.println("This is default implementation.");
        return new TreeMap<>();
    }

}

BerTlvParser

/**
 * 解析TLV数据工具类。
 * <p>
 * 使用:
 * <p>
 * <p>
 * 直接使用创建,匿名类方式进行创建,可以重写部分方法,灵活设置type,length长度。
 */
public final class BerTlvParser implements TlvParser {

    /**
     * 依据byte数组解析TLV列表数据。
     *
     * @param data   包含TLV数据的字节数组
     * @param offset TLV数据的开始索引
     * @param length TLV数据长度
     * @return tlv map
     */
    public Map<Integer, BerTlv> parseTLVList(byte[] data, int offset, int length) {
        byte[] tlvByteArray = new byte[length];
        System.arraycopy(data, offset, tlvByteArray, 0, length);

        return parseTlvList(tlvByteArray);
    }

    /**
     * 依据byte数组解析TLV列表数据。
     *
     * @param data TLV字节数组
     * @return TLV map
     */
    @Override
    public final Map<Integer, BerTlv> parseTlvList(byte[] data) {
        Objects.requireNonNull(data, "Byte array indicating TLV list can not be null!");

        if (data.length == 0) {
            return new TreeMap<>();
        }

        int currIndex = -1;
        int tag = data[++currIndex] & 0xFF;
        if ((tag & 0x20) != 0x20) {
            // 解析基本结构类型(单一结构)
            return parsePrimitiveData(data);
        }
        // 解析复合结构类型
        return parseComprehensiveData(data);
    }

    /**
     * 解析复合类型的TLV数据结构 —— 即嵌套的TLV结构。
     *
     * @param data 解析的数据
     * @return 完成解析的TLV map
     */
    private Map<Integer, BerTlv> parseComprehensiveData(byte[] data) {
        final Map<Integer, BerTlv> tlvMap = new TreeMap<>();
        parseComprehensiveData(data, tlvMap, 0);

        System.out.println(
                String.format(Locale.getDefault(),
                              "Parsed result size: %d",
                              tlvMap.size())
        );
        return tlvMap;
    }

    /**
     * 解析复杂类型TLV结构。
     *
     * @param data   解析的数据
     * @param retMap 存储结果的TLV map
     * @param index  当前索引位置
     */
    private void parseComprehensiveData(byte[] data,
                                        Map<Integer, BerTlv> retMap,
                                        int index) {
        int currIndex = index;
        if (currIndex >= data.length) {
            return;
        }

        final int lengthTag = sizeTag(data, currIndex);
        currIndex += lengthTag;
        final int lengthLen = sizeLength(data, currIndex);
        currIndex += lengthLen;

        int currentByte = data[currIndex] & 0xFF;
        // 单一结构
        while ((currentByte & 0x20) != 0x20) {
            currIndex += parsePrimitiveData(data, retMap, currIndex);
            if (currIndex >= data.length) {
                break;
            }
            currentByte = data[currIndex] & 0xFF;
        }

        if (currIndex >= data.length) {
            return;
        }

        parseComprehensiveData(data, retMap, currIndex);

    }

    /**
     * 解析复合结构内单一结构(无法再分)的TLV数据。
     *
     * @param data   解析的数据
     * @param retMap 存储结果的TLV map
     * @param index  当前索引位置
     * @return 当前TLV实体的长度
     */
    private int parsePrimitiveData(byte[] data,
                                   Map<Integer, BerTlv> retMap,
                                   int index) {
        if (index >= data.length) {
            return 0;
        }

        final BerTlv berTlv = new BerTlv();
        int currIndex = index;

        final int lengthOfTag = sizeTag(data, currIndex);
        berTlv.tag = parseTagNumber(data, currIndex);
        currIndex += lengthOfTag;

        final int lengthOfLength = sizeLength(data, currIndex);
        berTlv.length = parseLength(data, currIndex);
        currIndex += lengthOfLength;

        final int lengthOfValue = berTlv.length;
        berTlv.value = parseValueArray(data, currIndex, lengthOfValue);
        currIndex += lengthOfValue;

        retMap.put(berTlv.tag, berTlv);
        return currIndex - index;
    }

    /**
     * 解析单一结构(primitive constructed data) TLV数据。
     *
     * @param data TLV数据
     * @return 解析后的TLV map
     */
    private Map<Integer, BerTlv> parsePrimitiveData(byte[] data) {
        final BerTlv berTlv = new BerTlv();
        int currIndex = 0;

        final int lengthOfTag = sizeTag(data, currIndex);
        berTlv.tag = parseTagNumber(data, currIndex);
        currIndex += lengthOfTag;

        final int lengthOfLength = sizeLength(data, currIndex);
        berTlv.length = parseLength(data, currIndex);
        currIndex += lengthOfLength;

        final int lengthOfValue = berTlv.length;
        berTlv.value = parseValueArray(data, currIndex, lengthOfValue);
        currIndex += lengthOfValue;

        System.out.println(String.format(Locale.getDefault(), "final index: %d", currIndex));

        if (currIndex < data.length) {
            throw new RuntimeException(
                    "Parse primitive constructed data, value length does not match length field value."
            );
        }

        return new TreeMap<Integer, BerTlv>() {
            {
                put(berTlv.tag, berTlv);
            }
        };
    }

    /**
     * 解析获取tlv字节数组中表示value的字节数组,返回原始的字节数组。
     *
     * @param data   TLV字节数组
     * @param offset 当前索引
     * @param length 表示value的字节数组的长度
     * @return 表示value的字节数组
     */
    private byte[] parseValueArray(byte[] data, int offset, int length) {
        byte[] valueArray = new byte[length];
        System.arraycopy(data, offset, valueArray, 0, length);
        return valueArray;
    }

    /**
     * 解析tag域,并返回tag域的值。
     * <p>
     * 如: 4F 05 48656C6C6F
     * 解析过程:
     * <ul>
     *     <li>'4F'(01001111) 是tag域的字段,其高三位中前两位表示解析的信息类型,第三位表示TLV是基本结构类型或是复合类型的结构,暂且不计,用途不影响解析;</li>
     *     <li>低5位(01111),最高位若不是1,则剩余4位即可标识TLV实体的标号;若5位均是1(11111),则表示后续还有tag域的字节;</li>
     * </ul>
     *
     * @param data   TLV字节数组
     * @param offset 当前偏移值(索引)
     * @return tag值
     */
    private int parseTagNumber(byte[] data, int offset) {
        int byteTag = data[offset] & 0xFF;

        if ((byteTag & 0x1F) < 0x1F) {
            return byteTag & 0x1F;
        }

        int indexTag = offset + 1;
        byteTag = data[indexTag] & 0xFF;
        while (((byteTag >>> 7) & 0x01) == 0x01) {
            byteTag = data[++indexTag];
        }
        int tagNumber = 0;
        // 表示tag number的字节数
        int lenTagNumber = indexTag - offset;
        byte[] tagNumberByes = new byte[lenTagNumber];
        System.arraycopy(data, offset + 1, tagNumberByes, 0, lenTagNumber);
        switch (lenTagNumber) {
            case 1: {
                tagNumber = tagNumberByes[0] & 0xFF;
                break;
            }

            case 2: {
                tagNumber = ((tagNumberByes[0] & 0xFF) << 8) | (tagNumberByes[1] & 0xFF);
                break;
            }

            case 3: {
                tagNumber = ((tagNumberByes[0] & 0xFF) << 16) |
                        ((tagNumberByes[1] & 0xFF) << 8) | (tagNumberByes[2] & 0xFF);
                break;
            }

            case 4: {
                tagNumber = ((tagNumberByes[0] & 0xFF) << 24) |
                        ((tagNumberByes[1] & 0xFF) << 16) |
                        ((tagNumberByes[2] & 0xFF) << 8) |
                        (tagNumberByes[2] & 0xFF);
                break;
            }
        }
        if (tagNumber < 0x1F) {
            throw new RuntimeException(
                    "Invalid tag number, code < 31, but len of tag field is " + (lenTagNumber + 1)
                            + ", index = " + offset
            );
        }
        return tagNumber;
    }

    /**
     * 解析获取length域的值,即tag-length-value中length值。
     *
     * @param data   TLV字节数组
     * @param offset 当前偏移值(索引)
     * @return length值
     */
    private int parseLength(byte[] data, int offset) {
        int byteLength = data[offset] & 0xFF;

        if (byteLength < 0x80) {
            return byteLength & 0x7F;
        }

        if (byteLength == 0x80) {
            throw new RuntimeException("Invalid length field code = 0x80!");
        }

        final int numLenBytes = byteLength & 0x7F;
        byte[] lengthBytes = new byte[numLenBytes];
        System.arraycopy(data, offset + 1, lengthBytes, 0, numLenBytes);
        int valLength = 0;
        switch (lengthBytes.length) {
            case 1: {
                valLength = lengthBytes[0] & 0xFF;
                break;
            }

            case 2: {
                valLength = ((lengthBytes[0] & 0xFF) << 8) | (lengthBytes[1] & 0xFF);
                break;
            }

            case 3: {
                valLength = ((lengthBytes[0] & 0xFF) << 16) |
                        ((lengthBytes[1] & 0xFF) << 8) |
                        (lengthBytes[2] & 0xFF);
                break;
            }

            case 4: {
                valLength = ((lengthBytes[0] & 0xFF) << 24) |
                        ((lengthBytes[1] & 0xFF) << 16) |
                        ((lengthBytes[2] & 0xFF) << 8) |
                        (lengthBytes[3] & 0xFF);
                break;
            }
        }
        return valLength;
    }

    /**
     * 解析tag域长度,判断依据:
     * </p>
     * <ul>
     *      <li>判断tag域首字节低5位是否全为1,若全是1 则表示tag域还有后续字节表示tag编号;</li>
     *      <li>若有后续字节,查看最高位,若最高位是1,则表示后续依然有字节表示tag,0则结束。</li>
     * </ul>
     *
     * @param data   tlv字节数组
     * @param offset 当前偏移位(索引位置)
     * @return tag域占的字节数
     */
    private int sizeTag(byte[] data, int offset) {
        int indexTag = offset;
        int byteTag = data[indexTag] & 0xFF;

        if ((byteTag & 0x1F) == 0x1F) {
            byteTag = data[++indexTag] & 0xFF;
            while (((byteTag >>> 7) & 0x01) == 1) {
                byteTag = data[++indexTag] & 0xFF;
            }
        }

        return indexTag - offset + 1;
    }

    /**
     * BER-TLV中的长度表示Value域中的数据长度,由1到多个字节组成。
     * <p>
     * 解析length域占的字节数,判断依据:
     * <ul>
     *     <li>如果首字节的最高位为0,则低7位表示长度,数据长度最大值为127;</li>
     *     <li>如果首字节的最高位为1,则表明Value域中的数据长度超过127,其低7位表示后续LEN域的字节数。</li>
     * </ul>
     *
     * @param data   tlv字节数组
     * @param offset 当前偏移位(索引位置)
     * @return length域占的字节数
     */
    private int sizeLength(byte[] data, int offset) {
        int byteLength = data[offset] & 0xFF;
        int sizeLength = 1;

        if (((byteLength >>> 7) & 0x01) == 0x01) {
            sizeLength += (byteLength & 0x7F);
        }

        return sizeLength;
    }
}

### 回答1: 误码率(Bit Error Rate,BER)是指传输信号中出现误码的概率,通常用来衡量数字通信系统的性能。信噪比(Signal-to-Noise Ratio,SNR)是指信号的功率与噪声的功率之比,用来表示信号的强度和噪声的影响程度。 误码率与信噪比之间存在着相互影响的关系。一般来说,误码率与信噪比成反比关系,即信噪比越高,误码率越低。这是因为当信噪比较高时,信号的能量相对较强,而噪声的影响较小,接收机能够更容易区分正确的信号与噪声,从而降低了误码率。 误码率与信噪比之间的关系可以通过误码率公式进行解析。误码率公式一般采用伯努利试验的概率模型来描述,即假设传输的每个比特独立地以相同的概率出现误码。根据信号与噪声的统计分布特性,误码率计算方式如下: BER = 0.5 * erfc(sqrt(SNR)) 其中,erfc表示余补误差函数,SNR表示信噪比。 由上述公式可知,随着信噪比的增加,误码率将呈指数级下降。这是因为当信噪比较高时,erfc(sqrt(SNR))值较小,因此BER值较低,对应着较低的误码率。 总之,误码率与信噪比之间存在着反比关系。提高信噪比将有助于降低误码率,从而提高数字通信系统的性能和可靠性。 ### 回答2: 误码率(Bit Error Rate,简称BER)是衡量数字通信系统性能的重要指标之一,它代表着在传输过程中,每传输1比特中出现错误的比特数的比例。 信噪比(Signal-to-Noise Ratio,简称SNR)是指信号功率与噪声功率之比,它描述了在传输信号的过程中,信号与噪声的相对强弱关系。 BER与SNR之间存在着确定的关系,一般来说,BER随着SNR的增加而下降。原因如下: 首先,当信噪比较低时,噪声的影响会明显增强,从而导致传输信号被噪声所干扰,出现误码的概率增加。因此,BER随着SNR的降低而增加。 其次,随着信噪比的提高,传输通道的容错能力也随之增强,信号的抗干扰能力变强,因此误码率会逐渐减小。 最后,当信噪比较高时,噪声的影响显著减弱,传输信号质量提高,误码率趋于接近零。 总的来说,较高的信噪比将对传输信号的品质起到积极的影响,降低误码率。然而,误码率不仅与SNR有关,还受到其他因素(例如调制方式、传输速率、编码方式等)的影响。 因此,在设计和优化数字通信系统时,需要综合考虑SNR以及其他相关参数,以达到所需的误码率要求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VoidHope

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值