1. 程式人生 > >使用asn1tools進行asn1編解碼

使用asn1tools進行asn1編解碼

最近在做3GPP的編解碼,發現有兩個第三方庫比較好用。一個是ASN1C(c語言編譯環境),一個是python第三方庫asn1tools。這裡介紹下asn1tools的使用方法:

1 第一步:生成asn檔案

將需要編碼的資料結構儲存在asn字尾名的檔案中

3GPP中的結構如下:

-- ASN1START

BCCH-BCH-Message-NB ::= SEQUENCE {

    message                BCCH-BCH-MessageType-NB

}

BCCH-BCH-MessageType-NB::= MasterInformationBlock-NB

-- ASN1STOP

對應的.asn檔案的基本結構如下:也就是講ASN1START和ASN1STOP中的資料提取出來。然後上asn自己的頭資訊

EUTRA-RRC-Definitions DEFINITIONS AUTOMATIC TAGS ::=

BEGIN

BCCH-BCH-Message ::= SEQUENCE {

        message                                    BCCH-BCH-MessageType

}

END

在3GPP中有大量的類似結構,如果一個個手動的拷貝,太耗費時間了。因此用下面的程式碼將3GPP中的資料結構自動提取出來儲存在asn檔案中。程式碼如下:

#include <iostream>

#include <fstream>

#include <string>

#include <vector>

using namespace std;

int main()

{

    std::string output_file;

    std::string input_file = "D:/code_block_prj/gen_asn/protol.txt";

    std::cout<<input_file.c_str()<<std::endl;

    int pos = input_file.find('.');

    if (pos == std::string::npos )

    {

        output_file = input_file + ".asn";

    }

    else

    {

        output_file = input_file.substr(0,pos) + ".asn";

    }

    std::fstream input;

    input.open(input_file.c_str(), std::fstream::in );

    if ( input.fail() == true)

    {

        std::cout<<"Please check input file is correct !"<<std::endl;

        return 1;

    }

    std::fstream output;

    output.open(output_file.c_str(), std::fstream::out );

    if ( output.fail() == true)

    {

        std::cout<<"The output file can not be created here !"<<std::endl;

        return 1;

    }

    std::string input_line;

    std::vector<std::string > vec_asn;

    std::vector<std::string >::iterator itr;

    const unsigned long cul_asn_idle  = 0x0;

    const unsigned long cul_asn_start = 0x1;

    unsigned long asn_state = cul_asn_idle;

    while ( std::getline(input, input_line) )

    {

        if ( cul_asn_idle == asn_state )

        {

            if ( input_line.find("-- ASN1START") != std::string::npos )

            {

                asn_state |=  cul_asn_start;

            }

            continue;

        }

        if ( 0 != (cul_asn_start & asn_state) )

        {

            if ( input_line.find("-- ASN1STOP") != std::string::npos )

            {

                asn_state = cul_asn_idle;

            }

            else

            {

                vec_asn.push_back(input_line);

            }

        }

    }

    for ( itr  = vec_asn.begin(); itr != vec_asn.end(); ++itr )

    {

        output<<*itr<<std::endl;

    }

    input.close();

    output.close();

    return 0;

}

2:開啟36331的word文件並另存為txt檔案

3:執行上面的程式,其中input_file就是儲存txt檔案的位置,需要自己設定。執行完後會在本地資料夾下面生成一個asn檔案

第二步:利用asn1tools進行編解碼:

一:首先pip3 install asn1tools進行模組安裝

二:在3GPP中有大量的資料結構,例如sequence, bit string, octer string, bool, sequence of等等,這些結構體在python對應的結構體如下表。

ASN.1 type

Python type

Example

BOOLEAN

bool

True

'ackNackSRS-SimultaneousTransmission': True

INTEGER

int

87

'p0-NominalPUCCH': -127,

REAL

float

33.12

NULL

None

BIT STRING

tuple(bytes, int)

(b'\x50', 4)

元組第一個引數為值,第二個引數為bit長度

示例:

ac-BarringForSpecialAC                            BIT STRING (SIZE(5))

'ac-BarringForSpecialAC': (b'\xf0', 5)

OCTET STRING

bytes

b'\x44\x1e\xff'

hnb-Name                                                          OCTET STRING (SIZE(1..48))

'hnb-Name': b'4'

OBJECT IDENTIFIER

str

'1.33.2'

ENUMERATED

str

'one'

ac-BarringTime                                                  ENUMERATED {s4, s8, s16, s32, s64, s128, s256, s512},

程式碼:

'ac-BarringTime': 's128',

SEQUENCE

dict

{'a': 52, 'b': 1}

SEQUENCE OF

list

[1, 3]採用list列表的方法[]

示例一:

InterFreqCarrierFreqList ::=       SEQUENCE (SIZE (1..maxFreq)) OF InterFreqCarrierFreqInfo

'interFreqCarrierFreqList': ['dl-CarrierFreq': 1,

                                            'q-RxLevMin': -45,

                                            't-ReselectionEUTRA': 0,

                                            'threshX-High': 31,

                                            'threshX-Low': 29,

                                            'allowedMeasBandwidth': 'mbw6',

                                            'presenceAntennaPort1': True,

                                            'neighCellConfig': (b'\x00', 2),

                                            'q-OffsetFreq': 'dB0]

SET

dict

{'foo': 'bar'}

SET OF

list

[3, 0, 7]

CHOICE

tuple

('a', 5)

UTF8String

str

'hello'

NumericString

str

'234359'

PrintableString

str

'goo'

IA5String

str

'name'

VisibleString

str

'gle'

GeneralString

str

'abc'

BMPString

str

'ko'

GraphicString

str

'a b'

TeletexString

str

'ßø'

UniversalString

str

'åäö'

UTCTime

datetime.datetime

datetime(2018, 6, 11)

GeneralizedTime

datetime.datetime

datetime(2018, 1, 31)

ObjectDescriptor

三:對結構進行賦值。以BCCH-DL-SCH-Message-NB結構為例,首先需要根據BCCH-DL-SCH-Message-NB的結構用python的結構體進行賦值。如下所示。具體的賦值方法參考上面的表格。

BCCH_DL_SCH_Message_NB={

    'message':(

        'c1',(

            'systemInformationBlockType1-r13',{

                'hyperSFN-MSB-r13':(b'\x07',8),

                'cellAccessRelatedInfo-r13':{

                    'plmn-IdentityList-r13':[

                        {'plmn-Identity-r13':{'mcc':[0,0,1],'mnc':[0,1]},

                         'cellReservedForOperatorUse-r13':'notReserved',

                         'attachWithoutPDN-Connectivity-r13':'true'}],

                'trackingAreaCode-r13':(b'\x00\x01',16),

                'cellIdentity-r13':(b'\x00\x01\x10\x10',28),

                'cellBarred-r13':'notBarred',

                'intraFreqReselection-r13':'notAllowed',

            },

                'cellSelectionInfo-r13':{

                    'q-RxLevMin-r13':-53,

                    'q-QualMin-r13':-20

                },

                'freqBandIndicator-r13':8,

                'schedulingInfoList-r13':[{'si-Periodicity-r13':'rf64','si-RepetitionPattern-r13':'every8thRF','sib-MappingInfo-r13':[],'si-TB-r13':'b552'}],

                'si-WindowLength-r13':'ms160'

            }

        )

    )

}

四:進行編碼。在encode函式中第一個引數就是asn檔案中的結構體名稱。第二個引數就是上面賦值的字典結構。最終得到16進位制的碼流

def asn1tools__3GPP():

    foo = asn1tools.compile_files('protol.asn', 'uper')

    encoded = foo.encode('BCCH-DL-SCH-Message-NB',BCCH_DL_SCH_Message_NB)

print(encoded.hex())

五:資料視覺化:16進位制的碼流對於觀測不方便。因此將前面編碼得到的16進位制碼流再進行解碼並儲存在json檔案中,然後通過jsonViewer工具進行檢視。程式碼如下

class MyEncoder(json.JSONEncoder):

    def default(self, obj):

        if isinstance(obj, bytes):

            return str(obj, encoding='utf-8');

        return json.JSONEncoder.default(self, obj)

def asn1tools__3GPP():

    foo = asn1tools.compile_files('protol.asn', 'uper')

    encoded = foo.encode('BCCH-DL-SCH-Message-NB',BCCH_DL_SCH_Message_NB)

    print(encoded.hex())

    decoded=foo.decode('BCCH-DL-SCH-Message-NB',encoded)

    value=json.dumps(decoded,indent=1,cls=MyEncoder,ensure_ascii=False)

    print(decoded,value)

    f=open('BCCH.json','wb')

    f.write(value.encode("utf-8"))

f.close()

在jsonViewer中開啟json檔案,可以更直觀的觀測結構

但是這樣會有一個問題,在編譯BIT STRING或者OCTER STRING的時候,編譯完後的資料無法寫入json檔案。原因在於json檔案是utf-8的編碼格式。某些位元組utf-8無法識別,例如0x80這樣的資料。如果將trackingAreaCode-r13改成如下的值,那麼在寫入json檔案的時候就會提示utf-8 can’t decode \0x80的錯誤

'trackingAreaCode-r13':(b'\x80\x01',16),

那麼程式碼修改如下。用uper和jer兩種編碼方式編譯asn檔案。然後將UPER解碼得到的資料用jer的方法進行編碼。然後再寫入json檔案。

def asn1tools__3GPP():

    foo = asn1tools.compile_files('protol.asn', 'uper')

    foo_jer=asn1tools.compile_files('protol.asn', 'jer')

    encoded = foo.encode('BCCH-DL-SCH-Message-NB',BCCH_DL_SCH_Message_NB)

    print(encoded.hex())

    decoded=foo.decode('BCCH-DL-SCH-Message-NB',encoded)

    value_jer = foo_jer.encode('BCCH-DL-SCH-Message-NB', decoded)

    with open('BCCH.json','wb') as f:

        f.write(value_jer)

這樣做的原理是UPER是將資料以位元組的形式編碼,而jer是以字串的形式編碼。因此寫入json檔案沒有任何問題