3GPP WCDMA下行数据解码流程,从Bits数据流解码到语音和短信,C++实战演示代码

为了学习验证从空中扫频,到解码出语音短信这个流程。所以只解析了必要的BBCH_BCH(为了获取频点信息),以及FACH和DPCH这三个通道的数据。
用了一些自己定义的数据结构Buffer_t这类,无关乎流程。整个解析代码,从bits流解到每个通道的MAC,RLC,到RRC流程交互,都是自己全手工编写完成,没有参考样例,没有经过大量数据测试,无责任不保证有大量错误。

RRC消息结构解析用的asn1c(吐槽:)当年的asn1c还不甚完善,会生成大量重复的结构定义,耗费了不少时间手工修改,不知道现在改进了没。
Milenage和Kasumi解密算法代码在另外两篇博文中。

程序解码流程根据标准文档编写,整个流程能成功走完。先用ReadBitsFile函数读取bits数据流文本文件(自定义格式),拿到几个通道的数据,然后用L3Decode函数解码流程,保存解码出来的语音和短信文件。
代码中有好几处地方不太确定,比如sfn,cfn和hfn的值计算,以及后面做f8解密时,解密参数count的计算。代码流程仅供学习参考。

(吐槽:)f8的解密参数bearer是用RB identity-1,而RB identity获取时,则是用承载序号+1。这个小小的细节,做f8解密时整整卡了我2个星期翻英文的TS25.331标准文档,比较尴尬的是,中文的翻译版反而看不懂流程…

L3Decode.h

#ifndef _L3_DECODE_H_INCLUDE_
#define _L3_DECODE_H_INCLUDE_

static const int DATA_LENGTH_BCH = 246;
static const int DATA_LENGTH_FACH = 168;
static const int DATA_LENGTH_DPCH = 148;

static const char* CH_TYPE_BCH = "DL_BCCH_BCH";
static const char* CH_TYPE_FACH = "FACH";
static const char* CH_TYPE_DPCH = "DPCH";

static const char* VOICE_FILE_OUT_NAME = "voice.amr";
static const char* RRC_TMSI_LIST_FILE_OUT_NAME = "rrc_tmsi_list.txt";

typedef struct asn_TYPE_descriptor_s asn_TYPE_descriptor_t;

struct TimeCode_t
{
	TimeCode_t()
	{
		Clear();
	}
	void Clear()
	{
		cfn = 0;
		wHour = 0;
		wMinute = 0;
		wSecond = 0;
	}
	uint32_t cfn;
	uint16_t wHour;
	uint16_t wMinute;
	uint16_t wSecond;
};

struct PDULineInfo_t
{
	PDULineInfo_t()
	{
		Clear();
	};

	void Clear()
	{
		chIdx = 0;
		timeCode.Clear();
		pduLine.clear();
	};

	TimeCode_t timeCode;
	string pduLine;
	string chType;
	uint8_t chIdx;
};

struct BitBuffer_t : public Buffer_t
{
	BitBuffer_t() : Buffer_t(), m_nLastSkipBits(0) { };
	BitBuffer_t(PBYTE pData, UINT nDataSize, UINT nLastSkipBits) : Buffer_t(pData, nDataSize), m_nLastSkipBits(nLastSkipBits) { };
	BitBuffer_t(const BitBuffer_t& st) : Buffer_t(st), m_nLastSkipBits(st.m_nLastSkipBits) { };

	virtual BitBuffer_t& operator =(const BitBuffer_t& st)
	{
		this->m_nLastSkipBits = st.m_nLastSkipBits;

		*(Buffer_t *)this = st;

		return *this;
	}

	virtual void Free()
	{
		Buffer_t::Free();
		m_nLastSkipBits = 0;
	}

	virtual void ClearData()
	{
		Buffer_t::ClearData();
		m_nLastSkipBits = 0;
	}

	virtual size_t GetDataBits()
	{
		return m_nDataSize * 8 - m_nLastSkipBits;
	}

	virtual BOOL Allocate(INT nBufferSize)
	{
		if (Buffer_t::Allocate(nBufferSize))
		{
			m_nLastSkipBits = 0;
			return TRUE;
		}
		return FALSE;
	}

	uint8_t m_nLastSkipBits;
};

struct PDUData_t
{
	PDUData_t() 
	{
		desType = 0; 
		isRepeatedSN = 0;
	}

	string chType;
	asn_TYPE_descriptor_t* desType;

	string desMAC;
	string desRLC;
	string modeRLC;

	Buffer_t dataMAC;
	Buffer_t dataRLC;
	BitBuffer_t dataPDU;
	BitBuffer_t dataF8PDU;

	bool isRepeatedSN;
};

struct PDU_CH_SN_Data_t
{
	PDU_CH_SN_Data_t()
	{
		sn = -1;
	}

	int sn;
	BitBuffer_t data;
	vector<BitBuffer_t > pduDataList;
};

struct SIBInfo_t
{
	SIBInfo_t()
	{
		sibDesType = 0;
		sibType = 0;
		sibCount = 0;
	};

	asn_TYPE_descriptor_t* sibDesType;
	int sibType;
	int sibCount;
	map<int, BitBuffer_t> sibData;
};

struct RRCConnectInfo_t
{
	RRCConnectInfo_t()
	{
		tmsi = 0;
		dpchFrameOffset = 0;
		sf = 0;
		codeNumber = 0;
	};

	uint32_t tmsi;
	long dpchFrameOffset;
	long sf;
	long codeNumber;
};


struct L3DecodeSys_t
{
	L3DecodeSys_t()
	{
		cn_DomainIdentity = 0;
		memset(ki, 0, 16);
		memset(opc, 0, 16);

		Init();
	};

	~L3DecodeSys_t()
	{
		Close();
	};

	void Init()
	{
		dpchPDUSNList.clear();
		bchPDUSNList.clear();
		fachPDUSNList.clear();

		//bearer = 0;
		direction = 1; //目前只能解码下行
		
		rrcInfoFilter = false;
		tmsiFilter = 0;

		start = 0;

		hfn_um = 0;
		hfn_am = 0;

		hfn_tm = 0;
		doff = 0;
		sfn = 0;
		cfn = 0;

		trRbChMap.clear();
		trSizeMap.clear();

		isF8Ciphering = false;
		sibList.clear();

		rrcConnected = false;
		radioBearerSetup = false;
		radioBearerReconfiguration = false;
		bVoiceData = false;

		rrcConnectInfoList.clear();
		tmsiInfoList.clear();
		rrc_decode_info.clear();
		rrc_decode_info_simplify.clear();
	};

	void Reset()
	{
		rrcConnected = false;
		radioBearerSetup = false;
		radioBearerReconfiguration = false;
		bVoiceData = false;
		isF8Ciphering = false;
		f8CipheringActiveTimeList.clear();
	};

	void Close()
	{
		if (fdVoiceFile.is_open())
		{
			fdVoiceFile.close();
		}
	};

	// int: ch
	map<size_t, PDU_CH_SN_Data_t> dpchPDUSNList;
	map<size_t, PDU_CH_SN_Data_t> fachPDUSNList;
	map<size_t, PDU_CH_SN_Data_t> bchPDUSNList;

	int direction;
	
	bool rrcInfoFilter;
	uint32_t tmsiFilter;

	uint8_t ki[16];
	uint8_t opc[16];

	// start
	uint32_t start;

	// UM HFN
	uint32_t hfn_um;

	// AM HFN
	uint32_t hfn_am;

	// TM HFN
	uint32_t hfn_tm;

	// sfn->cfn
	uint32_t doff;
	uint32_t sfn;
	uint8_t cfn;

	map<uint32_t, uint32_t> trRbChMap;
	map<uint32_t, uint32_t> trSizeMap;

	// f8 ciphering
	bool isF8Ciphering;
	map<int, int> f8CipheringActiveTimeList;
	int cn_DomainIdentity;
	map<int, vector<uint8_t>> cn_Rand;
	map<int, vector<uint8_t>> cn_Domain_ck;

	// sib list
	vector<SIBInfo_t> sibList;

	// state
	bool rrcConnected;
	bool radioBearerSetup;
	bool radioBearerReconfiguration;

	//voice file
	bool bVoiceData;
	ofstream fdVoiceFile;
	vector<int8_t> buffVoice;
	bs_t bitsVoice;

	// decode info
	string rrc_decode_info;
	string rrc_decode_info_simplify;

	vector<RRCConnectInfo_t> rrcConnectInfoList;
	string tmsiInfoList;
};

static inline bool CheckHex(char c)
{
	if ((c >= 'a' && c <= 'f') ||
		(c >= 'A' && c <= 'F') ||
		(c >= '0' && c <= '9'))
	{
		return true;
	}
	return false;
}

static inline UINT Text2Data(LPCSTR dataType, LPCSTR text, BitBuffer_t& data)
{
	UINT nBitsCount = 0;
	int textLen = strlen(text);

	if (strstr(dataType, "Bits"))
	{
		data.Allocate((textLen + 7) / 8);
		memset(data.m_pBuffer, 0, data.m_nBufferSize);

		bs_s bs;
		bs_init(&bs, data.m_pBuffer, data.m_nBufferSize);

		int iByte = 0;

		for (INT i = 0; i < textLen; i++)
		{
			if (text[i] == '0')
			{
				bs_write(&bs, 1, 0);
				++nBitsCount;
			}
			else if (text[i] == '1')
			{
				//buff.m_pBuffer[nBitsCount / 8] |= (1 << (nBitsCount % 8));
				bs_write(&bs, 1, 1);
				++nBitsCount;
			}
		}

		data.m_nDataSize = (nBitsCount + 7) / 8;
		data.m_nLastSkipBits = data.m_nDataSize * 8 - nBitsCount;
	}
	else if (strstr(dataType, "Hex"))
	{
		data.Allocate(textLen / 2);

		for (INT i = 0; i < textLen; )
		{
			if (!CheckHex(text[i]) ||
				(i + 1) >= textLen ||
				!CheckHex(text[i + 1]))
			{
				++i;
				continue;
			}

			BYTE t;
			if (1 == sscanf(text + i, "%02hhx", &t))
			{
				data.m_pBuffer[nBitsCount / 8] = t;
				++data.m_nDataSize;
				nBitsCount += 8;
			}
			i += 2;
		}
	}
	return nBitsCount;
}

static inline UINT Text2Data(LPCTSTR dataType, LPCTSTR text, BitBuffer_t& data)
{
	return Text2Data(t2a(dataType).c_str(), t2a(text).c_str(), data);
}

static inline UINT Text2Data(const string& dataType, const string& text, BitBuffer_t& data)
{
	return Text2Data(dataType.c_str(), text.c_str(), data);
}

static inline string Data2Text(LPCSTR dataType, const uint8_t *pData, UINT nBitsCount)
{
	string msg;

	if (strstr(dataType, "Hex"))
	{
		char text[3] = { 0 };
		for (UINT i = 0; i < nBitsCount; )
		{
			_snprintf(text, 3, "%02hhX", pData[i / 8]);
			msg += text;
			i += 8;
			if (0 == (i % 256))
				msg += "\n";
			else if (0 == (i % 64))
				msg += "  ";
			else if (0 == (i % 8))
				msg += ' ';
		}
	}
	else if (strstr(dataType, "Bits"))
	{
		bs_s bs;
		bs_init(&bs, (void *)pData, (nBitsCount + 7) / 8);

		for (UINT i = 0; i < nBitsCount; )
		{
			if (bs_read1(&bs) > 0)
			{
				msg += '1';
			}
			else
			{
				msg += '0';
			}

			++i;
			if (0 == (i % 64))
				msg += "\n";
			else if (0 == (i % 8))
				msg += ' ';
		}
	}

	return msg;
}

static inline string Data2Text(LPCSTR dataType, const Buffer_t& data, UINT nBitsCount)
{
	return Data2Text(dataType, data.m_pData, nBitsCount);
}

static inline string Data2Text(const string& dataType, const Buffer_t& data, UINT nBitsCount)
{
	return Data2Text(dataType.c_str(), data, nBitsCount);
}

static inline string_t Data2Text(LPCTSTR dataType, const Buffer_t& data, UINT nBitsCount)
{
	return a2t(Data2Text(t2a(dataType), data, nBitsCount));
}

static inline int get_des_field_number(LPCSTR desField, const string& des)
{
	int i = des.find(desField);
	if (i < 0)
		return i;

	LPCSTR p = des.c_str() + i + strlen(desField);
	return atol(p);
}

static inline UINT32 bs_read_data(bs_t& bsR, bs_t& bsW, int nRead, UINT& nLen)
{
	UINT32 n = bs_read(&bsR, nRead);
	bs_write(&bsW, nRead, n);
	nLen += nRead;
	return n;
}

static inline string get_li_des(BYTE li)
{
	string text;

	s_sprintf(&text, "LI:%u ", li);

	if (li == 0x00)	/*0000000*/		// 第一个SDU开始标记
	{
		text += "noLI:0000000 ";
	}
	else if (li == 0x7C)	/*1111100*/		// 第一个SDU开始标记
	{
		text += "umdFirst:1111100 ";
	}
	else if (li == 0x7D		/*1111101*/) // 第一个SDU开始标记
	{
		text += "umdFirst:1111101 ";
	}
	else if (li == 0x7E		/*1111110*/)
	{
		text += "STATUS:1111101 ";
	}
	else if (li == 0x7F /*1111111*/)	// PAD内容,丢弃并结束
	{
		text += "PAD:1111111 ";
	}
	return text;
}

string decode_sms(Buffer_t& nasMessage);

string l3_decode_bch(BitBuffer_t& dataPDU, PDUData_t& pduInfo);

string l3_decode_fach(BitBuffer_t& dataPDU, PDUData_t& pduInfo);

string l3_decode_dpch(BitBuffer_t& dataPDU, PDUData_t& pduInfo);

string l3_decode_rrc(BYTE* pData, UINT nBitsCount, UINT nSkipBits, asn_TYPE_descriptor_t *asn_type_des, void **msg);

void ReadBitsFile(ifstream &fd, multimap<DWORD, PDULineInfo_t> &pduLineList);

void L3Decode(L3DecodeSys_t &sys, multimap<DWORD, PDULineInfo_t> &pduLineList);

#endif //_L3_DECODE_H_INCLUDE_

L3Decode.cpp

/*
* Ryan chen 2018
* Contact: 70565912@qq.com
*/

#include "Common.h"

#include "L3Decode.h"

#include "RRCDecode.h"
#include "kasumi.h"
#include "Milenage.h"

static void write_bits(bs_t &bs, uint8_t *pData, UINT bits)
{
	// 按位写数据
	for (int i_bits = 0; i_bits < bits; ++i_bits)
	{
		// 注意:bs_write是先写高位
		bs_write(&bs, 1, (pData[0] >> (7 - (i_bits % 8))));

		if (0 == ((i_bits + 1) % 8))
			++pData;
	}
}

string l3_decode_rrc(BYTE* pData, UINT nBitsCount, UINT nSkipBits, asn_TYPE_descriptor_t *asn_type_des, void **msg)
{
	string des;
	char text[MAX_PATH];
	char *p_name = NULL;
	long consumed = 0;
	char *p_des = 0;
	size_t des_size = 0;

	int ret = rrc_def_decode(pData, nBitsCount, nSkipBits, &p_name, &consumed, asn_type_des, msg, &p_des, &des_size);
	if (ret < 0)
	{
		_snprintf(text, MAX_PATH, "Broken decoding at byte %ld.\n", (long)consumed);
		return text;
	}

	if (ret > 0)
	{
		des = "Need more data.\n";
		return des;
	}

	if (p_des && des_size)
	{
		des.append(p_des, des_size);
		des += "\n";
		free(p_des);
	}

	return des;
}

static bool check_rrc_connect_release(L3DecodeSys_t &sys, DL_DCCH_Message *msg)
{
	if (msg->message.present == DL_DCCH_MessageType_PR_rrcConnectionRelease)
	{
#if 0  // 暂不做释放
		sys.Reset();
#endif
		return true;
	}
	return false;
}

static bool check_rrc_connect_release(L3DecodeSys_t &sys, DL_CCCH_Message *msg)
{
#if 0  // 暂不做释放
	if (msg->message.present == DL_CCCH_MessageType_PR_rrcConnectionRelease)
	{
		sys.rrcConnected = false;
		// f8加密结束
		sys.isF8Ciphering = false;
		sys.f8CipheringActiveTimeList.clear();
		return true;
	}
#endif 
	return false;
}

static void save_rrc_info(L3DecodeSys_t &sys, string &decodeInfo)
{
	vector<RRCConnectInfo_t> &infoList = sys.rrcConnectInfoList;	
	if (!infoList.size())
		return;

	RRCConnectInfo_t rrcInfo = (infoList.size() == 1) ? infoList.front() : infoList.back();
	
	char text[MAX_PATH];
	string tmsiInfo;

	BitBuffer_t tmsiData;
	tmsiData.FillData((LPBYTE)&rrcInfo.tmsi, 4);
	string bitsTextTMSI = Data2Text("Bits", tmsiData, 32);

	snprintf(text, MAX_PATH, "TMSI: %s ", bitsTextTMSI.c_str());
	tmsiInfo += text;

	snprintf(text, MAX_PATH, "DPCH-FrameOffset: %d ", rrcInfo.dpchFrameOffset);
	tmsiInfo += text;

	text[0] = 0;
	switch (rrcInfo.sf)
	{
	case SF512_AndCodeNumber_PR_sf4:
		snprintf(text, MAX_PATH, "SF4: %d\n", rrcInfo.codeNumber);
		break;
	case SF512_AndCodeNumber_PR_sf8:
		snprintf(text, MAX_PATH, "SF8: %d\n", rrcInfo.codeNumber);
		break;
	case SF512_AndCodeNumber_PR_sf16:
		snprintf(text, MAX_PATH, "SF16: %d\n", rrcInfo.codeNumber);
		break;
	case SF512_AndCodeNumber_PR_sf32:
		snprintf(text, MAX_PATH, "SF32: %d\n", rrcInfo.codeNumber);
		break;
	case SF512_AndCodeNumber_PR_sf64:
		snprintf(text, MAX_PATH, "SF64: %d\n", rrcInfo.codeNumber);
		break;
	case SF512_AndCodeNumber_PR_sf128:
		snprintf(text, MAX_PATH, "SF128: %d\n", rrcInfo.codeNumber);
		break;
	case SF512_AndCodeNumber_PR_sf256:
		snprintf(text, MAX_PATH, "SF256: %d\n", rrcInfo.codeNumber);
		break;
	case SF512_AndCodeNumber_PR_sf512:
		snprintf(text, MAX_PATH, "SF512: %d\n", rrcInfo.codeNumber);
		break;
	}
	tmsiInfo += text;

	// 文件保存
	ofstream fd;
	if (infoList.size() == 1)
		fd.open(RRC_TMSI_LIST_FILE_OUT_NAME, ios::out);
	else
		fd.open(RRC_TMSI_LIST_FILE_OUT_NAME, ios::out | ios::app);

	if (fd.is_open())
	{
		fd.write(tmsiInfo.c_str(), tmsiInfo.size());
	}

	// 列表保存
	sys.tmsiInfoList += tmsiInfo;

	// 保存日志
	decodeInfo += "RRCConnectionSetup :" + tmsiInfo;
}

static string get_sms_number(uint8_t* pData, int& idx, int len)
{
	string des;
	uint8_t n;
	char t[2];

	for (int i = 0; i < len * 2; i++)
	{
		if ((i % 2) == 0)
		{
			++idx;
			n = pData[idx] & 0x0F;
		}
		else
		{
			n = pData[idx] >> 4;
		}

		if (n == 0x0F)
			break;

		snprintf(t, 2, "%hhu", n);
		des += t;
	}
	++idx;

	return des;
}

string decode_sms(Buffer_t& nasMessage)
{
	int idx = 0;
	string smsText;

	if (nasMessage.m_nDataSize < 3)
		return smsText;

	if (nasMessage.m_pData[0] != 0x09 && nasMessage.m_pData[1] != 0x01)
		return smsText;

	uint8_t naslen = nasMessage.m_pData[2];
	if (nasMessage.m_nDataSize - 3 != naslen)
		return smsText;

	idx = 5;

	uint8_t dataLen = nasMessage.m_pData[idx++];
	smsText += "Number:";
	smsText += get_sms_number(nasMessage.m_pData, idx, dataLen);

	uint8_t rpD_len = nasMessage.m_pData[idx++];
	idx += rpD_len;

	uint8_t rpUser_len = nasMessage.m_pData[idx++];
	idx += 1;

	uint8_t numLen = nasMessage.m_pData[idx++];
	smsText += " Number:";
	smsText += get_sms_number(nasMessage.m_pData, idx, dataLen);
	idx += 2;

	uint8_t nYear = nasMessage.m_pData[idx++];
	uint8_t nMonth = nasMessage.m_pData[idx++];
	uint8_t nDay = nasMessage.m_pData[idx++];
	uint8_t nHour = nasMessage.m_pData[idx++];
	uint8_t nMinute = nasMessage.m_pData[idx++];
	uint8_t nSecond = nasMessage.m_pData[idx++];
	uint8_t nTimeZone = nasMessage.m_pData[idx++];

	uint8_t tp_udl = nasMessage.m_pData[idx++];

	uint8_t UDHL = nasMessage.m_pData[idx++];
	idx += UDHL;
	if (idx >= nasMessage.m_nDataSize)
		return smsText;

	int smsLen = tp_udl - 1 - UDHL;
	if (smsLen + idx != nasMessage.m_nDataSize)
		return smsText;

	//Win32是UTF-16LE,需要调换顺序
	vector<uint8_t> utf16le;
	utf16le.resize(smsLen + 2);
	memset(&utf16le[0], 0, smsLen + 2);
	for (int i = 0; i < smsLen / 2; i++)
	{
		utf16le[i * 2 + 1] = nasMessage.m_pData[idx++];
		utf16le[i * 2] = nasMessage.m_pData[idx++];
	}

	smsText += "\nSMS:";
	smsText += w2a((wchar_t *)&utf16le[0]);//UTF16LE转为窄字符编码

	return smsText;
}

static uint32_t check_rrc_connect_setup(L3DecodeSys_t &sys, DL_CCCH_Message *msg, string &decodeInfo)
{
	uint32_t tmsi = 0;
	long defaultDPCH_OffsetValue = 0;
	long dpch_FrameOffset = 0;
	long sf = 0;
	long codeNumber = 0;

	if (msg->message.present != DL_CCCH_MessageType_PR_rrcConnectionSetup)
		return 0;

	RRCConnectionSetup_t *rrcConnectionSetup = &msg->message.choice.rrcConnectionSetup;
	if (rrcConnectionSetup->present != RRCConnectionSetup_PR_r3)
		return 0;
	
	RRCConnectionSetup_r3_IEs_t *rrcConnectionSetup_r3 = &rrcConnectionSetup->choice.r3.rrcConnectionSetup_r3;

	InitialUE_Identity_t *initialUE_Identity = &rrcConnectionSetup_r3->initialUE_Identity;
	if (initialUE_Identity->present != InitialUE_Identity_PR_tmsi_and_LAI)
		return 0;
	
	TMSI_and_LAI_GSM_MAP_t *tmsi_and_LAI = &initialUE_Identity->choice.tmsi_and_LAI;
	if (tmsi_and_LAI->tmsi.size == 4)
	{
		tmsi = htonl(*(uint32_t*)tmsi_and_LAI->tmsi.buf);

		// 过滤掉非需要的tmsi连接消息
		if (sys.tmsiFilter && sys.tmsiFilter != tmsi)
		{
			return tmsi;
		}
	}

	// 获取rb id与tr id
	for (int iRb = 0; iRb < rrcConnectionSetup_r3->srb_InformationSetupList.list.count; ++iRb)
	{
		SRB_InformationSetup *srbInfo = rrcConnectionSetup_r3->srb_InformationSetupList.list.array[iRb];
		if (srbInfo->rb_MappingInfo.list.count > 0)
		{
			RB_MappingOption *rbOp = srbInfo->rb_MappingInfo.list.array[0];
			for (int iCh = 0; iCh < rbOp->dl_LogicalChannelMappingList->list.count; ++iCh)
			{
				DL_LogicalChannelMapping *dl_loChMap = rbOp->dl_LogicalChannelMappingList->list.array[iCh];
				if (dl_loChMap->logicalChannelIdentity)
				{
					// TS25.331 8.6.4.1
					// apply a default value of the IE "RB identity" equal to 1 for the first IE "Signalling RB information to setup"; 
					// and increase the default value by 1 for each occurrence.
					sys.trRbChMap[*dl_loChMap->logicalChannelIdentity] = iRb + 1; 
				}
			}
		}
	}

	DL_CommonInformation *dl_CommonInformation = rrcConnectionSetup_r3->dl_CommonInformation;
	if (dl_CommonInformation)
	{
		if (dl_CommonInformation->modeSpecificInfo.present == DL_CommonInformation__modeSpecificInfo_PR_fdd)
		{
			if (dl_CommonInformation->modeSpecificInfo.choice.fdd.defaultDPCH_OffsetValue)
			{
				//-- Actual value DefaultDPCH-OffsetValueFDD = IE value * 512 
				defaultDPCH_OffsetValue = *dl_CommonInformation->modeSpecificInfo.choice.fdd.defaultDPCH_OffsetValue * 512;
			}
		}
	}

	DL_InformationPerRL_List *dl_InformationPerRL_List = rrcConnectionSetup_r3->dl_InformationPerRL_List;
	if (dl_InformationPerRL_List)
	{
		for (int i = 0; i < dl_InformationPerRL_List->list.count; i++)
		{
			DL_InformationPerRL *info = dl_InformationPerRL_List->list.array[i];
			if (info->dl_DPCH_InfoPerRL->present == DL_DPCH_InfoPerRL_PR_fdd)
			{
				//-- Actual value DPCH-FrameOffset = IE value * 256
				dpch_FrameOffset = info->dl_DPCH_InfoPerRL->choice.fdd.dpch_FrameOffset * 256;

				if (info->dl_DPCH_InfoPerRL->choice.fdd.dl_ChannelisationCodeList.list.count)
				{
					struct DL_ChannelisationCode *dl_chSationCode = info->dl_DPCH_InfoPerRL->choice.fdd.dl_ChannelisationCodeList.list.array[0];
					sf = dl_chSationCode->sf_AndCodeNumber.present;
					codeNumber = dl_chSationCode->sf_AndCodeNumber.choice.sf128;
				}
			}
			
		}
	}
	
	/* TS25.331 8.6.6.14 
	1 > The UE shall :
		2 > if only one Radio Link is included in the message :
			3 > if the UE is configured for DPCH:
				4 > if (Default DPCH Offset Value) mod 38400 = DPCH frame offset :
					5 > set DOFF(see subclause 8.5.15.1) to Default DPCH Offset Value.
				4 > else:
					5 > set the variable INVALID_CONFIGURATION to TRUE.
			3 > if the UE is configured for F-DPCH:
				4 > if (Default DPCH Offset Value) mod 38400 = DPCH frame offset :
					5 > set DOFF(see subclause 8.5.15.1) to Default DPCH Offset Value.
				4 > else if (Default DPCH Offset Value + 256) mod 38400 = DPCH frame offset :
					5 > set DOFF(see subclause 8.5.15.1) to Default DPCH Offset Value + 256.
				4 > else:
					5 > set the variable INVALID_CONFIGURATION to TRUE.
	*/

	// F-DPCH
	if ((defaultDPCH_OffsetValue % 38400) == dpch_FrameOffset)
	{
		sys.doff = defaultDPCH_OffsetValue;
	}
	else if (((defaultDPCH_OffsetValue + 256) % 38400) == dpch_FrameOffset)
	{
		sys.doff = defaultDPCH_OffsetValue + 256;
	}

	sys.rrcConnected = true;

	RRCConnectInfo_t rrcInfo;
	rrcInfo.tmsi = htonl(tmsi);
	rrcInfo.dpchFrameOffset = dpch_FrameOffset / 256;
	rrcInfo.sf = sf;
	rrcInfo.codeNumber = codeNumber;
	sys.rrcConnectInfoList.push_back(rrcInfo);
	save_rrc_info(sys, decodeInfo);

	return tmsi;
}

static void check_radio_bearer_setup(L3DecodeSys_t &sys, DL_DCCH_Message *msg, string &decodeInfo)
{
	if (msg->message.present != DL_DCCH_MessageType_PR_radioBearerSetup)
		return;

	RadioBearerSetup_t *radioBearerSetup = &msg->message.choice.radioBearerSetup;

	if (radioBearerSetup->present != RadioBearerSetup_PR_r3)
		return;

	RadioBearerSetup_r3_IEs_t *radioBearerSetup_r3 = &radioBearerSetup->choice.r3.radioBearerSetup_r3;
	if (!radioBearerSetup_r3->rab_InformationSetupList)
		return;

	for (int iRab = 0; iRab < radioBearerSetup_r3->rab_InformationSetupList->list.count; iRab++)
	{
		struct RAB_InformationSetup *rabInfo = radioBearerSetup_r3->rab_InformationSetupList->list.array[iRab];

		for (int iRb = 0; iRb < rabInfo->rb_InformationSetupList.list.count; iRb++)
		{
			RB_InformationSetup *rbInfo = rabInfo->rb_InformationSetupList.list.array[iRb];
			long rb_id = rbInfo->rb_Identity;

			for (int iRbInfo = 0; iRbInfo < rbInfo->rb_MappingInfo.list.count; ++iRbInfo)
			{
				struct RB_MappingOption *rbMapInfo = rbInfo->rb_MappingInfo.list.array[iRbInfo];

				if (rbMapInfo->dl_LogicalChannelMappingList)
				{
					for (int iLogCh = 0; iLogCh < rbMapInfo->dl_LogicalChannelMappingList->list.count; ++iLogCh)
					{				
						DL_LogicalChannelMapping *loChMap = rbMapInfo->dl_LogicalChannelMappingList->list.array[iLogCh];
						if (loChMap->dl_TransportChannelType.present == DL_TransportChannelType_PR_dch)
						{
							long tr_id = loChMap->dl_TransportChannelType.choice.dch;

							sys.trRbChMap[tr_id] = rb_id;
						}
					}
				}
			}
		}

		if (!radioBearerSetup_r3->ul_AddReconfTransChInfoList)
			return;

		for (int iUlAddTrCh = 0; iUlAddTrCh < radioBearerSetup_r3->ul_AddReconfTransChInfoList->list.count; iUlAddTrCh++)
		{
			UL_AddReconfTransChInformation *ulReInfo = radioBearerSetup_r3->ul_AddReconfTransChInfoList->list.array[iUlAddTrCh];
			long tr_id = ulReInfo->transportChannelIdentity;

			if (ulReInfo->transportFormatSet.present == TransportFormatSet_PR_dedicatedTransChTFS)
			{
				DedicatedTransChTFS_t *dedicatedTransChTFS = &ulReInfo->transportFormatSet.choice.dedicatedTransChTFS;

				if (dedicatedTransChTFS->tti.present == DedicatedTransChTFS__tti_PR_tti20)
				{
					DedicatedDynamicTF_InfoList_t *tti20 = &dedicatedTransChTFS->tti.choice.tti20;
					for (int iTti = 0; iTti < tti20->list.count; iTti++)
					{
						DedicatedDynamicTF_Info *ddtInfo = tti20->list.array[iTti];

						int rlc_bit_size = 0;
						if (ddtInfo->rlc_Size.present == DedicatedDynamicTF_Info__rlc_Size_PR_bitMode)
						{
							rlc_bit_size = ddtInfo->rlc_Size.choice.bitMode.choice.sizeType1;
						}
						else
						{
							rlc_bit_size = ddtInfo->rlc_Size.choice.octetModeType1.choice.sizeType1 * 8;
						}

						sys.trSizeMap[rlc_bit_size] = tr_id;
					}
				}
			}
		}
	}

	sys.radioBearerSetup = true;
	sys.hfn_tm = 0;
	decodeInfo += "RadioBearerSetup\n";
}

static void check_radio_bearer_reconfiguration(L3DecodeSys_t &sys, DL_DCCH_Message *msg, string &decodeInfo)
{
	if (msg->message.present != DL_DCCH_MessageType_PR_radioBearerReconfiguration)
		return;

	sys.radioBearerReconfiguration = true;
	sys.hfn_tm = 0;
	decodeInfo += "RadioBearerReconfiguration :HFN begin counting.\n";
}

static void check_security_mode_command(L3DecodeSys_t &sys, DL_DCCH_Message *msg, string &decodeInfo)
{
	if (sys.isF8Ciphering) // 已经进入F8 ciphering状态,不用再check SecurityModeCommand消息
		return;

	if (msg->message.present == DL_DCCH_MessageType_PR_securityModeCommand)
	{
		if (msg->message.choice.securityModeCommand.present == SecurityModeCommand_PR_r3)
		{
			SecurityModeCommand_r3_IEs_t *securityModeCommand_r3 = &msg->message.choice.securityModeCommand.choice.r3.securityModeCommand_r3;
			if (securityModeCommand_r3->cipheringModeInfo)
			{
				CipheringModeInfo *cipheringModeInfo = securityModeCommand_r3->cipheringModeInfo;
				if (cipheringModeInfo->rb_DL_CiphActivationTimeInfo)
				{
					RB_ActivationTimeInfoList *rb_DL_CiphActivationTimeInfo = cipheringModeInfo->rb_DL_CiphActivationTimeInfo;
					sys.f8CipheringActiveTimeList.clear();

					for (int i = 0; i < rb_DL_CiphActivationTimeInfo->list.count; i++)
					{
						int rb_id = rb_DL_CiphActivationTimeInfo->list.array[i]->rb_Identity;
						int rlc_sn = rb_DL_CiphActivationTimeInfo->list.array[i]->rlc_SequenceNumber;

						sys.f8CipheringActiveTimeList[rb_id] = rlc_sn;
					}
				}

				// 当前作用域
				sys.cn_DomainIdentity = securityModeCommand_r3->cn_DomainIdentity;

				// 计算当前作用域的CK
				map<int, vector<uint8_t>>::iterator pos = sys.cn_Rand.find(sys.cn_DomainIdentity);
				if (pos != sys.cn_Rand.end())
				{
					//RES    RES = f2;
					BYTE RES[0x08] = { 0x00 };
					//IK     IK = f4;
					BYTE IK[0x10] = { 0x00 };
					//AK     AK = f5;
					BYTE AK[0x06] = { 0x00 };

					vector<uint8_t> ck;
					ck.resize(16);

					f2345(sys.opc, sys.ki, &pos->second[0], RES, &ck[0], IK, AK);

					sys.cn_Domain_ck[sys.cn_DomainIdentity] = ck;
				}

				// TS25.331 8.1.12.3
				sys.hfn_um = (sys.start << 5);	// 20位->25位 TS33.102 6.6.4 
				sys.hfn_am = (sys.start);		// 20位

				// f8加密开始
				sys.isF8Ciphering = true;
				decodeInfo += "SecurityModeCommand: Begin F8 Chiphering(Domain:" + (sys.cn_DomainIdentity > 0 ? string("PS") : string("CS")) + ").\n";
			}
		}
	}
}

static void check_downlink_direct_transfer(L3DecodeSys_t &sys, DL_DCCH_Message *msg, Buffer_t& nasMessage, string &decodeInfo)
{
	if (msg->message.present == DL_DCCH_MessageType_PR_downlinkDirectTransfer)
	{
		DownlinkDirectTransfer_t *downlinkDirectTransfer = &msg->message.choice.downlinkDirectTransfer;
		if (downlinkDirectTransfer->present == DownlinkDirectTransfer_PR_r3)
		{
			DownlinkDirectTransfer_r3_IEs_t *downlinkDirectTransfer_r3 = &downlinkDirectTransfer->choice.r3.downlinkDirectTransfer_r3;
			NAS_Message_t *nas_Message = &downlinkDirectTransfer_r3->nas_Message;
			int cn_DomainId = downlinkDirectTransfer_r3->cn_DomainIdentity;
			
			// 查找短信
			if (nas_Message->size > 0 && (nas_Message->buf[0] & 0xF) == 0x09) // SMS message
			{
				nasMessage.FillData(nas_Message->buf, nas_Message->size);
			}

			// 获取鉴权随机数
			if (nas_Message->size == 37 && nas_Message->buf[0] == 0x05 && nas_Message->buf[1] == 0x12)
			{
				//检查随机数
				vector<uint8_t> cn_Rand;
				cn_Rand.resize(16);
				memcpy(&cn_Rand[0], nas_Message->buf + 3, 16);
				sys.cn_Rand[cn_DomainId] = cn_Rand;

				decodeInfo += "DownlinkDirectTransfer: New Rand(Domain:" + (cn_DomainId > 0 ? string("PS") : string("CS")) + ").";

				// 收到新的鉴权key,在rrc已经连接或者SecurityModeCommand确认进入加密模式下,Start置0
				if (sys.rrcConnected || sys.isF8Ciphering)
				{
					// Start置0
					sys.start = 0;
					decodeInfo += "START set 0.";
				}
				decodeInfo += '\n';
			}
		}
	}
}

// 支持的解析类型才返回true
static bool set_sib_info(SIBInfo_t &sibInfo, long sib_Type, long sibCount, long sibIdx, uint8_t *buf, int size, int lastSkipBits)
{
	switch (sib_Type)
	{
	case SIB_Type_masterInformationBlock:
		sibInfo.sibDesType = &asn_DEF_MasterInformationBlock;
		break;
	case SIB_Type_systemInformationBlockType1:
		sibInfo.sibDesType = &asn_DEF_SysInfoType1;
		break;
	case SIB_Type_systemInformationBlockType3:
		sibInfo.sibDesType = &asn_DEF_SysInfoType3;
		break;
	case SIB_Type_systemInformationBlockType5:
		sibInfo.sibDesType = &asn_DEF_SysInfoType5;
		break;
	case SIB_Type_systemInformationBlockType7:
		sibInfo.sibDesType = &asn_DEF_SysInfoType7;
		break;
	case SIB_Type_systemInformationBlockType12:
		sibInfo.sibDesType = &asn_DEF_SysInfoType12;
		break;
	default:
		break;
	}

	sibInfo.sibType = sib_Type;
	sibInfo.sibCount = sibCount;
	sibInfo.sibData[sibIdx] = BitBuffer_t(buf, size, lastSkipBits);

	return sibInfo.sibDesType ? true : false;
}

static void check_bcch_message(L3DecodeSys_t &sys, BCCH_BCH_Message *msg)
{
	// for test fixed
	// 步长为2 -- Actual value SFN-Prime = 2 * IE value
	sys.sfn = msg->message.sfn_Prime * 2;

	// 计算CFN
	// TS25.331 8.5.15 CELL_DCH
	uint8_t cfn = ((sys.sfn - sys.doff  / 38400) % 256);

	if (cfn < sys.cfn)
	{
		// 溢出累计hfn
		++sys.hfn_tm;
	}

	sys.cfn = cfn; 
}

static string check_sib_message(L3DecodeSys_t &sys, BCCH_BCH_Message *msg)
{
	// sib组包
	switch (msg->message.payload.present)
	{
	case SystemInformation_BCH__payload_PR_completeSIB_List:
	{
		// 多个完整包
		CompleteSIB_List_t *payload = &msg->message.payload.choice.completeSIB_List;
		for (int i = 0; i < payload->list.count; i++)
		{
			CompleteSIBshort *completeSIBshort = payload->list.array[i];
			long sib_Type = completeSIBshort->sib_Type;
			uint8_t *buf = completeSIBshort->sib_Data_variable.buf;
			int size = completeSIBshort->sib_Data_variable.size;
			int lastSkipBits = completeSIBshort->sib_Data_variable.bits_unused;

			SIBInfo_t sibInfo;
			if (set_sib_info(sibInfo, sib_Type, 1, 0, buf, size, lastSkipBits))
			{
				sys.sibList.push_back(sibInfo);
			}
		}
		break;
	}
	case SystemInformation_BCH__payload_PR_firstSegment:
	case SystemInformation_BCH__payload_PR_subsequentSegment:
	case SystemInformation_BCH__payload_PR_lastSegmentShort:
	{
		switch (msg->message.payload.present)
		{
		case SystemInformation_BCH__payload_PR_firstSegment:
		{
			// 第一个包,新建
			FirstSegment_t *firstSegment = &msg->message.payload.choice.firstSegment;
			SIB_Data_fixed_t *sib_Data_fixed = &firstSegment->sib_Data_fixed;

			long sib_Type = firstSegment->sib_Type;
			uint8_t *buf = sib_Data_fixed->buf;
			int size = sib_Data_fixed->size;
			int count = firstSegment->seg_Count;
			int lastSkipBits = sib_Data_fixed->bits_unused;

			SIBInfo_t sibInfo;
			if (set_sib_info(sibInfo, sib_Type, count, 0, buf, size, lastSkipBits))
			{
				sys.sibList.push_back(sibInfo);
			}
			break;
		}
		case SystemInformation_BCH__payload_PR_subsequentSegment:
		{
			// 中间包,插入
			SubsequentSegment *subsequentSegment = &msg->message.payload.choice.subsequentSegment;
			SIB_Data_fixed_t *sib_Data_fixed = &subsequentSegment->sib_Data_fixed;

			long sib_Type = subsequentSegment->sib_Type;
			uint8_t *buf = sib_Data_fixed->buf;
			int size = sib_Data_fixed->size;
			int idx = subsequentSegment->segmentIndex;
			int lastSkipBits = sib_Data_fixed->bits_unused;

			for (size_t i = 0; i < sys.sibList.size(); i++)
			{
				if (sys.sibList[i].sibType == sib_Type)
				{
					sys.sibList[i].sibData[idx] = BitBuffer_t(buf, size, lastSkipBits);
					break;
				}
			}
			break;
		}
		case SystemInformation_BCH__payload_PR_lastSegmentShort:
		{
			// 结束包,插入
			LastSegmentShort_t *lastSegmentShort = &msg->message.payload.choice.lastSegmentShort;
			SIB_Data_variable_t *sib_Data_variable = &lastSegmentShort->sib_Data_variable;

			long sib_Type = lastSegmentShort->sib_Type;
			uint8_t *buf = sib_Data_variable->buf;
			int size = sib_Data_variable->size;
			int idx = lastSegmentShort->segmentIndex;
			int lastSkipBits = sib_Data_variable->bits_unused;

			for (size_t i = 0; i < sys.sibList.size(); i++)
			{
				if (sys.sibList[i].sibType == sib_Type)
				{
					sys.sibList[i].sibData[idx] = BitBuffer_t(buf, size, lastSkipBits);
					break;
				}
			}

			break;
		}
		default:
			break;
		}
		break;
	}
	default:
		break;
	}
	
	// sib解码
	string sibDes;
	for (vector<SIBInfo_t>::iterator itSibInfo = sys.sibList.begin(); itSibInfo != sys.sibList.end(); )
	{
		if (itSibInfo->sibCount == itSibInfo->sibData.size()) // 完成组包
		{
			// 获取数据字节长度
			int sibBitsLen = 0;
			for (map<int, BitBuffer_t>::iterator itSibData = itSibInfo->sibData.begin();
				itSibData != itSibInfo->sibData.end();
				++itSibData)
			{
				sibBitsLen += itSibData->second.GetDataBits();
			}

			// 初始化数据缓存
			Buffer_t sibData;
			bs_s bits;
			sibData.Allocate((sibBitsLen + 7) / 8);
			memset(sibData.m_pBuffer, 0, sibData.m_nBufferSize);
			bs_init(&bits, sibData.m_pBuffer, sibData.m_nBufferSize);

			// 循环组包
			for (map<int, BitBuffer_t>::iterator itSibData = itSibInfo->sibData.begin();
				itSibData != itSibInfo->sibData.end();
				++itSibData)
			{
				write_bits(bits, itSibData->second.m_pData, itSibData->second.GetDataBits());
			}

			// 解码sib
			string rrcDes = l3_decode_rrc(sibData.m_pData, sibBitsLen, 0, itSibInfo->sibDesType, NULL);
			if (rrcDes.size())
				sibDes += rrcDes;

			itSibInfo = sys.sibList.erase(itSibInfo);
		}
		else
			++itSibInfo;
	}

	return sibDes;
}

static void l3_decode_pdu(L3DecodeSys_t &sys, PDUData_t& info)
{
	char text[256];
	map<size_t, PDU_CH_SN_Data_t> *pduSNList = NULL;
	Buffer_t nasMessage;

	// 重发确认包不用解析
	if (info.isRepeatedSN)
		return;

	if (info.chType == CH_TYPE_BCH)
		pduSNList = &sys.bchPDUSNList;
	else if (info.chType == CH_TYPE_FACH)
		pduSNList = &sys.fachPDUSNList;
	else if (info.chType == CH_TYPE_DPCH)
		pduSNList = &sys.dpchPDUSNList;
	else
		return;

	for (map<uint32_t, PDU_CH_SN_Data_t>::iterator pCH = pduSNList->begin();
		pCH != pduSNList->end(); pCH++)
	{
		vector<BitBuffer_t>& pduDataList = pCH->second.pduDataList;
		for (UINT i = 0; i < pduDataList.size(); i++)
		{
			string textHex;
			string textRrc;
			string textSib;
			string decodeInfo;

			void *msg = 0;
			textHex = Data2Text("Hex", pduDataList[i], pduDataList[i].GetDataBits());
			textRrc = l3_decode_rrc(pduDataList[i].m_pData, pduDataList[i].GetDataBits(), 0, info.desType, &msg);

			if (msg && info.desType == &asn_DEF_BCCH_BCH_Message)
			{
				// 获取SFN
				 check_bcch_message(sys, (BCCH_BCH_Message *)msg);
				// 组包并解码sib
				textSib = check_sib_message(sys, (BCCH_BCH_Message *)msg);
			}

			if (msg && info.desType == &asn_DEF_DL_CCCH_Message)
			{
				// 检查释放f8加密状态
				check_rrc_connect_release(sys, (DL_CCCH_Message *)msg);

				// 检查释放f8加密状态,获取dpch-FrameOffset
				uint32_t tmsi = check_rrc_connect_setup(sys, (DL_CCCH_Message *)msg, decodeInfo);

				// TMSI过滤
				if (sys.tmsiFilter && tmsi)
				{
					if (sys.tmsiFilter != tmsi)
					{
						textRrc = "<RRCConnectionSetup> TMSI filter, Can be to ignore...\n";
					}
				}
			}

			nasMessage.ClearData();
			if (msg && info.desType == &asn_DEF_DL_DCCH_Message)
			{
				// 获取鉴权随机数
				check_downlink_direct_transfer(sys, (DL_DCCH_Message *)msg, nasMessage, decodeInfo);

				// 检查释放f8加密状态
				check_rrc_connect_release(sys, (DL_DCCH_Message *)msg);

				// 检查进入加密状态
				check_security_mode_command(sys, (DL_DCCH_Message *)msg, decodeInfo);

				// 获取RB通道配置
				check_radio_bearer_setup(sys, (DL_DCCH_Message *)msg, decodeInfo);

				// 获取RB通道重配置
				check_radio_bearer_reconfiguration(sys, (DL_DCCH_Message *)msg, decodeInfo);
			}

			if (info.desType && msg)
				ASN_STRUCT_FREE(*info.desType, msg);

			// 过滤RRC信息
			if (sys.rrcInfoFilter)
			{
#define FILTER_RRC_TEXT(t)\
				if (t.size()){\
					int split = t.find('\n');\
					if (split > 0)\
						t.erase(split, t.size() - 1 - split);\
				}\

				FILTER_RRC_TEXT(textRrc);
				FILTER_RRC_TEXT(textSib);
			}

			// 输出明文和RRC解码消息
			if (textHex.size())
			{
				sys.rrc_decode_info += "RRC:" + textHex + '\n' + textRrc;
			}

			// 输出SIB解码消息
			if (textSib.size())
			{
				sys.rrc_decode_info += "SIB:" + textSib;
			}

			// 输出BCH的SFN日志
			if (info.desType == &asn_DEF_BCCH_BCH_Message)
			{
				snprintf(text, 256, "SFN-Prime:%u CFN:%hhu HFN:%u\n", sys.sfn, sys.cfn, sys.hfn_tm);
				sys.rrc_decode_info += text;
			}
			
			// 输出短消息的NASMessage
			if (nasMessage.m_nDataSize)
			{
				string sms = decode_sms(nasMessage);
				sys.rrc_decode_info += "NASMessage:" + Data2Text("Hex", nasMessage.m_pData, nasMessage.m_nDataSize * 8) + "\n";
				if (sms.size())
				{
					decodeInfo += "SMSMessage:\n" + sms + '\n';
				}
			}

			// 记录解码日志
			sys.rrc_decode_info += decodeInfo;
			sys.rrc_decode_info_simplify += decodeInfo;
		}

		// 解析后即清理
		pduDataList.clear();
	}

	return;
}

static void f8_ciphering(L3DecodeSys_t &sys, PDUData_t& pduInfo, int ch, int sn)
{
	if (!sys.isF8Ciphering)
		return;
	
	// F8解密
	pduInfo.dataF8PDU = pduInfo.dataPDU;

	// 查找解密作用域对应的CK
	map<int, vector<uint8_t>>::iterator pCK = sys.cn_Domain_ck.find(sys.cn_DomainIdentity);
	if (pCK == sys.cn_Domain_ck.end())
		return;
	vector<uint8_t> ck = pCK->second;
	
	// AM/UM模式,Bearer使用逻辑通道号查询rb id并-1
	// TS25.331 8.6.4.3
	// start to perform ciphering on the radio bearer in lower layers, using the value of the IE "RB identity" minus
	// one as the value of BEARER in the ciphering algorithm.
	int bearer = sys.trRbChMap[ch] - 1; 

	// pduInfo.modeRLC == "AM"	
	int c = (sys.hfn_am << 12); // HFN 20位
	c |= (sn & 0xFFF);			// RLC SN 12位

	if (pduInfo.modeRLC == "UM")
	{
		c = (sys.hfn_um << 7);	// HFN 25位
		c |= (sn & 0x7F);		// RLC SN 7位
	}
	else if (pduInfo.modeRLC == "TM")
	{
		// 提前到来的语音数据会干扰hfn起始值计算
		if (!sys.radioBearerSetup && !sys.radioBearerReconfiguration)
			return;

		if (!sys.bVoiceData)
		{
			if (!sys.radioBearerReconfiguration) 
				sys.hfn_tm -= 1;	// ??? fixed it,目前可能拿不到RADIO BEARER RECONFIGURATION消息,暂用-1方案
			sys.hfn_tm |= ((sys.start + 2) << 4); // TM模式下HFN 24位,取START 20位在高位
			sys.bVoiceData = true;
		}

		c = sys.hfn_tm << 8;	// HFN 24位
		c |= (sys.cfn & 0xFF);	// CFN 8位

		// 根据数据大小查询逻辑通道id
		long tr_id = sys.trSizeMap[pduInfo.dataF8PDU.GetDataBits()];
		// 使用逻辑通道id查询无线承载id
		long rb_id = sys.trRbChMap[tr_id];
		// Bearer使用查询的无线承载id并-1
		// TS25.331 8.6.4.3
		// start to perform ciphering on the radio bearer in lower layers, using the value of the IE "RB identity" minus
		// one as the value of BEARER in the ciphering algorithm.
		bearer = rb_id - 1;
	}

	// 生成f8明文
	f8(&ck[0], c, bearer, sys.direction,
		pduInfo.dataF8PDU.m_pData, pduInfo.dataF8PDU.GetDataBits());
}

static void save_voice_file(L3DecodeSys_t &sys, PDUData_t& pduInfo)
{
	BitBuffer_t &f8Data = pduInfo.dataF8PDU;

	if (!f8Data.m_nDataSize)
		return;

	if (!sys.fdVoiceFile.is_open())
	{
		sys.fdVoiceFile.open(VOICE_FILE_OUT_NAME, ios::out | ios::binary);

		if (!sys.fdVoiceFile.is_open())
			return;

		sys.fdVoiceFile.write("#!AMR\n", 6);
		sys.rrc_decode_info_simplify += "Begin Save Voice AMR Data.\n";
	}
	
	if (f8Data.GetDataBits() == 81)
	{
		int frameLen = 1 + (244 + 7) / 8;

		// 重置buff
		sys.buffVoice.resize(frameLen);
		memset(&sys.buffVoice[0], 0, frameLen);
		bs_init(&sys.bitsVoice, &sys.buffVoice[0], frameLen);

		// 设置头部
		uint8_t amrHeader = 0x3C;
		bs_write(&sys.bitsVoice, 8, amrHeader);
	}

	// 设置数据
	write_bits(sys.bitsVoice, f8Data.m_pData, f8Data.GetDataBits());

	if (f8Data.GetDataBits() == 60)
	{
		// 写入文件
		sys.fdVoiceFile.write((const char *)&sys.buffVoice[0], sys.buffVoice.size());
	}
}

static int l3_combine_pdu(L3DecodeSys_t &sys, PDUData_t& pduInfo)
{
	char text[256];
	// int: ch
	map<size_t, PDU_CH_SN_Data_t> *pduSNList = NULL;

	if (pduInfo.chType == CH_TYPE_BCH)
		pduSNList = &sys.bchPDUSNList;
	else if (pduInfo.chType == CH_TYPE_FACH)
		pduSNList = &sys.fachPDUSNList;
	else if (pduInfo.chType == CH_TYPE_DPCH)
		pduSNList = &sys.dpchPDUSNList;
	else
		return -1;

	// TM不参与组包流程
	if (pduInfo.modeRLC == "TM")
	{
		// 语音数据需要F8解密,不参与解包
		if (pduInfo.chType == CH_TYPE_DPCH)
		{
			f8_ciphering(sys, pduInfo, -1, -1);
			return 0;
		}

		// TM数据直接参与解包
		if (pduInfo.dataF8PDU.m_nDataSize)
			(*pduSNList)[0].pduDataList.push_back(pduInfo.dataF8PDU);
		else
			(*pduSNList)[0].pduDataList.push_back(pduInfo.dataPDU);
		return 0;
	}
	
	// 控制报文不参与组包流程
	if (pduInfo.desRLC.find("ControlType") != -1)
	{
		return 0;
	}

	int ch = -1;
	int sn = -1;
	
	// 没有逻辑通道的数据在-1通道上组包
	ch = get_des_field_number("LogicalChannel:", pduInfo.desMAC);

	// 必须有序号才能按序组包
	sn = get_des_field_number("SequenceNumber:", pduInfo.desRLC);
	if (sn < 0)
		return -1;

	int ret = -1;
	do
	{
		if (sn > -1 && sn == (*pduSNList)[ch].sn)
		{
			// 重复包不参与组包流程
			pduInfo.isRepeatedSN = true;
			snprintf(text, 256, "Repeated SequenceNumber(LogicalChannel:%d SN:%d).\n", ch, sn);
			sys.rrc_decode_info += text;
			return 0;
		}

		// 检查组包序号
		if (0 == sn)
		{
			// 重新开始组包
			(*pduSNList)[ch].data.ClearData();
		}
		else if (sn != (*pduSNList)[ch].sn + 1)
		{
			// 非连续包
			(*pduSNList)[ch].data.ClearData();

			snprintf(text, 256, "SN is not continuity(LogicalChannel:%d SN:%d->%d).Restart combine PDU.\n",
				ch, sn, (*pduSNList)[ch].sn + 1);
			sys.rrc_decode_info += text;
		}
		(*pduSNList)[ch].sn = sn;

		// F8加密状态
		if (pduInfo.desType == &asn_DEF_DL_DCCH_Message)
		{
			// ch 1 - 4
			if (ch > -1 && (int)sys.f8CipheringActiveTimeList.size() >= ch && sn >= sys.f8CipheringActiveTimeList[ch])
			{
				f8_ciphering(sys, pduInfo, ch, sn);
			}
		}

		BYTE* pData = (pduInfo.dataF8PDU.m_nDataSize) ? pduInfo.dataF8PDU.m_pData : pduInfo.dataPDU.m_pData;
		int dataLen = pduInfo.dataPDU.m_nDataSize;

		// 序号连续,进入组包
		//int p = get_des_field_number(_T("P:"), pduInfo.desRLC);
		int e = get_des_field_number("E:", pduInfo.desRLC);

		// 有长度指示器
		if (e > 0)
		{
			// 循环读取长度指示器
			vector<uint8_t> liLenList;
			for (int i = 0; i < dataLen; i++)
			{
				BYTE liLen = pData[i] >> 1;
				bool e = (pData[i] & 0x1) > 0 ? true : false;
				liLenList.push_back(liLen);

				if (!e) // 结束
					break;
			}

			// 数据长度有问题????
			if (liLenList.size() == 0)
			{
				snprintf(text, 256, "Length Indicator count(%u) is Zero.\n", liLenList.size());
				sys.rrc_decode_info += text;
				break;
			}

			// 长度指示器目前观察不会超过3个
			if (liLenList.size() > 3)
			{
				snprintf(text, 256, "Length Indicator count(%u) is too more.\n", liLenList.size());
				sys.rrc_decode_info += text;
				break;
			}

			if (pduInfo.modeRLC == "UM")
			{
				// UM长度指示器为1111100时为数据包开始
				if (liLenList[0] == 0x7C)
				{
					int nLen = dataLen - liLenList.size();
					(*pduSNList)[ch].data.FillData(pData + liLenList.size(), nLen);

					// 有第2段长度指示器,使用指定的长度结束
					if (liLenList.size() > 1 && liLenList[1] <= nLen)
					{
						(*pduSNList)[ch].data.m_nDataSize = liLenList[1];
						(*pduSNList)[ch].pduDataList.push_back((*pduSNList)[ch].data);
						(*pduSNList)[ch].data.ClearData();
					}
				}
				// UM长度指示器为0时数据包结束
				else if (liLenList[0] == 0)
				{
					(*pduSNList)[ch].pduDataList.push_back((*pduSNList)[ch].data);
					(*pduSNList)[ch].data.ClearData();
				}
			}
			else
			{
				// 长度指示器分段
				int li1 = (liLenList[0] > dataLen - liLenList.size()) ? 0 : liLenList[0];
				int li2 = (liLenList.size() > 1) ? liLenList[1] : 0;

				// AM第一个长度指示器指示当前包结束位置
				(*pduSNList)[ch].data.AppendData(pData + liLenList.size(), li1);
				(*pduSNList)[ch].pduDataList.push_back((*pduSNList)[ch].data);
				(*pduSNList)[ch].data.ClearData();

				int nLen2 = dataLen - liLenList.size() - li1;
				if (li2 != 0x7F) // 1111111
				{
					// 记录后半截数据整段数,不包括pading
					(*pduSNList)[ch].data.FillData(pData + liLenList.size() + li1, nLen2);
				}

				// 有正确的第二个长度指示器
				if (li2 > 0 && li2 <= nLen2)
				{
					// 第二个包结束,使用li2长度重新记录后半截数据
					(*pduSNList)[ch].data.m_nDataSize = li2;
					(*pduSNList)[ch].pduDataList.push_back((*pduSNList)[ch].data);
					(*pduSNList)[ch].data.ClearData();
				}
			}
		}
		else
		{
			// 没有长度指示器,整段数据纳入当前包
			(*pduSNList)[ch].data.AppendData(pData, dataLen);
		}

		ret = 0;

	} while (false);
	return ret;
}

static void l3_decode_rlc(string& desRLC, const string& pdu_mode, bs_t& bs, bs_t& bsRLC, UINT& lenRLC)
{
	char text[MAX_PATH];

	// 判断MAC Header
	if (pdu_mode == "UM")
	{
		//读取RLC Sequence Number和E结束标志
		UINT16 sn = bs_read_data(bs, bsRLC, 7, lenRLC);
		BYTE e = bs_read_data(bs, bsRLC, 1, lenRLC);

		_snprintf(text, MAX_PATH, "SequenceNumber:%hu E:%C", sn, (e > 0) ? '1' : '0');
		desRLC += text;

#if 0
		// 循环读取Length Indicator
		while (e)
		{
			BYTE li = bs_read_data(bs, bsRLC, 7, lenRLC);
			e = bs_read_data(bs, bsRLC, 1, lenRLC);

			fachInfo.desRLC += get_li_des(li);
		}
#endif
	}
	else if (pdu_mode == "AM")
	{
		BYTE dc = bs_read_data(bs, bsRLC, 1, lenRLC);

		if (dc > 0)
		{
			// Data PDU
			UINT sn = bs_read_data(bs, bsRLC, 12, lenRLC);
			UINT p = bs_read_data(bs, bsRLC, 1, lenRLC);
			UINT he = bs_read_data(bs, bsRLC, 2, lenRLC);
			UINT e = (he & 0x1);

			_snprintf(text, MAX_PATH, "SequenceNumber:%u P:%C HE:%x ", sn, (p > 0) ? '1' : '0', he);
			desRLC += text;
#if 0
			while (e)
			{
				if (bs.p >= bs.p_end)
					break;

				BYTE li = bs_read_data(bs, bsRLC, 7, lenRLC);
				e = bs_read_data(bs, bsRLC, 1, lenRLC);

				fachInfo.desRLC += get_li_des(li);
			}
#endif
		}
		else
		{
			// Control PDU
			UINT pdu_type = bs_read_data(bs, bsRLC, 3, lenRLC);

			BYTE sufi_type = 0;
			BYTE sufi_len = 0;
			UINT16 sufi_sn = 0;

			switch (pdu_type)
			{
			case 0:
				desRLC += " ControlType:STATUS";

				sufi_type = bs_read_data(bs, bsRLC, 4, lenRLC);
				if (!sufi_type)
				{
					strcpy(text, " NO_MORE");
					desRLC += text;
				}

				while (sufi_type)
				{
					if (sufi_type == 1)
					{
						//0001 Window Size(WINDOW)
						sufi_sn = bs_read_data(bs, bsRLC, 12, lenRLC);
						_snprintf(text, MAX_PATH, " Window:%hu", sufi_sn);
						desRLC += text;

					}
					else if (sufi_type == 2)
					{
						//0010 Acknowledgement(ACK)
						sufi_sn = bs_read_data(bs, bsRLC, 12, lenRLC);
						_snprintf(text, MAX_PATH, " ACK:%hu", sufi_sn);
						desRLC += text;
					}
					else if (sufi_type == 3)
					{
						//0011 List(LIST)
						sufi_len = bs_read_data(bs, bsRLC, 4, lenRLC);
						_snprintf(text, MAX_PATH, " LIST:%hhu", sufi_len);
						desRLC += text;

#if 0
						for (BYTE i = 0; i < sufi_len; i++)
						{
							UINT16 list_sni = bs_read_data(bs, bsRLC, 12, lenRLC);
							BYTE list_li = bs_read_data(bs, bsRLC, 4, lenRLC);
							_snprintf(text, MAX_PATH, "[%hu:%hhu]", list_sni, list_li);
							desRLC += text;
						}
#endif
					}
					else if (sufi_type == 4)
					{
						//0100 Bitmap(BITMAP)
						sufi_len = bs_read_data(bs, bsRLC, 4, lenRLC);
						_snprintf(text, MAX_PATH, " BITMAP:%hhu", sufi_len);
						desRLC += text;

#if 0
						for (BYTE i = 0; i < sufi_len; i++)
						{
							UINT16 bmp_fsn = bs_read_data(bs, bsRLC, 12, lenRLC);
							bs_read_data(bs, bsRLC, bmp_fsn, lenRLC);

							_snprintf(text, MAX_PATH, "[%hu]", bmp_fsn);
							desRLC += text;
						}
#endif
					}
					else if (sufi_type == 5)
					{
						//0101 Relative list(Rlist)
						sufi_len = bs_read_data(bs, bsRLC, 4, lenRLC);
						sufi_sn = bs_read_data(bs, bsRLC, 12, lenRLC);
						_snprintf(text, MAX_PATH, " Rlist:0x%hhu %hu", sufi_len, sufi_sn);
						desRLC += text;

#if 0
						for (BYTE i = 0; i < sufi_len; i++)
						{
							BYTE rlist_cw = bs_read_data(bs, bsRLC, 4, lenRLC);
							_snprintf(text, MAX_PATH, "[%hhu]", rlist_cw);
							desRLC += text;
						}
#endif
					}
					else if (sufi_type == 6)
					{

						//0110 Move Receiving Window(MRW)
						sufi_len = bs_read_data(bs, bsRLC, 4, lenRLC);
						sufi_sn = bs_read_data(bs, bsRLC, 12, lenRLC);
						_snprintf(text, MAX_PATH, " MRW:0x%hhu %hu", sufi_len, sufi_sn);
						desRLC += text;
					}
					else if (sufi_type == 7)
					{
						//0111 Move Receiving Window Acknowledgement (MRW_ACK)
						sufi_len = bs_read_data(bs, bsRLC, 4, lenRLC);
						_snprintf(text, MAX_PATH, " MRW_ACK:0x%hhx", sufi_len);
						desRLC += text;

#if 0
						for (BYTE i = 0; i < sufi_len; i++)
						{
							UINT16 mrw_ack_sn = bs_read_data(bs, bsRLC, 12, lenRLC);
							_snprintf(text, MAX_PATH, "[%hu]", mrw_ack_sn);
							desRLC += text;
						}

						for (BYTE i = 0; i < sufi_len; i++)
						{
							BYTE mrw_ack_len = bs_read_data(bs, bsRLC, 4, lenRLC);
							_snprintf(text, MAX_PATH, "[%hu]", mrw_ack_len);
							desRLC += text;
						}
#endif
					}
					else if (sufi_type == 8)
					{
						//1000 Poll(POLL)
						sufi_sn = bs_read_data(bs, bsRLC, 12, lenRLC);
						_snprintf(text, MAX_PATH, " POLL:%hu", sufi_sn);
						desRLC += text;
					}
					else
						break;

					if (bs_eof(&bs))
						break;

					sufi_type = bs_read_data(bs, bsRLC, 4, lenRLC);
				}

				break;
			case 1:
				desRLC += " ControlType:RESET";
				break;
			case 2:
				desRLC += " ControlType:RESET ACK";
				break;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
string l3_decode_bch(BitBuffer_t& dataPDU, PDUData_t& pduInfo)
{
	char text[MAX_PATH];
	string des;

	if (dataPDU.GetDataBits() < DATA_LENGTH_BCH)
	{
		_snprintf(text, MAX_PATH, "Data length(%u-%u bits) is too short.", dataPDU.GetDataBits(), DATA_LENGTH_BCH);
		return text;
	}

	pduInfo.desType = &asn_DEF_BCCH_BCH_Message;
	pduInfo.desRLC = "Mode:TM";
	pduInfo.modeRLC = "TM";
	pduInfo.dataPDU = dataPDU;

	dataPDU.Free();

	return des;
}

string l3_decode_fach(BitBuffer_t& dataPDU, PDUData_t& pduInfo)
{
	char text[MAX_PATH];
	string des;

	if (dataPDU.GetDataBits() < DATA_LENGTH_FACH)
	{
		_snprintf(text, MAX_PATH, "Data length(%u bits) is too short.", dataPDU.GetDataBits());
		return text;
	}

	bs_t bs;
	bs_init(&bs, dataPDU.m_pData, dataPDU.m_nDataSize);

	pduInfo.dataMAC.Allocate(5);
	memset(pduInfo.dataMAC.m_pBuffer, 0, 5);
	bs_t bsMAC;
	bs_init(&bsMAC, pduInfo.dataMAC.m_pBuffer, 5);

	pduInfo.dataRLC.Allocate(20);
	memset(pduInfo.dataRLC.m_pBuffer, 0, 20);
	bs_t bsRLC;
	bs_init(&bsRLC, pduInfo.dataRLC.m_pBuffer, 20);

	UINT ct = 0;
	string rlcMode = "TM";

	UINT nMACBits = 0;

	// BCCH
	if ((dataPDU.m_pData[0] >> 6) == 0x00)
	{
		pduInfo.desType = &asn_DEF_BCCH_FACH_Message;
		pduInfo.desMAC += pduInfo.desType->name;

		// skip TCTF
		bs_read_data(bs, bsMAC, 2, nMACBits);
	}
	// CCCH
	else if (dataPDU.m_pData[0] == 0x40)
	{
		pduInfo.desType = &asn_DEF_DL_CCCH_Message;
		pduInfo.desMAC += pduInfo.desType->name;

		// skip TCTF
		bs_read_data(bs, bsMAC, 8, nMACBits);

		rlcMode = "UM";
	}
	// MCCH
	else if (dataPDU.m_pData[0] == 0x50)
	{
		pduInfo.desType = &asn_DEF_MCCH_Message;
		pduInfo.desMAC += pduInfo.desType->name;

		// skip TCTF
		bs_read_data(bs, bsMAC, 8, nMACBits);
	}
	// MSCH
	else if (dataPDU.m_pData[0] == 0x5F)
	{
		pduInfo.desType = &asn_DEF_MCCH_Message;
		pduInfo.desMAC += pduInfo.desType->name;

		// skip TCTF
		bs_read_data(bs, bsMAC, 8, nMACBits);
	}
	// MTCH
	else if ((dataPDU.m_pData[0] >> 4) == 0x6)
	{
		//pduInfo.desType = &asn_DEF_MTCH_Message;
		pduInfo.desMAC += "MTCH";//pduInfo.desType->name;

		// skip TCTF
		bs_read_data(bs, bsMAC, 2, nMACBits);
	}
	// CTCH
	else if (dataPDU.m_pData[0] == 0x80)
	{
		//pduInfo.desType = &asn_DEF_CTCH_Message;
		pduInfo.desMAC += "CTCH";//pduInfo.desType->name;

		// skip TCTF
		bs_read_data(bs, bsMAC, 8, nMACBits);
	}
	// DCCH/DTCH voer FACH
	else if ((dataPDU.m_pData[0] >> 6) == 0x3)
	{
		UINT32 n;

		// skip TCTF
		bs_read_data(bs, bsMAC, 2, nMACBits);

		// skip UI-Type
		n = bs_read_data(bs, bsMAC, 2, nMACBits);

		// skip UE-Id
		string ui_type;
		UINT32 ui_id = 0;
		if (n > 0)
		{
			// C-RNTI
			ui_id = bs_read_data(bs, bsMAC, 16, nMACBits);
			ui_type = "C-RNIT";
		}
		else
		{
			// U-RNTI
			ui_id = bs_read_data(bs, bsMAC, 32, nMACBits);
			ui_type = "U-RNIT";
		}

		// skip C/T
		ct = bs_read_data(bs, bsMAC, 4, nMACBits);

		// C/T决定PDU Type
		rlcMode = "AM";
		if (ct > 14)
		{
			//pduInfo.desType = &asn_DEF_DL_DCCH_Message; // DTCH ??
			pduInfo.desMAC += "DTCH";
			rlcMode = "TM";
		}
		else
		{
			pduInfo.desType = &asn_DEF_DL_DCCH_Message;
			pduInfo.desMAC += pduInfo.desType->name;

			//if (ct < 1)
			//	pdu_mode = _T("UM");
			//else
			rlcMode = "AM";
		}


		_snprintf(text, MAX_PATH, " %s UE-Id:%u ", ui_type.c_str(), ui_id);
		pduInfo.desMAC += text;

		_snprintf(text, MAX_PATH, "LogicalChannel:%u ", ct + 1);
		pduInfo.desMAC += text;
	}
	else
	{
		pduInfo.desType = 0;
		pduInfo.desMAC += "Unknow TCTF";
	}

	if (pduInfo.desType)
	{
		_snprintf(text, MAX_PATH, "Mode:%s ", rlcMode.c_str());
		pduInfo.desRLC += text;
	}

	pduInfo.dataMAC.m_nDataSize = (nMACBits + 7) / 8;
	pduInfo.modeRLC = rlcMode;

	// 解码RLC
	UINT nRLCBits = 0;
	s_sprintf(&pduInfo.desRLC, "Mode:%s ", rlcMode.c_str());
	l3_decode_rlc(pduInfo.desRLC, rlcMode, bs, bsRLC, nRLCBits);

	pduInfo.dataRLC.m_nDataSize = (nRLCBits + 7) / 8;

	BYTE byteRead;
	int dataBits = DATA_LENGTH_FACH - nMACBits - nRLCBits;
	while (dataBits > 0)
	{
		int nRead = (dataBits >= 8) ? 8 : dataBits;
		byteRead = bs_read(&bs, nRead);
		pduInfo.dataPDU.AppendData(&byteRead, 1);
		dataBits -= nRead;
	}

	dataPDU.Free();

	return des;
}

string l3_decode_dpch(BitBuffer_t& dataPDU, PDUData_t& pduInfo)
{
	string des;
	char text[MAX_PATH];

	if (dataPDU.GetDataBits() < 8)
	{
		_snprintf(text, MAX_PATH, "Data length(%u bits) is too short.", dataPDU.GetDataBits());
		dataPDU.ClearData();
		return text;
	}

	pduInfo.dataMAC.Allocate(1);
	memset(pduInfo.dataMAC.m_pBuffer, 0, 1);

	bs_t bsMAC;
	bs_init(&bsMAC, pduInfo.dataMAC.m_pBuffer, 1);

	pduInfo.dataRLC.Allocate(20);
	memset(pduInfo.dataRLC.m_pBuffer, 0, 20);
	bs_t bsRLC;
	bs_init(&bsRLC, pduInfo.dataRLC.m_pBuffer, 20);

	bs_t bs;
	bs_init(&bs, dataPDU.m_pData, dataPDU.m_nDataSize);

	UINT ct = 0;
	UINT nMACBits = 0;
	UINT nRLCBits = 0;

	if (dataPDU.GetDataBits() != DATA_LENGTH_DPCH)
	{
		// 通过长度判断语音数据
		pduInfo.modeRLC = "TM";
	}
	else
	{
		// C/T
		ct = bs_read_data(bs, bsMAC, 4, nMACBits);
		s_sprintf(&pduInfo.desMAC, "LogicalChannel:%u ", ct + 1);

		// 信令数据,C/T决定PDU Type
		if (ct > 0)
		{
			pduInfo.modeRLC = "AM";
		}
		else
		{
			pduInfo.modeRLC = "UM";
		}

		// DCCH/DTCH
		pduInfo.desType = &asn_DEF_DL_DCCH_Message;
		pduInfo.dataMAC.m_nDataSize = (nMACBits + 7) / 8;

		// 解码RLC
		s_sprintf(&pduInfo.desRLC, "Mode:%s ", pduInfo.modeRLC.c_str());
		l3_decode_rlc(pduInfo.desRLC, pduInfo.modeRLC, bs, bsRLC, nRLCBits);
		pduInfo.dataRLC.m_nDataSize = (nRLCBits + 7) / 8;
	}
	
	BYTE byteRead;
	int nDataBits = dataPDU.GetDataBits() - nMACBits - nRLCBits;
	while (nDataBits > 0)
	{
		int nRead = (nDataBits >= 8) ? 8 : nDataBits;
		byteRead = bs_read(&bs, nRead);
		pduInfo.dataPDU.AppendData(&byteRead, 1);
		nDataBits -= nRead;
	}
	pduInfo.dataPDU.m_nLastSkipBits = dataPDU.m_nLastSkipBits;

	dataPDU.Free();

	return des;
}

void ReadBitsFile(ifstream &fd, multimap<DWORD, PDULineInfo_t> &pduLineList)
{
	string line;
	uint32_t idx;
	DWORD tcCode = 0;
	char lastCRC = '0';

	while (getline(fd, line))
	{
		if (line.size() > 1024)
			continue;

		PDULineInfo_t lineInfo;

		// 读取时码
		idx = line.find("cfn:");
		if (idx != line.npos)
		{
			sscanf(line.c_str() + idx + strlen("cfn:"), " %u ", &tcCode);
		}
		lineInfo.timeCode.cfn = tcCode;

		// 读取时间
		idx = line.find("ts:");
		if (idx != line.npos)
		{
			sscanf(line.c_str() + idx + strlen("ts:"), " %hu:%hu:%hu ",
				&lineInfo.timeCode.wHour, &lineInfo.timeCode.wMinute, &lineInfo.timeCode.wSecond);
		}

		// 数据长度
		uint32_t pduBitsLen = 0;
		bool isNoCRC = false;
		if (line.find("DL_BCCH_BCH") != line.npos)
		{
			lineInfo.chType = "DL_BCCH_BCH";
			pduBitsLen = DATA_LENGTH_BCH;
		}
		else if (line.find("fach") != line.npos)
		{
			lineInfo.chType = "FACH";
			pduBitsLen = DATA_LENGTH_FACH;
		}
		else if (line.find("dpch") != line.npos)
		{
			lineInfo.chType = "DPCH";
			pduBitsLen = DATA_LENGTH_DPCH;

			// 读取通道
			idx = line.find("channel");
			if (idx != line.npos)
			{
				sscanf(line.c_str() + idx, "channel %hhu:",
					&lineInfo.chIdx);
			}

			// 读取长度
			idx = line.find('-');
			if (idx != line.npos)
			{
				uint32_t bitsIdx = line.find("bits", idx);
				if (bitsIdx != line.npos)
				{
					sscanf(line.c_str() + idx, "- %u bits:",
						&pduBitsLen);

					// 没有crc校验
					if (pduBitsLen == 103 || pduBitsLen == 60)
					{
						// 以上一次校验为准
						if (lastCRC == '1')
							isNoCRC = true;
					}

					line.erase(0, bitsIdx + 5);
				}
			}

			if (!pduBitsLen)
				continue;
		}

		char crc1 = 0, crc2 = 0;
		idx = line.find("bits:");
		if (idx != line.npos)
		{
			line.erase(0, idx + 5);
		}

		// 读取CRC信息
		idx = line.find("<-- data --- crc -->  ");
		if (idx != line.npos)
		{
			if (lineInfo.chType == "DL_BCCH_BCH")
			{
				sscanf(line.c_str() + idx + strlen("<-- data --- crc -->  "),
					"%c", &crc1);
				line.erase(idx);
			}
			else if (lineInfo.chType == "FACH")
			{
				sscanf(line.c_str() + idx + strlen("<-- data --- crc -->  "),
					"%c %c", &crc1, &crc2);
				line.erase(idx);
			}
			else if (lineInfo.chType == "DPCH")
			{
				if (sscanf(line.c_str() + idx + strlen("<-- data --- crc -->  "), "%c", &crc1) > 0)
				{
					lastCRC = crc1;
				}

				line.erase(idx);
			}
		}

		// 没有crc校验行
		if (isNoCRC)
			crc1 = '1';

		// 根据长度读取bits数据
		for (uint32_t i = 0, readLen = 0; i < line.size(); i++)
		{
			const char &crc_ok = (readLen < pduBitsLen) ? crc1 : crc2;
			if (crc_ok == '1')
			{
				if (line[i] == '0' || line[i] == '1')
				{
					++readLen;
					lineInfo.pduLine.push_back(line[i]);
				}

				if (lineInfo.pduLine.size() == pduBitsLen)
				{
					pduLineList.insert(make_pair(lineInfo.timeCode.cfn, lineInfo));
					lineInfo.pduLine.clear(); // fach有两段数据,清空后继续读取
				}
			}
		}
	}
	fd.close();
}

void L3Decode(L3DecodeSys_t &sys, multimap<DWORD, PDULineInfo_t> &pduLineList)
{
	char t[MAX_PATH];
	
	for (multimap<DWORD, PDULineInfo_t>::iterator ptrPDU = pduLineList.begin();
		ptrPDU != pduLineList.end(); ptrPDU++)
	{
		PDULineInfo_t *pPDUInfo = &ptrPDU->second;
		BitBuffer_t dataPDU;
		Text2Data("Bits", pPDUInfo->pduLine.c_str(), dataPDU);

		// Add ch type/time code info
		_snprintf(t, MAX_PATH, "CFN:%u %hu:%hu:%hu %s_%hhu %ubits:",
			pPDUInfo->timeCode.cfn,
			pPDUInfo->timeCode.wHour,
			pPDUInfo->timeCode.wMinute,
			pPDUInfo->timeCode.wSecond,
			pPDUInfo->chType.c_str(),
			pPDUInfo->chIdx,
			dataPDU.GetDataBits());
		sys.rrc_decode_info += t;

		// Add Hex Text msg
		sys.rrc_decode_info += Data2Text("Hex", dataPDU, dataPDU.GetDataBits()) + "\n";

		// Add MAC and RLC Dec
		if (pPDUInfo->chType == CH_TYPE_BCH)
		{
			PDUData_t pduInfo;
			pduInfo.chType = pPDUInfo->chType;

			sys.rrc_decode_info += l3_decode_bch(dataPDU, pduInfo);
			sys.rrc_decode_info += "RLC:" + Data2Text("Hex", pduInfo.dataRLC, pduInfo.dataRLC.m_nDataSize * 8) + " " + pduInfo.desRLC + "\n";

			l3_combine_pdu(sys, pduInfo);
			l3_decode_pdu(sys, pduInfo);
		}
		else if (pPDUInfo->chType == CH_TYPE_FACH)
		{
			PDUData_t pduInfo;
			pduInfo.chType = pPDUInfo->chType;

			sys.rrc_decode_info += l3_decode_fach(dataPDU, pduInfo);
			sys.rrc_decode_info += "MAC:" + Data2Text("Hex", pduInfo.dataMAC, pduInfo.dataMAC.m_nDataSize * 8) + " " + pduInfo.desMAC + "\n";
			sys.rrc_decode_info += "RLC:" + Data2Text("Hex", pduInfo.dataRLC, pduInfo.dataRLC.m_nDataSize * 8) + " " + pduInfo.desRLC + "\n";
			sys.rrc_decode_info += "DATA:" + Data2Text("Hex", pduInfo.dataPDU, pduInfo.dataPDU.m_nDataSize * 8) + "\n";

			l3_combine_pdu(sys, pduInfo);
			l3_decode_pdu(sys, pduInfo);
		}
		else if (pPDUInfo->chType == CH_TYPE_DPCH)
		{
			PDUData_t pduInfo;
			pduInfo.chType = pPDUInfo->chType;

			sys.rrc_decode_info += l3_decode_dpch(dataPDU, pduInfo);
			if (pduInfo.desType)
			{
				sys.rrc_decode_info += "MAC:" + Data2Text("Hex", pduInfo.dataMAC, pduInfo.dataMAC.m_nDataSize * 8) + " " + pduInfo.desMAC + "\n";
				sys.rrc_decode_info += "RLC:" + Data2Text("Hex", pduInfo.dataRLC, pduInfo.dataRLC.m_nDataSize * 8) + " " + pduInfo.desRLC + "\n";
				sys.rrc_decode_info += "DATA:" + Data2Text("Hex", pduInfo.dataPDU, pduInfo.dataPDU.m_nDataSize * 8) + "\n";
			}		

			l3_combine_pdu(sys, pduInfo);
			if (sys.isF8Ciphering && pduInfo.dataF8PDU.m_nDataSize)
			{
				sys.rrc_decode_info += "F8  :" + Data2Text("Hex", pduInfo.dataF8PDU, pduInfo.dataF8PDU.GetDataBits()) + "\n";
			}

			if (pduInfo.desType)
			{
				 l3_decode_pdu(sys, pduInfo);
			}
			else
			{
				// 保存解码语音数据
				save_voice_file(sys, pduInfo);
			}
		}

		// 换行
		sys.rrc_decode_info += '\n';
	}
}
posted @ 2021-09-12 22:59  裤子多多  阅读(579)  评论(0编辑  收藏  举报