module Osmocom_Gb_Types {

	/* This module contains additional definitions and templates that we use on top of the
	 * TITAN NS + BSSGP modules */

	import from General_Types all;
	import from Osmocom_Types all;
	import from GSM_Types all;
	import from GSM_RR_Types all;
	import from BSSGP_Types all
	import from NS_Types all
	import from Native_Functions all;

	type uint16_t Nsvci;
	type uint16_t Nsei;
	type uint16_t BssgpBvci;

	/* TS 48.016 10.3.7 */
	type enumerated NsPduType {
		NS_PDUT_NS_UNITDATA	('00000000'B),
		NS_PDUT_NS_RESET	('00000010'B),
		NS_PDUT_NS_RESET_ACK	('00000011'B),
		NS_PDUT_NS_BLOCK	('00000100'B),
		NS_PDUT_NS_BLOCK_ACK	('00000101'B),
		NS_PDUT_NS_UNBLOCK	('00000110'B),
		NS_PDUT_NS_UNBLOCK_ACK	('00000111'B),
		NS_PDUT_NS_STATUS	('00001000'B),
		NS_PDUT_NS_ALIVE	('00001010'B),
		NS_PDUT_NS_ALIVE_ACK	('00001011'B)
		/* FIXME: SNS */
	} with { variant "FIELDLENGTH(8)" };

	/* TS 48.016 10.3 */
	type enumerated NsIEI {
		NS_IEI_CAUSE		('00000000'B),
		NS_IEI_NSVCI		('00000001'B),
		NS_IEI_NS_PDU		('00000010'B),
		NS_IEI_BVCI		('00000011'B),
		NS_IEI_NSEI		('00000100'B),
		NS_IEI_LIST_IPv4	('00000101'B),
		NS_IEI_LIST_IPv6	('00000110'B),
		NS_IEI_MAX_NUM_NSVC	('00000111'B),
		NS_IEI_NUM_IPv4_EP	('00001000'B),
		NS_IEI_NUM_IPv6_EP	('00001001'B),
		NS_IEI_RESET_FLAG	('00001010'B),
		NS_IEI_IP_ADDRESS	('00001011'B)
	} with { variant "FIELDLENGTH(8)" };

	/* TS 48.016 10.3.2 */
	type enumerated NsCause {
		NS_CAUSE_TRANSIT_NETWORK_FAILURE		('00000000'B),
		NS_CAUSE_OM_INTERVENTION			('00000001'B),
		NS_CAUSE_EQUIPMENT_FAILURE			('00000010'B),
		NS_CAUSE_NSVC_BLOCKED				('00000011'B),
		NS_CAUSE_NSVC_UNKNOWN				('00000100'B),
		NS_CAUSE_BVCI_UNKNOWN_AT_NSE			('00000101'B),
		NS_CAUSE_SEMANTICALLY_INCORRECT_PDU		('00001000'B),
		NS_CAUSE_PDU_NOT_COMPATIBLE_WITH_PROTOCOL_STATE	('00001010'B),
		NS_CAUSE_PROTOCOL_ERROR_UNSPEIFIED		('00001011'B),
		NS_CAUSE_INVALID_ESSENTIAL_IE			('00001100'B),
		NS_CAUSE_MISSING_ESSENTIAL_IE			('00001101'B),
		NS_CAUSE_INVALID_NR_OF_IPv4_ENDPOINTS		('00001110'B),
		NS_CAUSE_INVALID_NR_OF_IPv6_ENDPOINTS		('00001111'B),
		NS_CAUSE_INVALID_NR_OF_NSVCS			('00010000'B),
		NS_CAUSE_INVALID_WEIGHTS			('00010001'B),
		NS_CAUSE_UNKNOWN_IP_ENDPOINT			('00010010'B),
		NS_CAUSE_UNKNOWN_IP_ADDRESS			('00010011'B),
		NS_CAUSE_IP_TEST_FAILEDA			('00010100'B)
	} with { variant "FIELDLENGTH(8)" };

	template (value) NS_SDU_ControlBits t_SduCtrlB := {
		rBit := '0'B,
		cBit := '0'B,
		spare := '000000'B
	}

	template (value) CauseNS ts_NS_IE_CAUSE(NsCause cause) := {
		iEI := '00'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 1
		},
		cause := int2oct(enum2int(valueof(cause)), 1)
	}
	function ts_NS_IE_CAUSE_omit(template (omit) NsCause cause) return template (omit) CauseNS {
		var template (omit) CauseNS ret;
		if (istemplatekind(cause, "omit")) {
			return omit;
		} else {
			ret := {
				iEI := '00'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 1
				},
				cause := int2oct(enum2int(valueof(cause)), 1)
			}
			return ret;
		}
	}

	function tr_NS_IE_CAUSE(template NsCause cause) return template CauseNS {
		var template CauseNS ret;
		ret.iEI := '00'O;
		ret.ext := '1'B;
		ret.lengthIndicator := { length1 := 1 };
		if (istemplatekind(cause, "omit")) {
			return omit;
		} else if (istemplatekind(cause, "*")) {
			return *;
		} else if (istemplatekind(cause, "?")) {
			ret.cause := ?
		} else {
			ret.cause := int2oct(enum2int(valueof(cause)), 1);
		}
		return ret;
	}

	function ts_SNS_IP_ADDR(template (omit) IPAddress ip) return template (omit) IP_Address_NS {
		var template (omit) IP_Address_NS ret;
		if (istemplatekind(ip, "omit")) {
			return omit;
		} else {
			ret.iEI := '0B'O;
			ret.ipAddress := ip;
			if (ischosen(ip.ip4Address)) {
				ret.addressType := '01'O;
			} else {
				ret.addressType := '02'O;
			}
		}
		return ret;
	}
	function tr_SNS_IP_ADDR(template IPAddress ip) return template IP_Address_NS {
		var template IP_Address_NS ret;
		ret.iEI := '0B'O;
		if (istemplatekind(ip, "omit")) {
			return omit;
		} else if (istemplatekind(ip, "*")) {
			return *;
		} else if (istemplatekind(ip, "?")) {
			return ?;
		} else {
			ret.ipAddress := ip;
			if (ischosen(ip.ip4Address)) {
				ret.addressType := '01'O;
			} else {
				ret.addressType := '02'O;
			}
		}
		return ret;
	}

	private function f_oct_or_wc(template integer inp, integer len) return template octetstring {
		if (istemplatekind(inp, "omit")) {
			return omit;
		} else if (istemplatekind(inp, "*")) {
			return *;
		} else if (istemplatekind(inp, "?")) {
			return ?;
		}
		return int2oct(valueof(inp), len);
	}

	template (value) NS_VCI ts_NS_IE_NSVCI(Nsvci nsvci) := {
		iEI := '01'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 2
		},
		nS_VCI := int2oct(nsvci, 2)
	}
	template NS_VCI tr_NS_IE_NSVCI(template Nsvci nsvci) := {
		iEI := '01'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 2
		},
		nS_VCI := f_oct_or_wc(nsvci, 2)
	}

	template (value) NSEI_NS ts_NS_IE_NSEI(Nsei nsei) := {
		iEI:= '04'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 2
		},
		nSEI := int2oct(nsei, 2)
	}
	template NSEI_NS tr_NS_IE_NSEI(template Nsei nsei) := {
		iEI:= '04'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 2
		},
		nSEI := f_oct_or_wc(nsei, 2)
	}

	template (value) IP4_Element ts_SNS_IPv4(charstring ip, integer udp_port,
						 uint8_t sig_weight := 1, uint8_t data_weight := 1) := {
		ipAddress := f_inet_addr(ip),
		uDP_Port := int2oct(udp_port, 2),
		signallingWeight := int2oct(sig_weight, 1),
		dataWeight := int2oct(data_weight, 1)
	}
	function tr_SNS_IPv4(template charstring ip, template integer udp_port,
				template uint8_t sig_weight := ?, template uint8_t data_weight := ?)
	return template IP4_Element {
		var template IP4_Element e;
		if (istemplatekind(ip, "?")) {
			e.ipAddress := ?;
		} else {
			e.ipAddress := f_inet_addr(valueof(ip));
		}
		if (istemplatekind(udp_port, "?")) {
			e.uDP_Port := ?;
		} else {
			e.uDP_Port := int2oct(valueof(udp_port), 2);
		}
		if (istemplatekind(sig_weight, "?")) {
			e.signallingWeight := ?;
		} else {
			e.signallingWeight := int2oct(valueof(sig_weight), 1);
		}
		if (istemplatekind(data_weight, "?")) {
			e.dataWeight := ?;
		} else {
			e.dataWeight := int2oct(valueof(data_weight), 1);
		}
		return e;
	}


	template (value) PDU_NS ts_NS_RESET(NsCause cause, Nsvci nsvci, Nsei nsei) := {
		pDU_NS_Reset := {
			nsPduType := '02'O,
			causeNS := ts_NS_IE_CAUSE(cause),
			nS_VCI := ts_NS_IE_NSVCI(nsvci),
			nSEI_NS := ts_NS_IE_NSEI(nsei)
		}
	}
	template PDU_NS tr_NS_RESET(template NsCause cause, template Nsvci nsvci, template Nsei nsei) := {
		pDU_NS_Reset := {
			nsPduType := '02'O,
			causeNS := tr_NS_IE_CAUSE(cause),
			nS_VCI := tr_NS_IE_NSVCI(nsvci),
			nSEI_NS := tr_NS_IE_NSEI(nsei)
		}
	}

	template (value) PDU_NS ts_NS_RESET_ACK(Nsvci nsvci, Nsei nsei) := {
		pDU_NS_Reset_Ack := {
			nsPduType := '03'O,
			nS_VCI := ts_NS_IE_NSVCI(nsvci),
			nSEI_NS := ts_NS_IE_NSEI(nsei)
		}
	}
	template PDU_NS tr_NS_RESET_ACK(template Nsvci nsvci, template Nsei nsei) := {
		pDU_NS_Reset_Ack := {
			nsPduType := '03'O,
			nS_VCI := tr_NS_IE_NSVCI(nsvci),
			nSEI_NS := tr_NS_IE_NSEI(nsei)
		}
	}

	template (value) PDU_NS ts_NS_BLOCK(NsCause cause, Nsvci nsvci) := {
		pDU_NS_Block := {
			nsPduType := '04'O,
			causeNS := ts_NS_IE_CAUSE(cause),
			nS_VCI := ts_NS_IE_NSVCI(nsvci)
		}
	}
	template PDU_NS tr_NS_BLOCK(template NsCause cause, template Nsvci nsvci) := {
		pDU_NS_Block := {
			nsPduType := '04'O,
			causeNS := tr_NS_IE_CAUSE(cause),
			nS_VCI := tr_NS_IE_NSVCI(nsvci)
		}
	}

	template (value) PDU_NS ts_NS_BLOCK_ACK(Nsvci nsvci) := {
		pDU_NS_Block_Ack := {
			nsPduType := '05'O,
			nS_VCI := ts_NS_IE_NSVCI(nsvci)
		}
	}
	template PDU_NS tr_NS_BLOCK_ACK(template Nsvci nsvci) := {
		pDU_NS_Block_Ack := {
			nsPduType := '05'O,
			nS_VCI := tr_NS_IE_NSVCI(nsvci)
		}
	}

	template PDU_NS t_NS_UNBLOCK := {
		pDU_NS_Unblock := {
			nsPduType := '06'O
		}
	}

	template PDU_NS t_NS_UNBLOCK_ACK := {
		pDU_NS_Unblock_Ack := {
			nsPduType := '07'O
		}
	}

	template PDU_NS t_NS_ALIVE := {
		pDU_NS_Alive := {
			nsPduType := '0A'O
		}
	}

	template PDU_NS t_NS_ALIVE_ACK := {
		pDU_NS_Alive_Ack := {
			nsPduType := '0B'O
		}
	}

	template (value) PDU_NS ts_NS_STATUS(NsCause cause, PDU_NS pdu) := {
		pDU_NS_Status := {
			nsPduType := '08'O,
			causeNS := ts_NS_IE_CAUSE(cause),
			nS_VCI := omit,
			nS_PDU := {
				iEI := '02'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 0 /* overwritten */
				},
				ns_PDU := enc_PDU_NS(pdu)
			},
			bVCI_NS := omit,
			listofIP4Elements := omit,
			listofIP6Elements := omit
		}
	}

	template PDU_NS tr_NS_STATUS(template NsCause cause) := {
		pDU_NS_Status := {
			nsPduType := '08'O,
			causeNS := tr_NS_IE_CAUSE(cause),
			nS_VCI := *,
			nS_PDU := *,
			bVCI_NS := *,
			listofIP4Elements := *,
			listofIP6Elements := *
		}
	}

	template (value) PDU_NS ts_NS_UNITDATA(template (value) NS_SDU_ControlBits bits, BssgpBvci bvci, octetstring sdu) := {
		pDU_NS_Unitdata := {
			nsPduType := '00'O,
			nS_SDU_ControlBits := bits,
			bVCI := int2oct(bvci, 2),
			nS_SDU := sdu
		}
	}
	template PDU_NS tr_NS_UNITDATA(template NS_SDU_ControlBits bits, template BssgpBvci bvci, template
octetstring sdu) := {
		pDU_NS_Unitdata := {
			nsPduType := '00'O,
			nS_SDU_ControlBits := bits,
			bVCI := f_oct_or_wc(bvci, 2),
			nS_SDU := sdu
		}
	}



	private function ts_num_of_ep(OCT1 iei, template (omit) uint16_t num_ep)
	return template (omit) NumberOfIP_Endpoints {
		var template (omit) NumberOfIP_Endpoints t;
		if (istemplatekind(num_ep, "omit")) {
			return omit;
		} else {
			t.iEI := iei;
			t.numberOfIP_Endpoints := int2oct(valueof(num_ep), 2);
			return t;
		}
	}
	private function tr_num_of_ep(OCT1 iei, template uint16_t num_ep)
	return template NumberOfIP_Endpoints {
		var template NumberOfIP_Endpoints t;
		if (istemplatekind(num_ep, "omit")) {
			return omit;
		} else if (istemplatekind(num_ep, "*")) {
			return *;
		} else if (istemplatekind(num_ep, "?")) {
			return ?;
		} else {
			t.iEI := iei;
			t.numberOfIP_Endpoints := int2oct(valueof(num_ep), 2);
			return t;
		}
	}


	template (value) ResetFlag ts_SNS_IE_ResetFlag(boolean rst) := {
		iEI := '0A'O,
		resetBIT := bool2bit(rst),
		spare := '0000000'B
	}
	template ResetFlag tr_SNS_IE_ResetFlag(template boolean rst) := {
		iEI := '0A'O,
		resetBIT := bool2bit_tmpl(rst),
		spare := '0000000'B
	}

	template (value) EndFlag ts_SNS_IE_EndFlag(boolean end) := {
		eBIT := bool2bit(end),
		spare := '0000000'B
	}
	template EndFlag tr_SNS_IE_EndFlag(template boolean end) := {
		eBIT := bool2bit_tmpl(end),
		spare := '0000000'B
	}

	template (value) MaxNumberOfNSVCs ts_SNS_IE_MaxNumOfNSVCs(uint16_t num) := {
		iEI := '07'O,
		maxNumberOfNSVCs := int2oct(num, 2)
	}
	template MaxNumberOfNSVCs tr_SNS_IE_MaxNumOfNSVCs(template uint16_t num) := {
		iEI := '07'O,
		maxNumberOfNSVCs := f_oct_or_wc(num, 2)
	}

	template (value) PDU_NS ts_SNS_SIZE(Nsei nsei, boolean rst_flag := true,
				uint16_t max_nsvcs := 2,
				template (omit) uint16_t num_v4 := 1,
				template (omit) uint16_t num_v6 := omit) := {
		pDU_SNS_Size := {
			nsPduType := '12'O,
			nSEI_NS := ts_NS_IE_NSEI(nsei),
			resetFlag := ts_SNS_IE_ResetFlag(rst_flag),
			maxNumberOfNSVCs := ts_SNS_IE_MaxNumOfNSVCs(max_nsvcs),
			numberOfIP4_Endpoints := ts_num_of_ep('08'O, num_v4),
			numberOfIP6_Endpoints := ts_num_of_ep('09'O, num_v6)
		}
	}
	template PDU_NS tr_SNS_SIZE(template Nsei nsei, template boolean rst_flag := ?,
				template uint16_t max_nsvcs := ?,
				template uint16_t num_v4 := ?,
				template uint16_t num_v6 := *) := {
		pDU_SNS_Size := {
			nsPduType := '12'O,
			nSEI_NS := tr_NS_IE_NSEI(nsei),
			resetFlag := tr_SNS_IE_ResetFlag(rst_flag),
			maxNumberOfNSVCs := tr_SNS_IE_MaxNumOfNSVCs(max_nsvcs),
			numberOfIP4_Endpoints := tr_num_of_ep('08'O, num_v4),
			numberOfIP6_Endpoints := tr_num_of_ep('09'O, num_v6)
		}
	}

	template PDU_NS ts_SNS_SIZE_ACK(Nsei nsei, template (omit) NsCause cause) := {
		pDU_SNS_Size_Ack := {
			nsPduType := '13'O,
			nSEI_NS := ts_NS_IE_NSEI(nsei),
			causeNS := ts_NS_IE_CAUSE_omit(cause)
		}
	}
	template PDU_NS tr_SNS_SIZE_ACK(template Nsei nsei, template NsCause cause) := {
		pDU_SNS_Size_Ack := {
			nsPduType := '13'O,
			nSEI_NS := tr_NS_IE_NSEI(nsei),
			causeNS := tr_NS_IE_CAUSE(cause)
		}
	}

	private function ts_SNS_IE_ListIP4(template (omit) IP4_Elements elem)
	return template (omit) ListofIP4Elements {
		var template (omit) ListofIP4Elements r;
		if (istemplatekind(elem, "omit")) {
			return omit;
		} else {
			r := {
				iEI := '05'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 0 /* overwritten */
				},
				iP4_Elements := elem
			}
			return r;
		}
	}
	private function tr_SNS_IE_ListIP4(template IP4_Elements elem)
	return template ListofIP4Elements {
		var template ListofIP4Elements r;
		if (istemplatekind(elem, "omit")) {
			return omit;
		} else {
			r := {
				iEI := '05'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := ? /* overwritten */
				},
				iP4_Elements := elem
			}
			return r;
		}
	}

	private function ts_SNS_IE_ListIP6(template (omit) IP6_Elements elem)
	return template (omit) ListofIP6Elements {
		var template (omit) ListofIP6Elements r;
		if (istemplatekind(elem, "omit")) {
			return omit;
		} else {
			r := {
				iEI := '06'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 0 /* overwritten */
				},
				iP6_Elements := elem
			}
			return r;
		}
	}
	private function tr_SNS_IE_ListIP6(template IP6_Elements elem)
	return template ListofIP6Elements {
		var template ListofIP6Elements r;
		if (istemplatekind(elem, "omit")) {
			return omit;
		} else if (istemplatekind(elem, "*")) {
			return *;
		} else {
			r := {
				iEI := '06'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := ? /* overwritten */
				},
				iP6_Elements := elem
			}
			return r;
		}
	}

	template (value) PDU_NS ts_SNS_CONFIG(Nsei nsei, boolean end_flag,
					template (omit) IP4_Elements v4,
					template (omit) IP6_Elements v6 := omit) := {
		pDU_SNS_Config := {
			nsPduType := '0F'O,
			endFlag := ts_SNS_IE_EndFlag(end_flag),
			nSEI_NS := ts_NS_IE_NSEI(nsei),
			listofIP4Elements := ts_SNS_IE_ListIP4(v4),
			listofIP6Elements := ts_SNS_IE_ListIP6(v6)
		}
	}
	template PDU_NS tr_SNS_CONFIG(template Nsei nsei, template boolean end_flag,
					template IP4_Elements v4,
					template IP6_Elements v6 := omit) := {
		pDU_SNS_Config := {
			nsPduType := '0F'O,
			endFlag := tr_SNS_IE_EndFlag(end_flag),
			nSEI_NS := tr_NS_IE_NSEI(nsei),
			listofIP4Elements := tr_SNS_IE_ListIP4(v4),
			listofIP6Elements := tr_SNS_IE_ListIP6(v6)
		}
	}

	template (value) PDU_NS ts_SNS_CONFIG_ACK(Nsei nsei, template (omit) NsCause cause) := {
		pDU_SNS_Config_Ack := {
			nsPduType := '10'O,
			nSEI_NS := ts_NS_IE_NSEI(nsei),
			causeNS := ts_NS_IE_CAUSE_omit(cause)
		}
	}
	template PDU_NS tr_SNS_CONFIG_ACK(template Nsei nsei, template NsCause cause) := {
		pDU_SNS_Config_Ack := {
			nsPduType := '10'O,
			nSEI_NS := tr_NS_IE_NSEI(nsei),
			causeNS := tr_NS_IE_CAUSE(cause)
		}
	}

	template (value) PDU_NS ts_SNS_ADD(Nsei nsei, uint8_t trans_id,
					   template (omit) IP4_Elements v4,
					   template (omit) IP6_Elements v6 := omit) := {
		pDU_SNS_Add := {
			nsPduType := '0D'O,
			nSEI_NS := ts_NS_IE_NSEI(nsei),
			transactionID := trans_id,
			listofIP4Elements := ts_SNS_IE_ListIP4(v4),
			listofIP6Elements := ts_SNS_IE_ListIP6(v6)
		}
	}
	template PDU_NS tr_SNS_ADD(template Nsei nsei, template uint8_t trans_id,
				   template IP4_Elements v4,
				   template IP6_Elements v6 := omit) := {
		pDU_SNS_Add := {
			nsPduType := '0D'O,
			nSEI_NS := tr_NS_IE_NSEI(nsei),
			transactionID := trans_id,
			listofIP4Elements := tr_SNS_IE_ListIP4(v4),
			listofIP6Elements := tr_SNS_IE_ListIP6(v6)
		}
	}

	template (value) PDU_NS ts_SNS_DEL(Nsei nsei, uint8_t trans_id,
					   template (omit) IPAddress ip_sns,
					   template (omit) IP4_Elements v4,
					   template (omit) IP6_Elements v6 := omit) := {
		pDU_SNS_Delete := {
			nsPduType := '11'O,
			nSEI_NS := ts_NS_IE_NSEI(nsei),
			transactionID := trans_id,
			iP_Address_NS := ts_SNS_IP_ADDR(ip_sns),
			listofIP4Elements := ts_SNS_IE_ListIP4(v4),
			listofIP6Elements := ts_SNS_IE_ListIP6(v6)
		}
	}
	template PDU_NS tr_SNS_DEL(template Nsei nsei, template uint8_t trans_id,
				   template IPAddress ip_sns,
				   template IP4_Elements v4,
				   template IP6_Elements v6 := omit) := {
		pDU_SNS_Delete := {
			nsPduType := '11'O,
			nSEI_NS := tr_NS_IE_NSEI(nsei),
			transactionID := trans_id,
			iP_Address_NS := tr_SNS_IP_ADDR(ip_sns),
			listofIP4Elements := tr_SNS_IE_ListIP4(v4),
			listofIP6Elements := tr_SNS_IE_ListIP6(v6)
		}
	}

	template (value) PDU_NS ts_SNS_CHG_WEIGHT(Nsei nsei, uint8_t trans_id,
						  template (omit) IP4_Elements v4,
						  template (omit) IP6_Elements v6 := omit) := {
		pDU_SNS_ChangeWeight := {
			nsPduType := '0E'O,
			nSEI_NS := ts_NS_IE_NSEI(nsei),
			transactionID := trans_id,
			listofIP4Elements := ts_SNS_IE_ListIP4(v4),
			listofIP6Elements := ts_SNS_IE_ListIP6(v6)
		}
	}
	template PDU_NS tr_SNS_CHG_WEIGHT(template Nsei nsei, template uint8_t trans_id,
					  template IP4_Elements v4,
					  template IP6_Elements v6 := omit) := {
		pDU_SNS_ChangeWeight := {
			nsPduType := '0E'O,
			nSEI_NS := tr_NS_IE_NSEI(nsei),
			transactionID := trans_id,
			listofIP4Elements := tr_SNS_IE_ListIP4(v4),
			listofIP6Elements := tr_SNS_IE_ListIP6(v6)
		}
	}


	template (value) PDU_NS ts_SNS_ACK(Nsei nsei, uint8_t trans_id,
					   template (omit) NsCause cause := omit,
					   template (omit) IP4_Elements v4 := omit,
					   template (omit) IP6_Elements v6 := omit) := {
		pDU_SNS_Ack := {
			nsPduType := '0C'O,
			nSEI_NS := ts_NS_IE_NSEI(nsei),
			transactionID := trans_id,
			causeNS := ts_NS_IE_CAUSE_omit(cause),
			iP_Address_NS := omit,
			listofIP4Elements := ts_SNS_IE_ListIP4(v4),
			listofIP6Elements := ts_SNS_IE_ListIP6(v6)
		}
	}
	template PDU_NS tr_SNS_ACK(template Nsei nsei, template uint8_t trans_id := ?,
					   template NsCause cause := omit,
					   template IP4_Elements v4 := *,
					   template IP6_Elements v6 := *) := {
		pDU_SNS_Ack := {
			nsPduType := '0C'O,
			nSEI_NS := tr_NS_IE_NSEI(nsei),
			transactionID := trans_id,
			causeNS := tr_NS_IE_CAUSE(cause),
			iP_Address_NS := omit,
			listofIP4Elements := tr_SNS_IE_ListIP4(v4),
			listofIP6Elements := tr_SNS_IE_ListIP6(v6)
		}
	}







	type record BssgpCellId {
		RoutingAreaIdentification	ra_id,
		CellIdentity			cell_id
	} with { variant "" };

	type enumerated BssgpCause {
		BSSGP_CAUSE_PROC_OVERLOAD			('00'H),
		BSSGP_CAUSE_EQUIMENT_FAILURE			('01'H),
		BSSGP_CAUSE_TRANSIT_NETWORK_FAILURE		('02'H),
		BSSGP_CAUSE_NET_SV_CAP_MOD_GT_ZERO_KBPS		('03'H),
		BSSGP_CAUSE_UNKNOWN_MS				('04'H),
		BSSGP_CAUSE_BVCI_UNKNOWN			('05'H),
		BSSGP_CAUSE_CELL_TRAFFIC_CONGESTION		('06'H),
		BSSGP_CAUSE_SGSN_CONGESTION			('07'H),
		BSSGP_CAUSE_OM_INTERVENTION			('08'H),
		BSSGP_CAUSE_BVCI_BLOCKED			('09'H),
		BSSGP_CAUSE_PFC_CREATE_FAILURE			('0a'H),
		BSSGP_CAUSE_PFC_PREEMPTED			('0b'H),
		BSSGP_CAUSE_ABQP_NO_MORE_SUPPORTED		('0c'H),
		BSSGP_CAUSE_SEMANTICALLY_INCORRECT_PDU		('20'H),
		BSSGP_CAUSE_INVALID_MANDATORY_IE		('21'H),
		BSSGP_CAUSE_MISSING_MANDATORY_IE		('22'H),
		BSSGP_CAUSE_MISSING_CONDITIONAL_IE		('23'H),
		BSSGP_CAUSE_UNEXPECTED_CONDITIONAL_IE		('24'H),
		BSSGP_CAUSE_CONDITIONAL_IE_ERROR		('25'H),
		BSSGP_CAUSE_PDU_NOT_COMPATIBLE_WITH_PROTOCOL_STATE ('26'H),
		BSSGP_CAUSE_PROTOCOL_ERROR_UNSPECIFIED		('27'H),
		BSSGP_CAUSE_PDU_NOT_COMPATIBLE_WITH_FEATURE_SET	('28'H),
		BSSGP_CAUSE_REQUESTED_INFO_NOT_AVAILABLE	('29'H),
		BSSGP_CAUSE_UNKNOWN_DESTINATION_ADDRESS		('2a'H),
		BSSGP_CAUSE_UNKNOWN_RIM_APP_IDENTITY		('2b'H),
		BSSGP_CAUSE_INVALID_CONTAINER_UNIT_INFO		('2c'H),
		BSSGP_CAUSE_PFC_QUEUING				('2d'H),
		BSSGP_CAUSE_PFC_CREATED_SUCCESSFULLY		('2e'H),
		BSSGP_CAUSE_T12_EXPIRY				('2f'H),
		BSSGP_CAUSE_MS_UNDER_PS_HANDOVER_TREATMENT	('30'H),
		BSSGP_CAUSE_UPLINK_QUALITY			('31'H),
		BSSGP_CAUSE_UPLINK_STRENGTH			('32'H),
		BSSGP_CAUSE_DOWNLINK_QUALITY			('33'H),
		BSSGP_CAUSE_DOWNLINK_STRENGTH			('34'H),
		BSSGP_CAUSE_DISTANCE				('35'H),
		BSSGP_CAUSE_BETTER_CELL				('36'H),
		BSSGP_CAUSE_TRAFFIC				('37'H),
		BSSGP_CAUSE_OM_INTERVENTION2			('38'H),
		BSSGP_CAUSE_MS_BACK_ON_OLD_CHANNEL		('39'H),
		BSSGP_CAUSE_T13_EXPIRY				('3a'H),
		BSSGP_CAUSE_T14_EXPIRY				('3b'H),
		BSSGP_CAUSE_NOT_ALL_REQUESTED_PFC_CREATED	('3c'H)
	} with { variant "FIELDLENGTH(8)" };


	template BVCI t_BSSGP_BVCI(template BssgpBvci bvci) := {
		iEI := '04'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 2
		},
		unstructured_value := f_oct_or_wc(bvci, 2)
	}

	template (value) TLLI_BSSGP ts_BSSGP_TLLI(template (value) GprsTlli tlli) := {
		iEI := '1F'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 4
		},
		tLLI_Value := tlli
	}
	template TLLI_BSSGP tr_BSSGP_TLLI(template GprsTlli tlli) := {
		iEI := '1F'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 4
		},
		tLLI_Value := tlli
	}

	template (value) Suspend_Reference_Number ts_BSSGP_SUSP_REF(template (value) OCT1 susp_ref) := {
		iEI := '1D'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 1
		},
		suspend_Reference_Number_value := susp_ref
	}
	template Suspend_Reference_Number tr_BSSGP_SUSP_REF(template OCT1 susp_ref) := {
		iEI := '1D'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 1
		},
		suspend_Reference_Number_value := susp_ref
	}

	template IMSI_BSSGP tr_BSSGP_IMSI(template hexstring imsi) := {
		iEI := '0D'O,
		ext := '1'B,
		lengthIndicator := ?,
		type_of_Identity := '001'B,
		oddevenIndicator := ?,
		digits := imsi
	}

	template IMSI_BSSGP ts_BSSGP_IMSI(hexstring imsi) := {
		iEI := '0D'O,
		ext := '1'B,
		lengthIndicator := { length1 := 0 /* overwritten */ },
		type_of_Identity := '001'B,
		oddevenIndicator := f_hex_is_odd_length(imsi),
		digits := imsi
	}

	template TMSI_BSSGP ts_BSSGP_TMSI(GsmTmsi tmsi) := {
		iEI := '20'O,
		ext := '1'B,
		lengthIndicator := { length1 := 4 },
		tMSI_Value := int2oct(tmsi, 4)
	}

	function f_bssgp_length_ind(integer len) return LIN2_2a {
		var LIN2_2a ret;
		if (len > 255) {
			ret := { length2 := len };
		} else {
			ret := { length1 := len };
		}
		return ret;
	}

	template LLC_PDU ts_BSSGP_LLC_PDU(octetstring pdu) := {
		iEI := '0D'O,
		ext := '1'B,
		lengthIndicator := f_bssgp_length_ind(lengthof(pdu)),
		lLC_PDU := pdu
	}

	template LLC_PDU tr_BSSGP_LLC_PDU(template octetstring pdu := ?) := {
		iEI := '0E'O,
		ext := ?,
		lengthIndicator := ?,
		lLC_PDU := pdu
	}

	function ts_BSSGP_CAUSE(template (omit) BssgpCause cause) return template (omit) Cause_BSSGP {
		var template (omit) Cause_BSSGP ret;
		if (istemplatekind(cause, "omit")) {
			ret := omit;
		} else {
			ret.iEI := '07'O;
			ret.ext := '1'B;
			ret.lengthIndicator := { length1 := 1 };
			ret.cause_Value := int2oct(enum2int(valueof(cause)), 1);
		}
		return ret;
	}
	function t_BSSGP_CAUSE(template BssgpCause cause) return template Cause_BSSGP {
		var template Cause_BSSGP ret;
		ret.iEI := '07'O;
		ret.ext := '1'B;
		ret.lengthIndicator := { length1 := 1 };
		if (isvalue(cause)) {
			ret.cause_Value := int2oct(enum2int(valueof(cause)), 1);
		} else {
			ret.cause_Value := ?
		}
		return ret;
	}

	function t_BSSGP_IE_CellId(template BssgpCellId cid) return template Cell_Identifier {
		var template Cell_Identifier ret := {
			iEI := '08'O,
			ext := '1'B,
			lengthIndicator := { length1 := 8 },
			mccDigit1 := ?,
			mccDigit2 := ?,
			mccDigit3 := ?,
			mncDigit3 := ?,
			mncDigit1 := ?,
			mncDigit2 := ?,
			lac := ?,
			rac := ?,
			cI_value := ?
		}
		if (istemplatekind(cid, "omit")) {
			return omit;
		} else if (istemplatekind(cid, "*")) {
			return *;
		} else if (istemplatekind(cid, "?")) {
			return ?;
		}
		if (isvalue(cid) and isvalue(cid.ra_id) and isvalue(cid.ra_id.lai)) {
			if (isvalue(cid.ra_id.lai.mcc_mnc)) {
				ret.mccDigit1 := cid.ra_id.lai.mcc_mnc[0];
				ret.mccDigit2 := cid.ra_id.lai.mcc_mnc[1];
				ret.mccDigit3 := cid.ra_id.lai.mcc_mnc[2];
				ret.mncDigit3 := cid.ra_id.lai.mcc_mnc[3];
				ret.mncDigit1 := cid.ra_id.lai.mcc_mnc[4];
				ret.mncDigit2 := cid.ra_id.lai.mcc_mnc[5];
			}
			if (isvalue(cid.ra_id.lai.lac)) {
				ret.lac := f_oct_or_wc(cid.ra_id.lai.lac, 2);
			}
		}
		if (isvalue(cid) and isvalue(cid.ra_id)) {
			ret.rac := f_oct_or_wc(cid.ra_id.rac, 1);
		}
		if (isvalue(cid)) {
			ret.cI_value := f_oct_or_wc(cid.cell_id, 2);
		}
		return ret;
	}

	template PDU_BSSGP ts_BVC_RESET(BssgpCause cause, BssgpBvci bvci,
					template BssgpCellId cell_id) := {
		pDU_BSSGP_BVC_RESET := {
			bssgpPduType := '22'O,
			bVCI := t_BSSGP_BVCI(bvci),
			cause := ts_BSSGP_CAUSE(cause),
			cell_Identifier := t_BSSGP_IE_CellId(cell_id),
			feature_bitmap := omit,
			extended_Feature_Bitmap := omit
		}
	}

	template PDU_BSSGP tr_BVC_RESET(template BssgpCause cause, template BssgpBvci bvci,
					template BssgpCellId cell_id) := {
		pDU_BSSGP_BVC_RESET := {
			bssgpPduType := '22'O,
			bVCI := t_BSSGP_BVCI(bvci),
			cause := t_BSSGP_CAUSE(cause),
			cell_Identifier := t_BSSGP_IE_CellId(cell_id),
			feature_bitmap := *,
			extended_Feature_Bitmap := *
		}
	}

	template PDU_BSSGP ts_BVC_RESET_ACK(BssgpBvci bvci, template BssgpCellId cell_id) := {
		pDU_BSSGP_BVC_RESET_ACK := {
			bssgpPduType := '23'O,
			bVCI := t_BSSGP_BVCI(bvci),
			cell_Identifier := t_BSSGP_IE_CellId(cell_id),
			feature_bitmap := omit,
			extended_Feature_Bitmap := omit
		}
	}

	template PDU_BSSGP tr_BVC_RESET_ACK(template BssgpBvci bvci, template BssgpCellId cell_id) := {
		pDU_BSSGP_BVC_RESET_ACK := {
			bssgpPduType := '23'O,
			bVCI := t_BSSGP_BVCI(bvci),
			cell_Identifier := t_BSSGP_IE_CellId(cell_id),
			feature_bitmap := *,
			extended_Feature_Bitmap := *
		}
	}


	template PDU_BSSGP t_BVC_UNBLOCK(template BssgpBvci bvci) := {
		pDU_BSSGP_BVC_UNBLOCK := {
			bssgpPduType := '24'O,
			bVCI := t_BSSGP_BVCI(bvci)
		}
	}

	template PDU_BSSGP t_BVC_UNBLOCK_ACK(template BssgpBvci bvci) := {
		pDU_BSSGP_BVC_UNBLOCK_ACK := {
			bssgpPduType := '25'O,
			bVCI := t_BSSGP_BVCI(bvci)
		}
	}

	template PDU_BSSGP t_BVC_BLOCK(template BssgpBvci bvci, template BssgpCause cause) := {
		pDU_BSSGP_BVC_BLOCK := {
			bssgpPduType := '20'O,
			bVCI := t_BSSGP_BVCI(bvci),
			cause := t_BSSGP_CAUSE(cause)
		}
	}

	template PDU_BSSGP t_BVC_BLOCK_ACK(template BssgpBvci bvci) := {
		pDU_BSSGP_BVC_BLOCK_ACK := {
			bssgpPduType := '21'O,
			bVCI := t_BSSGP_BVCI(bvci)
		}
	}

	template PDU_BSSGP t_BVC_FC_BVC(uint16_t bmax, uint16_t bucket_leak_rate,
					uint16_t bmax_default_ms, uint16_t r_default_ms, OCT1 tag) := {
		pDU_BSSGP_FLOW_CONTROL_BVC := {
			bssgpPduType := '26'O,
			tag := {
				iEI := '1E'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 1
				},
				unstructured_Value := tag
			},
			bVC_Bucket_Size := {
				iEI := '05'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				bmax := f_oct_or_wc(bmax, 2)
			},
			bucket_Leak_Rate := {
				iEI := '03'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				r_Value := f_oct_or_wc(bucket_leak_rate, 2)
			},
			bmax_default_MS := {
				iEI := '01'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				bmax := f_oct_or_wc(bmax_default_ms, 2)
			},
			r_default_MS := {
				iEI := '1C'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				r_default_MS_value := f_oct_or_wc(r_default_ms, 2)
			},
			bucket_Full_Ratio := omit,
			bVC_Measurement := omit,
			flow_Control_Granularity := omit
		}
	}
	template PDU_BSSGP tr_BVC_FC_BVC(template uint16_t bmax := ?,
					 template uint16_t bucket_leak_rate := ?,
					 template uint16_t bmax_default_ms := ?,
					 template uint16_t r_default_ms := ?,
					 template OCT1 tag := ?) := {
		pDU_BSSGP_FLOW_CONTROL_BVC := {
			bssgpPduType := '26'O,
			tag := {
				iEI := '1E'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 1
				},
				unstructured_Value := tag
			},
			bVC_Bucket_Size := {
				iEI := '05'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				bmax := f_oct_or_wc(bmax, 2)
			},
			bucket_Leak_Rate := {
				iEI := '03'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				r_Value := f_oct_or_wc(bucket_leak_rate, 2)
			},
			bmax_default_MS := {
				iEI := '01'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				bmax := f_oct_or_wc(bmax_default_ms, 2)
			},
			r_default_MS := {
				iEI := '1C'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				r_default_MS_value := f_oct_or_wc(r_default_ms, 2)
			},
			bucket_Full_Ratio := *,
			bVC_Measurement := *,
			flow_Control_Granularity := *
		}
	}

	template PDU_BSSGP t_BVC_FC_BVC_ACK(template OCT1 tag) := {
		pDU_BSSGP_FLOW_CONTROL_BVC_ACK := {
			bssgpPduType := '27'O,
			tag := {
				iEI := '1E'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 2
				},
				unstructured_Value := tag
			}
		}
	}

	template PDU_BSSGP ts_BSSGP_STATUS(template BssgpBvci bvci, template BssgpCause cause,
					   PDU_BSSGP pdu) := {
		pDU_BSSGP_STATUS := {
			bssgpPduType := '41'O,
			cause := t_BSSGP_CAUSE(cause),
			bVCI := t_BSSGP_BVCI(bvci),
			pDU_in_Error := {
				iEI := '15'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 0 /* overwritten */
				},
				erroneous_BSSGP_PDU := enc_PDU_BSSGP(pdu)
			}
		}
	}

	template PDU_BSSGP tr_BSSGP_STATUS(template BVCI bvci := ?, template BssgpCause cause := ?,
					   template octetstring pdu := ?) := {
		pDU_BSSGP_STATUS := {
			bssgpPduType := '41'O,
			cause := t_BSSGP_CAUSE(cause),
			bVCI := bvci,
			pDU_in_Error := {
				iEI := '15'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := ?
				},
				erroneous_BSSGP_PDU := pdu
			}
		}
	}

	private function f_presence_bit_MultislotCap_GPRS_BSSGP(template (omit) MultislotCap_GPRS_BSSGP mscap_gprs) return BIT1 {
		if (istemplatekind(mscap_gprs, "omit")) {
			return '0'B;
		}
		return '1'B;
	}
	private function f_presence_bit_MultislotCap_EGPRS_BSSGP(template (omit) MultislotCap_EGPRS_BSSGP mscap_egprs) return BIT1 {
		if (istemplatekind(mscap_egprs, "omit")) {
			return '0'B;
		}
		return '1'B;
	}
	template (value) MSRACapabilityValuesRecord_BSSGP ts_RaCapRec_BSSGP(BIT4 att := '0001'B /* E-GSM */, template (omit) MultislotCap_GPRS_BSSGP mscap_gprs := omit, template (omit) MultislotCap_EGPRS_BSSGP mscap_egprs := omit) := {
		mSRACapabilityValues := {
			mSRACapabilityValuesExclude1111 := {
				accessTechnType := att, /* E-GSM */
				accessCapabilities := {
					lengthIndicator := 0, /* overwritten */
					accessCapabilities := {
						rfPowerCapability := '001'B, /* FIXME */
						presenceBitA5 := '0'B,
						a5bits := omit,
						esind := '1'B,
						psbit := '0'B,
						vgcs := '0'B,
						vbs := '0'B,
						presenceBitMultislot := '1'B,
						multislotcap := {
							presenceBitHscsd := '0'B,
							hscsdmultislotclass := omit,
							presenceBitGprs := f_presence_bit_MultislotCap_GPRS_BSSGP(mscap_gprs),
							gprsmultislot := mscap_gprs,
							presenceBitSms := '0'B,
							multislotCap_SMS := omit,
							multislotCapAdditionsAfterRel97 := {
								presenceBitEcsdmulti := '0'B,
								ecsdmultislotclass := omit,
								presenceBitEgprsmulti := f_presence_bit_MultislotCap_EGPRS_BSSGP(mscap_egprs),
								multislotCap_EGPRS := mscap_egprs,
								presenceBitDtmGprsmulti := '0'B,
								multislotCapdtmgprsmultislotsubclass := omit
							}
						},
						accessCapAdditionsAfterRel97 := omit
					},
					spare_bits := omit
				}
			}
		},
		presenceBitMSRACap := '0'B
	};

	template QoS_Profile_V t_defaultQos := {
		peak_Bit_Rate := int2oct(80, 2),
		precedence := '000'B,
		a_bit := '0'B,
		t_bit := '0'B,
		c_r_bit := '0'B,
		peakBitRateGranularity := '00'B
	}

	template QoS_Profile ts_QoS_TLV(template QoS_Profile_V qos) := {
		iEI := '18'O,
		ext := '1'B,
		lengthIndicator := { length1 := 3 },
		peak_Bit_Rate := qos.peak_Bit_Rate,
		precedence := qos.precedence,
		a_bit := qos.a_bit,
		t_bit := qos.t_bit,
		c_r_bit := qos.c_r_bit,
		peakBitRateGranularity := qos.peakBitRateGranularity
	}

	template PDU_Lifetime t_DefaultLifetime(uint16_t delay := 65535) := {
		iEI := '16'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 2
		},
		delay_Value := f_oct_or_wc(delay, 2)
	}

	template DRX_Parameters t_defaultDRXparam := {
		iEI := '0A'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 2
		},
		splitPG_CycleCode := '00'O,
		nonDRXTimer := '000'B,
		splitOnCCCH := '0'B,
		cnSpecificDRXCycleLength := '0000'B
	}

	function ts_BSSGP_IE_MSRAcap_omit(template (omit) MSRadioAccessCapabilityV_BSSGP racap) return template (omit) MS_Radio_Access_Capability {
		var template (omit) MS_Radio_Access_Capability ret;
		if (istemplatekind(racap, "omit")) {
			return omit;
		} else {
			ret := {
				iEI := '00'O,
				ext := '1'B,
				lengthIndicator := {
					length1 := 1
				},
				mSRadioAccessCapabilityV_BSSGP := racap
			}
			return ret;
		}
	}

	template PDU_BSSGP ts_BSSGP_DL_UD(GprsTlli tlli, octetstring pdu,
					  template (omit) MSRadioAccessCapabilityV_BSSGP racap := omit,
					  template (omit) IMSI_BSSGP imsi := omit) := {
		pDU_BSSGP_DL_UNITDATA := {
			bssgpPduType := '00'O,
			tLLI_current := tlli,
			qoS_Profile := t_defaultQos,
			pDU_Lifetime := t_DefaultLifetime(65535),
			mS_Radio_Access_Capability := ts_BSSGP_IE_MSRAcap_omit(racap),
			priority := omit,
			dRX_Parameters := omit,
			iMSI := imsi,
			tLLI_old := omit,
			pFI := omit,
			lSA_Information := omit,
			service_UTRAN_CCO := omit,
			service_Class_Indicator := omit,
			subscriber_Profile_ID_For_RAT_Priority := omit,
			redirection_Indication := omit,
			redirection_Completed := omit,
			unconfirmed_Send_State_Variable := omit,
			sCI := omit,
			gGSN_PGW_Location := omit,
			eDRX_Paremeters := omit,
			old_Routing_Area_Identification := omit,
			attach_Indicator := omit,
			alignment_octets := omit,
			lLC_PDU := ts_BSSGP_LLC_PDU(pdu),
			initialLLC_PDU := omit
		}
	}

	template PDU_BSSGP tr_BSSGP_DL_UD := {
		pDU_BSSGP_DL_UNITDATA := {
			bssgpPduType := '00'O,
			tLLI_current := ?,
			qoS_Profile := ?,
			pDU_Lifetime := ?,
			mS_Radio_Access_Capability := *,
			priority := *,
			dRX_Parameters := *,
			iMSI := *,
			tLLI_old := *,
			pFI := *,
			lSA_Information := *,
			service_UTRAN_CCO := *,
			service_Class_Indicator := *,
			subscriber_Profile_ID_For_RAT_Priority := *,
			redirection_Indication := *,
			redirection_Completed := *,
			unconfirmed_Send_State_Variable := *,
			sCI := *,
			gGSN_PGW_Location := *,
			eDRX_Paremeters := *,
			old_Routing_Area_Identification := *,
			attach_Indicator := *,
			alignment_octets := *,
			lLC_PDU := tr_BSSGP_LLC_PDU,
			initialLLC_PDU := *
		}
	}

	template PDU_BSSGP ts_BSSGP_UL_UD(GprsTlli tlli, BssgpCellId cell_id, octetstring payload) := {
		pDU_BSSGP_UL_UNITDATA := {
			bssgpPduType := '01'O,
			tLLI := tlli,
			qoS_Profile := t_defaultQos,
			cell_Identifier := t_BSSGP_IE_CellId(cell_id),
			pFI := omit,
			lSA_Identifier_List := omit,
			redirect_Attempt_Flag := omit,
			iMSI_BSSGP := omit,
			unconfirmed_Send_State_Variable := omit,
			selected_PLMN_ID := omit,
			selected_Operator := omit,
			cS_Registered_Operator := omit,
			alignment_octets := omit,
			lLC_PDU := ts_BSSGP_LLC_PDU(payload)
		}
	}

	template PDU_BSSGP tr_BSSGP_UL_UD(template GprsTlli tlli := ?, template BssgpCellId cell_id := ?,
					  template octetstring payload := ?) := {
		pDU_BSSGP_UL_UNITDATA := {
			bssgpPduType := '01'O,
			tLLI := tlli,
			qoS_Profile := ?,
			cell_Identifier := t_BSSGP_IE_CellId(cell_id),
			pFI := *,
			lSA_Identifier_List := *,
			redirect_Attempt_Flag := *,
			iMSI_BSSGP := *,
			unconfirmed_Send_State_Variable := *,
			selected_PLMN_ID := *,
			selected_Operator := *,
			cS_Registered_Operator := *,
			alignment_octets := *,
			lLC_PDU := tr_BSSGP_LLC_PDU(payload)
		}
	}

	template PDU_BSSGP tr_BSSGP_CS_PAGING(BssgpBvci bvci) := {
		pDU_BSSGP_PAGING_CS := {
			bssgpPduType := '07'O,
			iMSI := ?,
			dRX_Parameters := ?,
			paging_Field4 := {
				bVCI := t_BSSGP_BVCI(bvci)
			},
			tLLI := *,
			channel_needed := *,
			eMLPP_Priority := *,
			tMSI := *,
			global_CN_Id := *
		}
	}

	template PDU_BSSGP ts_BSSGP_CS_PAGING_IMSI(BssgpBvci bvci, hexstring imsi) := {
		pDU_BSSGP_PAGING_CS := {
			bssgpPduType := '07'O,
			iMSI := ts_BSSGP_IMSI(imsi),
			dRX_Parameters := t_defaultDRXparam,
			paging_Field4 := {
				bVCI := t_BSSGP_BVCI(bvci)
			},
			tLLI := omit,
			channel_needed := omit,
			eMLPP_Priority := omit,
			tMSI := omit,
			global_CN_Id := omit
		}
	}

	template PDU_BSSGP ts_BSSGP_CS_PAGING_PTMSI(BssgpBvci bvci, hexstring imsi, GsmTmsi tmsi) := {
		pDU_BSSGP_PAGING_CS := {
			bssgpPduType := '07'O,
			iMSI := ts_BSSGP_IMSI(imsi),
			dRX_Parameters := t_defaultDRXparam,
			paging_Field4 := {
				bVCI := t_BSSGP_BVCI(bvci)
			},
			tLLI := omit,
			channel_needed := omit,
			eMLPP_Priority := omit,
			tMSI := ts_BSSGP_TMSI(tmsi),
			global_CN_Id := omit
		}
	}

	template PDU_BSSGP tr_BSSGP_PS_PAGING(template BssgpBvci bvci) := {
		pDU_BSSGP_PAGING_PS := {
			bssgpPduType := '06'O,
			iMSI := ?,
			dRX_Parameters := *,
			paging_Field4 := {
				bVCI := t_BSSGP_BVCI(bvci)
			},
			pFI := *,
			aBQP := *,
			qoS_Profile := ?,
			pTMSI := *,
			eDRX_Paremeters := *
		}
	}

	template PDU_BSSGP ts_BSSGP_PS_PAGING_IMSI(BssgpBvci bvci, hexstring imsi) := {
		pDU_BSSGP_PAGING_PS := {
			bssgpPduType := '06'O,
			iMSI := ts_BSSGP_IMSI(imsi),
			dRX_Parameters := omit,
			paging_Field4 := {
				bVCI := t_BSSGP_BVCI(bvci)
			},
			pFI := omit,
			aBQP := omit,
			qoS_Profile := ts_QoS_TLV(t_defaultQos),
			pTMSI := omit,
			eDRX_Paremeters := omit
		}
	}

	template PDU_BSSGP ts_BSSGP_PS_PAGING_PTMSI(BssgpBvci bvci, hexstring imsi, GsmTmsi tmsi) := {
		pDU_BSSGP_PAGING_PS := {
			bssgpPduType := '06'O,
			iMSI := ts_BSSGP_IMSI(imsi),
			dRX_Parameters := omit,
			paging_Field4 := {
				bVCI := t_BSSGP_BVCI(bvci)
			},
			pFI := omit,
			aBQP := omit,
			qoS_Profile := ts_QoS_TLV(t_defaultQos),
			pTMSI := ts_BSSGP_TMSI(tmsi),
			eDRX_Paremeters := omit
		}
	}

	template (value) Routeing_Area ts_BSSGP_RA_ID(RoutingAreaIdentification input) := {
		iEI := '1B'O,
		ext := '1'B,
		lengthIndicator := {
			length1 := 6
		},
		mccDigit1 := input.lai.mcc_mnc[0],
		mccDigit2 := input.lai.mcc_mnc[1],
		mccDigit3 := input.lai.mcc_mnc[2],
		mncDigit3 := input.lai.mcc_mnc[3],
		mncDigit1 := input.lai.mcc_mnc[4],
		mncDigit2 := input.lai.mcc_mnc[5],
		lac := int2oct(input.lai.lac, 2),
		rac := int2oct(input.rac, 1)
	}

	template (value) PDU_BSSGP ts_BSSGP_SUSPEND(GprsTlli tlli, RoutingAreaIdentification ra_id) := {
		pDU_BSSGP_SUSPEND := {
			bssgpPduType := '0B'O,
			tLLI := ts_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id)
		}
	}
	template PDU_BSSGP tr_BSSGP_SUSPEND(template GprsTlli tlli, RoutingAreaIdentification ra_id) := {
		pDU_BSSGP_SUSPEND := {
			bssgpPduType := '0B'O,
			tLLI := tr_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id)
		}
	}

	template (value) PDU_BSSGP ts_BSSGP_SUSPEND_ACK(GprsTlli tlli, RoutingAreaIdentification ra_id,
							template (value) OCT1 susp_ref) := {
		pDU_BSSGP_SUSPEND_ACK := {
			bssgpPduType := '0C'O,
			tLLI := ts_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id),
			suspend_Reference_Number := ts_BSSGP_SUSP_REF(susp_ref)
		}
	}
	template PDU_BSSGP tr_BSSGP_SUSPEND_ACK(GprsTlli tlli, RoutingAreaIdentification ra_id,
						template OCT1 susp_ref) := {
		pDU_BSSGP_SUSPEND_ACK := {
			bssgpPduType := '0C'O,
			tLLI := tr_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id),
			suspend_Reference_Number := tr_BSSGP_SUSP_REF(susp_ref)
		}
	}

	template (value) PDU_BSSGP ts_BSSGP_SUSPEND_NACK(GprsTlli tlli, RoutingAreaIdentification ra_id,
							 template (omit) BssgpCause cause) := {
		pDU_BSSGP_SUSPEND_NACK := {
			bssgpPduType := '0D'O,
			tLLI := ts_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id),
			cause := ts_BSSGP_CAUSE(cause)
		}
	}
	template PDU_BSSGP tr_BSSGP_SUSPEND_NACK(GprsTlli tlli, RoutingAreaIdentification ra_id,
						template BssgpCause cause) := {
		pDU_BSSGP_SUSPEND_NACK := {
			bssgpPduType := '0D'O,
			tLLI := tr_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id),
			cause := t_BSSGP_CAUSE(cause)
		}
	}



	template (value) PDU_BSSGP ts_BSSGP_RESUME(GprsTlli tlli, RoutingAreaIdentification ra_id,
						   OCT1 susp_ref) := {
		pDU_BSSGP_RESUME := {
			bssgpPduType := '0E'O,
			tLLI := ts_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id),
			suspend_Reference_Number := ts_BSSGP_SUSP_REF(susp_ref)
		}
	}
	template PDU_BSSGP tr_BSSGP_RESUME(template GprsTlli tlli, RoutingAreaIdentification ra_id,
					   template OCT1 susp_ref) := {
		pDU_BSSGP_RESUME := {
			bssgpPduType := '0E'O,
			tLLI := tr_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id),
			suspend_Reference_Number := tr_BSSGP_SUSP_REF(susp_ref)
		}
	}

	template (value) PDU_BSSGP ts_BSSGP_RESUME_ACK(GprsTlli tlli, RoutingAreaIdentification ra_id)
		:= {
		pDU_BSSGP_RESUME_ACK := {
			bssgpPduType := '0F'O,
			tLLI := ts_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id)
		}
	}
	template PDU_BSSGP tr_BSSGP_RESUME_ACK(template GprsTlli tlli,
						RoutingAreaIdentification ra_id) := {
		pDU_BSSGP_RESUME_ACK := {
			bssgpPduType := '0F'O,
			tLLI := tr_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id)
		}
	}

	template (value) PDU_BSSGP ts_BSSGP_RESUME_NACK(GprsTlli tlli, RoutingAreaIdentification ra_id,
						template (omit) BssgpCause cause) := {
		pDU_BSSGP_RESUME_NACK := {
			bssgpPduType := '10'O,
			tLLI := ts_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id),
			cause := ts_BSSGP_CAUSE(cause)
		}
	}
	template PDU_BSSGP tr_BSSGP_RESUME_NACK(template GprsTlli tlli,
						RoutingAreaIdentification ra_id,
						template BssgpCause cause) := {
		pDU_BSSGP_RESUME_NACK := {
			bssgpPduType := '10'O,
			tLLI := tr_BSSGP_TLLI(tlli),
			routeing_Area := ts_BSSGP_RA_ID(ra_id),
			cause := t_BSSGP_CAUSE(cause)
		}
	}




} with { encode "RAW" };
