osmo-ttcn3-hacks/bts/BTS_Tests.ttcn

8491 lines
272 KiB
Plaintext
Raw Normal View History

module BTS_Tests {
/* Integration Tests for OsmoBTS
* (C) 2018-2019 by Harald Welte <laforge@gnumonks.org>
* contributions by Vadim Yanitskiy and sysmocom - s.f.m.c. GmbH
* All rights reserved.
*
* Released under the terms of GNU General Public License, Version 2 or
* (at your option) any later version.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This test suite tests OsmoBTS by attaching to the external interfaces
* such as Abis RSL, PCU, VTY as well as by attaching to a MS L1 implementation
* using the L1CTL protocol/interface.
*
* You can run the tests with
* a) osmo-bts-trx + trxcon + fake_trx (without any hardware)
* b) any osmo-bts-* + OsmocomBB layer1 + osmocon (with real BTS hardware)
* c) osmo-bts-virtual + virt_phy (without any hardware)
*
* Some of the tests will only run on a subset of those three configurations
* due to limitations in the respective L1.
*/
import from Misc_Helpers all;
import from General_Types all;
import from GSM_Types all;
import from GSM_RR_Types all;
import from Osmocom_Types all;
import from GSM_Types all;
import from GSM_RestOctets all;
import from GSM_SystemInformation all;
import from L1CTL_PortType all;
import from L1CTL_Types all;
import from LAPDm_Types all;
import from LAPDm_RAW_PT all;
import from Native_Functions all;
import from Osmocom_CTRL_Adapter all;
import from Osmocom_CTRL_Functions all;
import from RSL_Types all;
import from RTP_Types all;
import from IPA_Types all;
import from IPA_Emulation all;
import from IPA_Testing all;
import from RSL_Emulation all;
import from RTP_Emulation all;
import from IPL4asp_Types all;
import from TRXC_Types all;
import from TRXC_CodecPort all;
import from TRXC_CodecPort_CtrlFunct all;
import from PCUIF_Types all;
import from PCUIF_CodecPort all;
import from UD_Types all;
import from MobileL3_CommonIE_Types all;
import from MobileL3_RRM_Types all;
import from MobileL3_Types all;
import from L3_Templates all;
import from L3_Common all;
import from MobileL3_GMM_SM_Types all;
import from Osmocom_VTY_Functions all;
import from TELNETasp_PortType all;
import from BTS_Tests_LAPDm all;
friend module BTS_Tests_SMSCB;
friend module BTS_Tests_VAMOS;
friend module BTS_Tests_virtphy;
friend module BTS_Tests_LAPDm;
friend module BTS_Tests_perf;
friend module BTS_Tests_OML;
/* The tests assume a BTS with the following timeslot configuration:
* TS0 : Combined CCCH + SDCCH/4
* TS1 : TCH/F
* TS2 : TCH/F
* TS3 : TCH/F_PDCH (IPA Style)
* TS4 : TCH/F_TCH/H_PDCH (Osmocom Style)
* TS5 : TCH/H
* TS6 : SDCCH/8
* TS7 : PDCH
*/
modulepar {
charstring mp_rsl_ip := "127.0.0.2";
integer mp_rsl_port := 3003;
charstring mp_bts_trxc_ip := "127.0.0.1";
integer mp_bts_trxc_port := 5701;
charstring mp_pcu_socket := PCU_SOCK_DEFAULT;
charstring mp_ctrl_ip := "127.0.0.1";
integer mp_ctrl_port := 4238;
charstring mp_bsc_ctrl_ip := "127.0.0.1";
integer mp_bsc_ctrl_port := 4249;
charstring mp_rtpem_bind_ip := "127.0.0.1";
integer mp_rtpem_bind_port := 6766;
integer mp_tolerance_rxqual := 1;
integer mp_tolerance_rxlev := 3;
integer mp_tolerance_timing_offset_256syms := 0;
integer mp_rxlev_exp := 57;
integer mp_ul_rxlev_exp := 10;
integer mp_ms_power_level_exp := 7;
integer mp_bts_tx_nom_pwr_exp := 50; /* Expected Tx Nominal Output Power of the BTS, in dBm */
integer mp_bts_tx_pwr_att_exp := 20; /* Expected Tx Power attenuation wrt to Tx Nominal Output Power, in dB */
integer mp_ms_actual_ta_exp := 0;
integer mp_timing_offset_256syms_exp := 512;
integer mp_uplink_power_target := -75;
integer mp_uplink_power_hysteresis := 8; /* -83 .. -67 */
/* Time to wait for RSL conn from BTS during startup of test */
float mp_ipa_up_timeout := 15.0;
float mp_ipa_up_delay := 0.0;
/* false for now, as only virtphy supports it, not calypso-l1 nor trxcon */
boolean mp_l1_supports_gprs := false;
/* how many transceivers do we expect to connect */
integer mp_transceiver_num := 1;
/* frequency hopping status */
boolean mp_freq_hop_enabled := false;
/* frequency hopping parameters */
FreqHopConfig mp_fh_config;
/* configuration for each individual transceiver */
TrxPars mp_trx_pars; /* see BTS_Tests.default */
/* default Training Sequence Code */
GsmTsc mp_tsc_def := 7;
/* Default interference boundaries in osmo-bsc (in dBm):
* 0(-85) X1(-91) X2(-97) X3(-103) X4(-109) X5(-115)
* Default interference level reported by fake_trx.py:
* 0: -109 >= -85 dBm (no)
* X1: -109 >= -91 dBm (no)
* X2: -109 >= -97 dBm (no)
* X3: -109 >= -103 dBm (no)
* X4: -109 >= -109 dBm (yes)
* X5: -109 >= -115 dBm (no)
* So all channels should be in band 4. */
uint3_t mp_interf_band := 4;
}
type record of RslChannelNr ChannelNrs;
type component test_CT extends CTRL_Adapter_CT {
/* IPA Emulation component underneath RSL */
var IPA_Emulation_CT vc_IPA;
/* RSL Emulation component (for ConnHdlr tests) */
var RSL_Emulation_CT vc_RSL;
/* Direct RSL_CCHAN_PT */
port RSL_CCHAN_PT RSL_CCHAN;
/* L1CTL port (for classic tests) */
port L1CTL_PT L1CTL;
/* Optional TRXC connection to FakeTRX (BTS side) */
port TRXC_CODEC_PT BTS_TRXC;
var integer g_bts_trxc_conn_id;
/* VTY connections to both BTS and BSC */
port TELNETasp_PT BTSVTY;
port TELNETasp_PT BSCVTY;
/* PCU Interface of BTS */
port PCUIF_CODEC_PT PCU;
var integer g_pcu_conn_id;
/* Last PCU INFO IND we received */
var PCUIF_Message g_pcu_last_info;
/* SI configuration */
var SystemInformationConfig si_cfg := {
bcch_extended := false,
si1_present := false,
si2bis_present := false,
si2ter_present := false,
si2quater_present := false,
si7_present := false,
si8_present := false,
si9_present := false,
si13_present := false,
si13alt_present := false,
si15_present := false,
si16_present := false,
si17_present := false,
si2n_present := false,
si21_present := false,
si22_present := false
};
/* all logical channels available on the BTS */
var ChannelNrs g_AllChannels;
var ChannelNrs g_AllChanTypes;
}
/* an individual call / channel */
type component ConnHdlr extends RSL_DchanHdlr, lapdm_test_CT {
port L1CTL_PT L1CTL;
/* Optional TRXC connection to FakeTRX (BTS side) */
port TRXC_CODEC_PT BTS_TRXC;
var integer g_bts_trxc_conn_id;
/* port to be initialized optionally to access BSC VTY */
port TELNETasp_PT BSCVTY;
timer g_Tguard;
timer g_Tmeas_exp := 2.0; /* >= 103 SACCH multiframe ~ 500ms */
var ConnHdlrPars g_pars;
var uint8_t g_next_meas_res_nr := 0;
var boolean g_first_meas_res := true;
/* PCU Interface of BTS */
port PCUIF_CODEC_PT PCU;
var RTP_Emulation_CT vc_RTPEM;
port RTPEM_CTRL_PT RTPEM_CTRL;
port RTPEM_DATA_PT RTPEM_DATA;
}
private function f_init_rsl(charstring id) runs on test_CT {
var bitstring trx_mask := '00000000'B;
var bitstring rfind_mask := '00000000'B;
var integer trx_count := 0;
var integer rfind_count := 0;
var RSLEm_Event ev;
var ASP_RSL_Unitdata rx_ud;
timer T;
vc_IPA := IPA_Emulation_CT.create(id & "-RSL-IPA");
vc_RSL := RSL_Emulation_CT.create(id & "-RSL");
map(vc_IPA:IPA_PORT, system:IPA_CODEC_PT);
connect(vc_IPA:IPA_RSL_PORT, vc_RSL:IPA_PT);
connect(self:RSL_CCHAN, vc_RSL:CCHAN_PT);
vc_IPA.start(IPA_Emulation.main_server(mp_rsl_ip, mp_rsl_port));
vc_RSL.start(RSL_Emulation.main(false));
/* We expect (N = mp_transceiver_num) IPA/RSL transceiver connections here.
* See https://gerrit.osmocom.org/q/Ib5ad31388ae25399ad09739aac3fdcb0b3a1f78b. */
T.start(mp_ipa_up_timeout);
alt {
/* These events are sent by the RSL_Emulation_CT */
[] RSL_CCHAN.receive(tr_RSLEm_EV(RSLEM_EV_TRX_UP, ?)) -> value ev {
/* Make sure that all transceivers use unique stream ID */
if (trx_mask[enum2int(ev.sid)] == '1'B) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Duplicate RSL stream ID (", ev.sid, ")"));
}
trx_mask[enum2int(ev.sid)] := '1'B;
trx_count := trx_count + 1;
log(trx_count, "/", mp_transceiver_num, " transceiver(s) connected");
repeat;
}
/* This message (RF RESource INDication) is sent by the IUT itself */
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RF_RES_IND, ?)) -> value rx_ud {
if (trx_mask[enum2int(rx_ud.streamId)] == '0'B) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Got RF Resource Indication before RSLEM_EV_TRX_UP (", rx_ud.streamId, ")"));
}
if (rfind_mask[enum2int(rx_ud.streamId)] == '1'B) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Duplicate RF Resource Indication ID (", rx_ud.streamId, ")"));
}
rfind_mask[enum2int(rx_ud.streamId)] := '1'B;
rfind_count := rfind_count + 1;
log(rfind_count, "/", mp_transceiver_num, " RF Resource Indication(s) received");
if (rfind_count < mp_transceiver_num) { repeat; }
}
/* osmo-bts may send us CCCH LOAD INDication or whatever else */
[] RSL_CCHAN.receive(ASP_RSL_Unitdata:?) { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Timeout waiting for RSL bring up");
}
}
}
friend type record length(1 .. 256) of TrxParsItem TrxPars;
friend type record TrxParsItem {
GsmArfcn arfcn,
TrxTsPars ts
};
friend type record length(8) of TrxTsParsItem TrxTsPars;
friend type record TrxTsParsItem {
PchanConfig config
};
friend type record FreqHopPars {
/* Whether frequency hopping is in use */
boolean enabled,
/* Mobile Allocation Index Offset / Hopping Sequence Number */
MaioHsn maio_hsn,
/* MA bitmap to be indicated in RR Immediate Assignment */
MobileAllocationLV ma_map,
/* The actual Mobile Allocation (ARFCN list) to be used */
L1ctlMA ma
};
type record ConnHdlrPars {
uint8_t trx_nr,
RslChannelNr chan_nr,
RSL_IE_ChannelMode chan_mode,
float t_guard,
ConnL1Pars l1_pars,
TestSpecUnion spec optional,
RSL_IE_EncryptionInfo encr optional,
BtsBand bts0_band optional,
/* Training Sequence Code */
GsmTsc tsc,
/* Frequency hopping parameters */
FreqHopPars fhp
};
/* Test-specific parameters */
friend type union TestSpecUnion {
RllTestCase rll,
TopTestCase top
}
private template (value) RachControlParameters ts_RachCtrl_default := {
max_retrans := RACH_MAX_RETRANS_7,
tx_integer := '1001'B, /* 12 slots */
cell_barr_access := false,
re_not_allowed := true,
acc := '0000010000000000'B
};
private template (value) CellSelectionParameters ts_CellSelPar_default := {
cell_resel_hyst_2dB := 2,
ms_txpwr_max_cch := mp_ms_power_level_exp,
acs := '0'B,
neci := true,
rxlev_access_min := 0
}
private template (value) LocationAreaIdentification ts_LAI_default := {
mcc_mnc := '262F42'H,
lac := 42
}
private template (value) GPRSIndicator ts_GPRSIndicator_def := {
ra_colour := 0,
si13_pos := '0'B
}
private template (value) SI3RestOctets ts_SI3RestOctets_def
modifies ts_SI3RestOctets := {
gprs_ind := {
presence := CSN1_H,
ind := ts_GPRSIndicator_def
}
}
private template (value) SI4RestOctets ts_SI4RestOctets_def
modifies ts_SI4RestOctets := {
gprs_ind := {
presence := CSN1_H,
ind := ts_GPRSIndicator_def
}
}
/* Default SYSTEM INFORMATION 3 */
private template (value) SystemInformation ts_SI3_default := {
header := ts_RrHeader(SYSTEM_INFORMATION_TYPE_3, 18),
payload := {
si3 := {
cell_id := 23,
lai := ts_LAI_default,
ctrl_chan_desc := {
msc_r99 := true,
att := true,
bs_ag_blks_res := 1,
ccch_conf := CCHAN_DESC_1CCCH_COMBINED,
si22ind := false,
cbq3 := CBQ3_IU_MODE_NOT_SUPPORTED,
spare := '00'B,
bs_pa_mfrms := 0, /* 2 multiframes */
t3212 := 1 /* 6 minutes */
},
cell_options := {
dn_ind := false,
pwrc := false,
dtx := MS_MAY_USE_UL_DTX,
radio_link_tout_div4 := (32/4)-1
},
cell_sel_par := ts_CellSelPar_default,
rach_control := ts_RachCtrl_default,
rest_octets := valueof(ts_SI3RestOctets_def)
}
}
}
private template (value) SystemInformation ts_SI2_default := {
header := ts_RrHeader(SYSTEM_INFORMATION_TYPE_2, 22),
payload := {
si2 := {
bcch_freq_list := '00000000000000000000000000000000'O,
ncc_permitted := '11111111'B,
rach_control := ts_RachCtrl_default
}
}
}
private template (value) SystemInformation ts_SI4_default := {
header := ts_RrHeader(SYSTEM_INFORMATION_TYPE_4, 12), /* no CBCH / restoct */
payload := {
si4 := {
lai := ts_LAI_default,
cell_sel_par := ts_CellSelPar_default,
rach_control := ts_RachCtrl_default,
cbch_chan_desc := omit,
cbch_mobile_alloc := omit,
rest_octets := valueof(ts_SI4RestOctets_def)
}
}
}
private function f_rsl_bcch_fill_raw(RSL_IE_SysinfoType rsl_si_type, octetstring si_enc)
runs on test_CT {
log("Setting ", rsl_si_type, ": ", si_enc);
RSL_CCHAN.send(ts_ASP_RSL_UD(ts_RSL_BCCH_INFO(rsl_si_type, si_enc)));
}
private function f_rsl_bcch_fill(RSL_IE_SysinfoType rsl_si_type, template (value) SystemInformation si_dec)
runs on test_CT {
var octetstring si_enc := enc_SystemInformation(valueof(si_dec));
log("Setting ", rsl_si_type, ": ", si_dec);
f_rsl_bcch_fill_raw(rsl_si_type, si_enc);
}
friend function f_init_vty(charstring id) runs on test_CT {
map(self:BTSVTY, system:BTSVTY);
f_vty_set_prompts(BTSVTY);
f_vty_transceive(BTSVTY, "enable");
}
friend function f_init_vty_bsc() runs on test_CT {
map(self:BSCVTY, system:BSCVTY);
f_vty_set_prompts(BSCVTY, "OsmoBSC");
f_vty_transceive(BSCVTY, "enable");
}
friend function f_connhdlr_init_vty_bsc() runs on ConnHdlr {
map(self:BSCVTY, system:BSCVTY);
f_vty_set_prompts(BSCVTY, "OsmoBSC");
f_vty_transceive(BSCVTY, "enable");
}
/* PCU socket may at any time receive a new INFO.ind */
friend altstep as_pcu_info_ind(PCUIF_CODEC_PT pt, integer pcu_conn_id,
out PCUIF_Message pcu_last_info) {
var PCUIF_send_data sd;
[] pt.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_INFO_IND(0, ?))) -> value sd {
pcu_last_info := sd.data;
}
[] pt.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_INFO_IND(?, ?, ?))) -> value sd {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Invalid PCU Version/BTS Number received");
}
}
private function f_init_pcu(PCUIF_CODEC_PT pt, charstring id,
out integer pcu_conn_id, out PCUIF_Message pcu_last_info) {
timer T := 2.0;
var PCUIF_send_data sd;
if (mp_pcu_socket == "") {
pcu_conn_id := -1;
return;
}
pcu_conn_id := f_pcuif_connect(pt, mp_pcu_socket);
T.start;
alt {
[] as_pcu_info_ind(pt, pcu_conn_id, pcu_last_info);
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for PCU INFO_IND");
}
}
}
private function f_init_trxc(TRXC_CODEC_PT pt, charstring id,
out integer trxc_conn_id) {
var Result res;
res := TRXC_CodecPort_CtrlFunct.f_IPL4_connect(pt, mp_bts_trxc_ip, mp_bts_trxc_port,
"", -1, -1, { udp := {} }, {});
if (not ispresent(res.connId)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Could not connect to the control (TRXC) interface " &
"of FakeTRX, check your configuration");
}
trxc_conn_id := res.connId;
}
/* global init function (without PCUIF connection) */
friend function f_init(uint8_t trx_nr := 0)
runs on test_CT
{
var TrxParsItem trx_pars := mp_trx_pars[trx_nr];
var charstring id := testcasename();
/* FIXME: FACCH/H is unreliable with calypso firmware, see OS#3653.
* TODO: also generate this list dynamically from module parameters. */
if (mp_bts_trxc_port != -1) {
g_AllChanTypes := {
/* TS 1..4: TCH/F */
valueof(ts_RslChanNr_Bm(1)),
/* TS 5: TCH/H */
valueof(ts_RslChanNr_Lm(5,1)),
/* TS 0: SDCCH/4 */
valueof(ts_RslChanNr_SDCCH4(0,2)),
/* TS 6: SDCCH/8 */
valueof(ts_RslChanNr_SDCCH8(6,4))
};
} else {
g_AllChanTypes := {
/* TS 1..4: TCH/F */
valueof(ts_RslChanNr_Bm(1)),
/* TS 0: SDCCH/4 */
valueof(ts_RslChanNr_SDCCH4(0,2)),
/* TS 6: SDCCH/8 */
valueof(ts_RslChanNr_SDCCH8(6,4))
};
}
g_AllChannels := { };
/* Generate list of all logical channels from module parameters */
for (var integer tn := 0; tn < lengthof(trx_pars.ts); tn := tn + 1) {
select (trx_pars.ts[tn].config) {
case (GSM_PCHAN_CCCH_SDCCH4) {
g_AllChannels := g_AllChannels &
{ valueof(ts_RslChanNr_SDCCH4(tn, 0)),
valueof(ts_RslChanNr_SDCCH4(tn, 1)),
valueof(ts_RslChanNr_SDCCH4(tn, 2)),
valueof(ts_RslChanNr_SDCCH4(tn, 3)) };
}
case (GSM_PCHAN_SDCCH8) {
g_AllChannels := g_AllChannels &
{ valueof(ts_RslChanNr_SDCCH8(tn, 0)),
valueof(ts_RslChanNr_SDCCH8(tn, 1)),
valueof(ts_RslChanNr_SDCCH8(tn, 2)),
valueof(ts_RslChanNr_SDCCH8(tn, 3)),
valueof(ts_RslChanNr_SDCCH8(tn, 4)),
valueof(ts_RslChanNr_SDCCH8(tn, 5)),
valueof(ts_RslChanNr_SDCCH8(tn, 6)),
valueof(ts_RslChanNr_SDCCH8(tn, 7)) };
}
case (GSM_PCHAN_TCHH_TCHF_PDCH) {
g_AllChannels := g_AllChannels &
{ valueof(ts_RslChanNr_Lm(tn, 0)),
valueof(ts_RslChanNr_Lm(tn, 1)),
valueof(ts_RslChanNr_Bm(tn)) };
}
case (GSM_PCHAN_TCHH) {
g_AllChannels := g_AllChannels &
{ valueof(ts_RslChanNr_Lm(tn, 0)),
valueof(ts_RslChanNr_Lm(tn, 1)) };
}
case (GSM_PCHAN_TCHF, GSM_PCHAN_TCHF_PDCH) {
g_AllChannels := g_AllChannels &
{ valueof(ts_RslChanNr_Bm(tn)) };
}
}
}
f_init_rsl(id);
f_sleep(0.5); /* workaround for OS#3000 */
f_init_vty(id);
f_ipa_ctrl_start_client(mp_ctrl_ip, mp_ctrl_port);
/* Send SI3 to the BTS, it is needed for various computations */
f_rsl_bcch_fill(RSL_SYSTEM_INFO_3, ts_SI3_default);
/* SI2 + SI4 are required for SI testing as they are mandatory defaults */
f_rsl_bcch_fill(RSL_SYSTEM_INFO_2, ts_SI2_default);
f_rsl_bcch_fill(RSL_SYSTEM_INFO_4, ts_SI4_default);
if (mp_bts_trxc_port != -1) {
var TrxcMessage ret;
/* Init TRXC interface to FakeTRX */
map(self:BTS_TRXC, system:BTS_TRXC);
f_init_trxc(BTS_TRXC, id, g_bts_trxc_conn_id);
/* Start with a default moderate timing offset equalling TA=2, and RSSI=-60 */
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, ts_TRXC_FAKE_TIMING(2*256));
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, ts_TRXC_FAKE_RSSI(-60));
/* OsmoBTS may have different AB / NB threshold (see MIN_QUAL_NORM, MIN_QUAL_RACH) */
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, ts_TRXC_FAKE_CI(60));
}
/* Wait some extra time to make sure the BTS emits a stable carrier.
* (this is only relevant when running the tests with a physical BTS.) */
f_sleep(mp_ipa_up_delay);
}
/* Attach L1CTL to master test_CT (classic tests, non-handler mode) */
friend function f_init_l1ctl() runs on test_CT {
map(self:L1CTL, system:L1CTL);
f_connect_reset(L1CTL);
}
friend type function void_fn(charstring id) runs on ConnHdlr;
private type record length(8) of FreqHopGroups FreqHopConfig;
private type record of FreqHopGroup FreqHopGroups;
private type record FreqHopGroup {
uint6_t hsn,
FreqHopGroupItems trx_maio
};
private type record of FreqHopGroupItem FreqHopGroupItems;
private type record FreqHopGroupItem {
uint8_t trx_nr,
uint6_t maio
};
friend function f_resolve_fh_params(inout FreqHopPars fhp, uint8_t tn,
uint8_t trx_nr := 0)
{
var FreqHopGroups groups := mp_fh_config[tn];
var integer i, j;
fhp.enabled := false;
for (i := 0; i < lengthof(groups); i := i + 1) {
var FreqHopGroup g := groups[i];
for (j := 0; j < lengthof(g.trx_maio); j := j + 1) {
var FreqHopGroupItem gi := g.trx_maio[j];
if (gi.trx_nr == trx_nr) {
fhp.maio_hsn.maio := gi.maio;
fhp.maio_hsn.hsn := g.hsn;
fhp.enabled := true;
break;
}
}
if (fhp.enabled) {
/* Prepare the Mobile Allocation bitmask (length & padding) */
fhp.ma_map.len := (mp_transceiver_num + 8 - 1) / 8; /* in bytes */
fhp.ma_map.ma := f_pad_bit('0'B, fhp.ma_map.len * 8, '0'B);
fhp.ma := { }; /* to be composed below */
/* Compose the actual Mobile Allocation and the bitmask */
for (j := 0; j < lengthof(g.trx_maio); j := j + 1) {
var FreqHopGroupItem gi := g.trx_maio[j];
var GsmArfcn arfcn := mp_trx_pars[gi.trx_nr].arfcn;
fhp.ma := fhp.ma & { valueof(ts_GsmBandArfcn(arfcn)) };
fhp.ma_map.ma[gi.trx_nr] := '1'B;
}
log("Freq. hopping parameters: ", fhp);
break; /* We're done */
}
}
}
/* create a new test component */
friend function f_start_handler(void_fn fn, ConnHdlrPars pars,
boolean pcu_comp := false,
boolean trxc_comp := false,
boolean l1ctl := true)
runs on test_CT return ConnHdlr {
var charstring id := testcasename();
var ConnHdlr vc_conn;
vc_conn := ConnHdlr.create(id);
/* connect to RSL Emulation main component */
connect(vc_conn:RSL, vc_RSL:CLIENT_PT);
connect(vc_conn:RSL_PROC, vc_RSL:RSL_PROC);
/* The ConnHdlr component may want to talk to some ports directly,
* so we disconnect it from the test_CT and connect it to the component.
* This obviously only works for one component, i.e. no concurrency. */
if (pcu_comp) {
unmap(self:PCU, system:PCU);
map(vc_conn:PCU, system:PCU);
}
if (trxc_comp) {
unmap(self:BTS_TRXC, system:BTS_TRXC);
map(vc_conn:BTS_TRXC, system:BTS_TRXC);
}
if (l1ctl) {
map(vc_conn:L1CTL, system:L1CTL);
}
/* Obtain frequency hopping parameters for a given timeslot */
if (mp_freq_hop_enabled and mp_transceiver_num > 1) {
f_resolve_fh_params(pars.fhp, pars.chan_nr.tn);
}
vc_conn.start(f_handler_init(fn, id, pars));
return vc_conn;
}
private altstep as_Tguard() runs on ConnHdlr {
[] g_Tguard.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Tguard timeout");
}
}
friend function f_l1_tune(L1CTL_PT L1CTL, L1ctlCcchMode ccch_mode := CCCH_MODE_COMBINED) {
var GsmBandArfcn arfcn := valueof(ts_GsmBandArfcn(mp_trx_pars[0].arfcn));
f_L1CTL_FBSB(L1CTL, arfcn, ccch_mode, mp_rxlev_exp);
}
private function f_trxc_fake_rssi(TRXC_RSSI rssi) runs on ConnHdlr {
var TrxcMessage ret;
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, ts_TRXC_FAKE_RSSI(rssi));
}
private function f_trxc_fake_toffs256(int16_t toffs256) runs on ConnHdlr {
var TrxcMessage ret;
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, ts_TRXC_FAKE_TIMING(toffs256));
}
/* first function started in ConnHdlr component */
private function f_handler_init(void_fn fn, charstring id, ConnHdlrPars pars)
runs on ConnHdlr {
g_pars := pars;
g_chan_nr := pars.chan_nr;
if (L1CTL.checkstate("Mapped")) {
f_connect_reset(L1CTL);
}
if (mp_bts_trxc_port != -1 and BTS_TRXC.checkstate("Mapped")) {
f_init_trxc(BTS_TRXC, id, g_bts_trxc_conn_id);
}
g_Tguard.start(pars.t_guard);
activate(as_Tguard());
f_rslem_register(pars.trx_nr, pars.chan_nr);
/* call the user-supplied test case function */
fn.apply(id);
}
private function f_rsl_transceive_ret(template RSL_Message tx, template RSL_Message exp_rx,
charstring id, boolean ignore_other := false)
runs on ConnHdlr return RSL_Message {
var RSL_Message rx;
timer T := 3.0;
RSL.send(tx);
T.start;
alt {
[] RSL.receive(exp_rx) -> value rx {
T.stop;
setverdict(pass);
}
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout expecting " & id);
}
[not ignore_other] as_l1_sacch();
[not ignore_other] as_meas_res();
[not ignore_other] as_l1_dcch_loop();
[not ignore_other] as_l1_tch_loop();
[not ignore_other] RSL.receive {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected RSL message received");
}
[ignore_other] RSL.receive { repeat; }
}
return rx;
}
friend function f_rsl_transceive(template RSL_Message tx, template RSL_Message exp_rx,
charstring id, boolean ignore_other := false)
runs on ConnHdlr {
var RSL_Message rx := f_rsl_transceive_ret(tx, exp_rx, id, ignore_other);
}
/* Send the given measurement results to the IUT over the Um interface,
* wait for the IUT to receive then and forward over the A-bis/RSL interface. */
friend function f_transceive_meas_rep(template (value) MeasurementResults meas_res)
runs on ConnHdlr {
var template (value) SacchL1Header l1h;
var octetstring l2, l3;
timer T;
/* RR Measurement Report to be sent */
var GsmRrL3Message meas_rep := {
header := valueof(t_RrL3Header(MEASUREMENT_REPORT)),
payload := { meas_rep := { meas_res := valueof(meas_res) } }
};
/* TITAN has weird (and often unusable) padding model, so we pad here manaully */
l3 := f_pad_oct(enc_GsmRrL3Message(meas_rep), 18, '00'O);
l2 := f_pad_oct(enc_LapdmFrameAB(valueof(ts_LAPDm_AB(0, meas_rep))), 21, '00'O);
l1h := ts_SacchL1Header(g_pars.l1_pars.ms_power_level, false,
g_pars.l1_pars.ms_actual_ta);
/* Send RR Measurement Report over the Um interface */
L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(g_chan_nr, ts_RslLinkID_SACCH(0), l1h, l2));
/* Expect MEASurement RESult on the A-bis/RSL interface */
T.start(2.0);
alt {
[] RSL.receive(tr_RSL_MEAS_RES_OSMO(g_chan_nr, l3_info := l3)) {
setverdict(pass);
}
[] RSL.receive { repeat; }
[] T.timeout {
setverdict(fail, "Timeout waiting for RSL MEASurement RESult");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
}
}
friend function f_rsl_chan_act(RSL_IE_ChannelMode mode,
boolean encr_enable := false,
RSL_IE_List more_ies := {},
RSL_IE_ActivationType act_type := t_RSL_IE_ActType_IA)
runs on ConnHdlr {
var RSL_Message ch_act := valueof(ts_RSL_CHAN_ACT(g_chan_nr, mode, act_type));
if (encr_enable) {
/* append encryption related IEs, if requested */
var RSL_IE_EncryptionInfo encr_info;
encr_info := valueof(ts_RSL_IE_EncrInfo(g_pars.encr.alg_id, g_pars.encr.key));
ch_act.ies := ch_act.ies & { valueof(t_RSL_IE(RSL_IE_ENCR_INFO, RSL_IE_Body:{encr_info :=
encr_info})) };
}
ch_act.ies := ch_act.ies & more_ies;
f_rsl_transceive(ch_act, tr_RSL_CHAN_ACT_ACK(g_chan_nr), "RSL CHAN ACT");
}
friend function f_rsl_chan_deact() runs on ConnHdlr {
f_rsl_transceive(ts_RSL_RF_CHAN_REL(g_chan_nr), tr_RSL_RF_CHAN_REL_ACK(g_chan_nr),
"RF CHAN REL", true);
}
friend template ConnHdlrPars t_Pars(template RslChannelNr chan_nr,
template RSL_IE_ChannelMode chan_mode,
template (omit) TestSpecUnion spec := omit,
uint8_t trx_nr := 0, float t_guard := 20.0) := {
trx_nr := trx_nr,
chan_nr := valueof(chan_nr),
chan_mode := valueof(chan_mode),
t_guard := t_guard,
l1_pars := {
dtx_enabled := false,
toa256_enabled := false,
meas_valid := true,
meas_ul := {
full := {
rxlev := mp_ul_rxlev_exp,
rxqual := 0
},
sub := {
rxlev := mp_ul_rxlev_exp,
rxqual := 0
}
},
timing_offset_256syms := mp_timing_offset_256syms_exp,
bs_power_level := 0,
ms_power_level := mp_ms_power_level_exp,
ms_actual_ta := mp_ms_actual_ta_exp,
facch_enabled := false
},
spec := spec,
encr := omit,
bts0_band := omit,
tsc := mp_tsc_def,
fhp := {
enabled := false,
maio_hsn := ts_HsnMaio(0, 0),
ma_map := c_MA_null,
ma := { }
}
}
/***********************************************************************
* Channel Activation / Deactivation
***********************************************************************/
/* Stress test: Do 500 channel activations/deactivations in rapid succession */
private function f_TC_chan_act_stress(charstring id) runs on ConnHdlr {
for (var integer i := 0; i < 500; i := i+1) {
f_rsl_chan_act(g_pars.chan_mode);
f_rsl_chan_deact();
}
setverdict(pass);
}
testcase TC_chan_act_stress() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_init();
vc_conn := f_start_handler(refers(f_TC_chan_act_stress), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Test if re-activation of an already active channel fails as expected */
private function f_TC_chan_act_react(charstring id) runs on ConnHdlr {
f_rsl_chan_act(g_pars.chan_mode);
/* attempt to activate the same lchan again -> expect reject */
RSL.send(ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode));
alt {
[] RSL.receive(tr_RSL_CHAN_ACT_ACK(g_chan_nr)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected CHAN ACT ACK on double activation");
}
[] RSL.receive(tr_RSL_CHAN_ACT_NACK(g_chan_nr)) {
setverdict(pass);
}
}
f_rsl_chan_deact();
}
testcase TC_chan_act_react() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_init();
vc_conn := f_start_handler(refers(f_TC_chan_act_react), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Attempt to de-activate a channel that's not active */
private function f_TC_chan_deact_not_active(charstring id) runs on ConnHdlr {
timer T := 3.0;
RSL.send(ts_RSL_RF_CHAN_REL(g_chan_nr));
T.start;
alt {
[] RSL.receive(tr_RSL_RF_CHAN_REL_ACK(g_chan_nr)) {
setverdict(pass);
}
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout expecting RF_CHAN_REL_ACK");
}
}
}
testcase TC_chan_deact_not_active() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_init();
var ConnHdlr vc_conn := f_start_handler(refers(f_TC_chan_deact_not_active), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* attempt to activate channel with wrong RSL Channel Nr IE; expect NACK */
private function f_TC_chan_act_wrong_nr(charstring id) runs on ConnHdlr {
RSL.send(ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode));
alt {
[] RSL.receive(tr_RSL_CHAN_ACT_ACK(g_chan_nr)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected CHAN ACT ACK");
}
[] RSL.receive(tr_RSL_CHAN_ACT_NACK(g_chan_nr)) {
setverdict(pass);
}
}
}
private type record WrongChanNrCase {
RslChannelNr chan_nr,
charstring description
}
private type record of WrongChanNrCase WrongChanNrCases;
private template WrongChanNrCase t_WCN(template RslChannelNr chan_nr, charstring desc) := {
chan_nr := chan_nr,
description := desc
}
testcase TC_chan_act_wrong_nr() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
var WrongChanNrCases wrong := {
valueof(t_WCN(t_RslChanNr_RACH(0), "RACH is not a dedicated channel")),
valueof(t_WCN(t_RslChanNr_RACH(1), "RACH doesn't exist on timeslot")),
valueof(t_WCN(t_RslChanNr_BCCH(0), "BCCH is not a dedicated channel")),
valueof(t_WCN(t_RslChanNr_PCH_AGCH(0), "PCH/AGCH is not a dedicated channel")),
valueof(t_WCN(t_RslChanNr_Bm(0), "TS0 cannot be TCH/F")),
valueof(t_WCN(t_RslChanNr_Lm(0, 0), "TS0 cannot be TCH/H")),
valueof(t_WCN(t_RslChanNr_Lm(0, 1), "TS0 cannot be TCH/H")),
valueof(t_WCN(t_RslChanNr_PDCH(0), "TS0 cannot be PDCH")),
valueof(t_WCN(t_RslChanNr_SDCCH8(0, 0), "TS0 cannot be SDCCH/8")),
valueof(t_WCN(t_RslChanNr_SDCCH8(0, 7), "TS0 cannot be SDCCH/8")),
valueof(t_WCN(t_RslChanNr_SDCCH4(7, 0), "TS7 cannot be SDCCH/4")),
valueof(t_WCN(t_RslChanNr_SDCCH4(7, 3), "TS7 cannot be SDCCH/4")),
valueof(t_WCN(t_RslChanNr_Lm(1, 0), "TS1 cannot be TCH/H"))
};
for (var integer i := 0; i < sizeof(wrong); i := i+1) {
pars := valueof(t_Pars(wrong[i].chan_nr, ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_chan_act_wrong_nr), pars);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* execute the same callback function on a variety of logical channels */
friend function f_testmatrix_each_chan(ConnHdlrPars pars, void_fn fn) runs on test_CT {
var ConnHdlr vc_conn;
f_init();
/* test on each of the channels we have */
for (var integer i := 0; i < sizeof(g_AllChanTypes); i := i+1) {
pars.chan_nr := valueof(g_AllChanTypes[i]);
log(testcasename(), ": XXX Starting on ", g_AllChanTypes[i]);
vc_conn := f_start_handler(fn, pars);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/***********************************************************************
* SACCH handling
***********************************************************************/
private function f_exp_sacch(boolean exp) runs on ConnHdlr {
timer T_sacch := 3.0;
T_sacch.start;
alt {
[not exp] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(0))) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received SACCH when not expecting it");
}
[not exp] T_sacch.timeout {
setverdict(pass);
}
[exp] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(0))) {
setverdict(pass);
}
[exp] T_sacch.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Timeout waiting for SACCH on ", g_chan_nr));
}
[] L1CTL.receive { repeat; }
[] RSL.receive { repeat; }
}
}
/* Test if DEACTIVATE SACCH actualy deactivates its transmission (TS 48.058 4.6) */
private function f_TC_deact_sacch(charstring id) runs on ConnHdlr {
f_l1_tune(L1CTL);
RSL.clear;
/* activate the logical channel */
f_est_dchan();
L1CTL.clear;
/* check that SACCH actually are received as expected */
f_exp_sacch(true);
/* deactivate SACCH on the logical channel */
RSL.send(ts_RSL_DEACT_SACCH(g_chan_nr));
f_sleep(1.0);
L1CTL.clear;
/* check that no SACCH are received anymore */
f_exp_sacch(false);
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_deact_sacch() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i+1) {
//for (var integer i := 0; i < 1; i := i+1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_deact_sacch), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* verify that given SACCH payload is present */
private function f_sacch_present(template octetstring l3_exp) runs on ConnHdlr {
var L1ctlDlMessage dl;
/* check that the specified SI5 value is actually sent */
timer T_sacch := 3.0;
L1CTL.clear;
T_sacch.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(0))) -> value dl {
var octetstring l3 := substr(dl.payload.data_ind.payload, 4, 19);
if (match(l3, l3_exp)) {
setverdict(pass);
} else {
repeat;
}
}
[] L1CTL.receive { repeat; }
[] T_sacch.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Timeout waiting for SACCH ", l3_exp));
}
}
}
/* verify that given SACCH payload is not present */
private function f_sacch_missing(template octetstring l3_exp) runs on ConnHdlr {
var L1ctlDlMessage dl;
/* check that the specified SI5 value is actually sent */
timer T_sacch := 3.0;
L1CTL.clear;
T_sacch.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(0))) -> value dl {
var octetstring l3 := substr(dl.payload.data_ind.payload, 4, 19);
if (match(l3, l3_exp)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Received unexpected SACCH ", dl));
} else {
repeat;
}
}
[] L1CTL.receive { repeat; }
[] T_sacch.timeout {
setverdict(pass);
}
}
}
/* Test for default SACCH FILL transmitted in DL SACCH (all channel types) */
private function f_TC_sacch_filling(charstring id) runs on ConnHdlr {
/* Set a known default SACCH filling for SI5 */
var octetstring si5 := f_rnd_octstring(19);
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5, si5));
f_l1_tune(L1CTL);
RSL.clear;
/* activate the logical channel */
f_est_dchan();
/* check that the specified SI5 value is actually sent */
f_sacch_present(si5);
/* release the channel */
RSL.clear;
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_sacch_filling() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i+1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_sacch_filling), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Test for lchan-specific SACCH INFO MODIFY (TS 48.058 4.12) */
private function f_TC_sacch_info_mod(charstring id) runs on ConnHdlr {
/* Set a known default SACCH filling for SI5 */
var octetstring si5 := f_rnd_octstring(19);
var octetstring si5_diff := f_rnd_octstring(19);
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5, si5));
f_l1_tune(L1CTL);
RSL.clear;
log("Activating channel, expecting standard SI5");
/* activate the logical channel */
f_est_dchan();
/* check that the specified SI5 value is actually sent */
f_sacch_present(si5);
/* set channel-specific different SI5 */
log("Setting channel specific SACCH INFO, expecting it");
RSL.send(ts_RSL_SACCH_INF_MOD(g_chan_nr, RSL_SYSTEM_INFO_5, si5_diff))
/* check that the specified lchan-specific value is now used */
f_sacch_present(si5_diff);
/* deactivate the channel and re-activate it, this should result in default SI5 */
log("De-activating and re-activating channel, expecting standard SI5");
f_rsl_chan_deact();
f_rsl_chan_act(valueof(ts_RSL_ChanMode_SIGN));
/* Verify that the TRX-wide default SACCH filling is present again */
f_sacch_present(si5);
/* release the channel */
RSL.clear;
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_sacch_info_mod() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i+1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_sacch_info_mod), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Test SACCH scheduling of multiple different SI message types */
private function f_TC_sacch_multi(charstring id) runs on ConnHdlr {
var octetstring si5 := f_rnd_octstring(19);
var octetstring si5bis := f_rnd_octstring(19);
var octetstring si5ter := f_rnd_octstring(19);
var octetstring si6 := f_rnd_octstring(19);
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5, si5));
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5bis, si5bis));
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5ter, si5ter));
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_6, si6));
f_l1_tune(L1CTL);
RSL.clear;
/* activate the logical channel */
f_est_dchan();
L1CTL.clear;
/* check that SACCH actually are received as expected */
f_sacch_present(si5);
f_sacch_present(si5bis);
f_sacch_present(si5ter);
f_sacch_present(si6);
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_sacch_multi() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i+1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_sacch_multi), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Test if SACH information is modified as expected */
private function f_TC_sacch_multi_chg(charstring id) runs on ConnHdlr {
var octetstring si5 := f_rnd_octstring(19);
var octetstring si6 := f_rnd_octstring(19);
/* First, configure both SI5 and SI6 to be transmitted */
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5, si5));
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_6, si6));
f_l1_tune(L1CTL);
RSL.clear;
/* activate the logical channel */
f_est_dchan();
L1CTL.clear;
/* check that SACCH actually are received as expected */
f_sacch_present(si5);
f_sacch_present(si6);
/* disable SI6 */
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_6, ''O));
/* check that SI5 is still transmitted */
f_sacch_present(si5);
/* check if SI6 is now gone */
f_sacch_missing(si6);
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_sacch_multi_chg() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i+1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_sacch_multi_chg), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Test for SACCH information present in RSL CHAN ACT (overrides FILLING) */
private function f_TC_sacch_chan_act(charstring id) runs on ConnHdlr {
var octetstring si5 := f_rnd_octstring(19);
var octetstring si6 := f_rnd_octstring(19);
var octetstring si5_specific := f_rnd_octstring(19);
var octetstring si6_specific := f_rnd_octstring(19);
/* First, configure both SI5 and SI6 to be transmitted */
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5, si5));
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_6, si6));
f_l1_tune(L1CTL);
RSL.clear;
/* activate channel with different SACCH filling */
var RSL_SacchInfo sacch_info := valueof(ts_RSL_SacchInfo({
ts_RSL_SacchInfoElem(RSL_SYSTEM_INFO_5, si5_specific),
ts_RSL_SacchInfoElem(RSL_SYSTEM_INFO_6, si6_specific)
}));
var RSL_IE_List addl_ies := { valueof(t_RSL_IE(RSL_IE_SACCH_INFO,
RSL_IE_Body:{sacch_info := sacch_info})) };
f_est_dchan(more_ies := addl_ies);
/* check that SACCH actually are received as expected */
f_sacch_present(si5_specific);
f_sacch_present(si6_specific);
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_sacch_chan_act() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i+1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_sacch_chan_act), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* verify that SACCH DL transmission is started only if MS power IE present
* see section 4.1.3 of 3GPP TS 48.058 */
private function f_TC_sacch_chan_act_ho_async(charstring id) runs on ConnHdlr {
var octetstring si5 := f_rnd_octstring(19);
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5, si5));
f_l1_tune(L1CTL);
RSL.clear;
/* Step 1: Activate ASYNC HO channel without MS power IE */
var integer ho_ref := oct2int(f_rnd_octstring(1));
var RSL_IE ho_ref_ie := valueof(t_RSL_IE(RSL_IE_HANDO_REF,
RSL_IE_Body:{ handover_ref := ho_ref }));
var RSL_IE_List addl_ies := {
ho_ref_ie
};
/* Activate channel on BTS side */
f_rsl_chan_act(g_pars.chan_mode, more_ies := addl_ies, act_type := t_RSL_IE_ActType_HO_ASYNC);
/* don't perform immediate assignment here, as we're testing non-IA case */
/* enable dedicated mode */
f_l1ctl_est_dchan(L1CTL, g_pars);
/* Verify that no DL SACCH is being received */
f_sacch_missing(?);
/* Send handover Access Burst and expect SACCH to start */
f_L1CTL_RACH(L1CTL, ho_ref, chan_nr := g_pars.chan_nr);
f_sacch_present(si5);
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
/* Step 2: Activate ASYNC HO channel with MS power IE */
/* Activate channel on BTS side */
addl_ies := {
ho_ref_ie,
valueof(t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ts_RSL_IE_MS_Power(0)}))
};
f_rsl_chan_act(g_pars.chan_mode, more_ies := addl_ies, act_type := t_RSL_IE_ActType_HO_ASYNC);
/* don't perform immediate assignment here, as we're testing non-IA case */
/* enable dedicated mode */
f_l1ctl_est_dchan(L1CTL, g_pars);
/* Verify that DL SACCH is being received */
f_sacch_present(si5);
/* Send handover Access Burst and expect SACCH to remain present */
f_L1CTL_RACH(L1CTL, ho_ref, chan_nr := g_pars.chan_nr);
f_sacch_present(si5);
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_sacch_chan_act_ho_async() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i+1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_sacch_chan_act_ho_async), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* verify that SACCH DL transmission is started only if TA + MS power IEs present,
* see section 4.1.4 of 3GPP TS 48.058 */
private function f_TC_sacch_chan_act_ho_sync(charstring id) runs on ConnHdlr {
var octetstring si5 := f_rnd_octstring(19);
RSL.send(ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5, si5));
var RSL_IE_List addl_ies;
f_l1_tune(L1CTL);
RSL.clear;
/* Step 1: Activate SYNC HO channel without MS power IE */
var integer ho_ref := oct2int(f_rnd_octstring(1));
var RSL_IE ho_ref_ie := valueof(t_RSL_IE(RSL_IE_HANDO_REF,
RSL_IE_Body:{ handover_ref := ho_ref }));
addl_ies := {
ho_ref_ie
};
/* Activate channel on BTS side */
f_rsl_chan_act(g_pars.chan_mode, more_ies := addl_ies, act_type := t_RSL_IE_ActType_HO_SYNC);
/* don't perform immediate assignment here, as we're testing non-IA case */
/* enable dedicated mode */
f_l1ctl_est_dchan(L1CTL, g_pars);
/* Verify that no DL SACCH is being received */
f_sacch_missing(?);
/* Send handover Access Burst and expect SACCH to start */
f_L1CTL_RACH(L1CTL, ho_ref, chan_nr := g_pars.chan_nr);
f_sacch_present(si5);
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
/* Step 2a: Activate SYNC HO channel with only MS power IE */
/* Activate channel on BTS side */
addl_ies := {
ho_ref_ie,
valueof(t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ts_RSL_IE_MS_Power(0)}))
};
f_rsl_chan_act(g_pars.chan_mode, more_ies := addl_ies, act_type := t_RSL_IE_ActType_HO_SYNC);
/* don't perform immediate assignment here, as we're testing non-IA case */
/* enable dedicated mode */
f_l1ctl_est_dchan(L1CTL, g_pars);
/* Verify that DL SACCH is being received, because MS Power IE was sent */
f_sacch_present(si5);
/* Send handover Access Burst and expect SACCH to remain present */
f_L1CTL_RACH(L1CTL, ho_ref, chan_nr := g_pars.chan_nr);
f_sacch_present(si5);
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
/* Step 2b: Activate SYNC HO channel with TA IE */
/* Activate channel on BTS side */
addl_ies := {
ho_ref_ie,
valueof(t_RSL_IE(RSL_IE_TIMING_ADVANCE, RSL_IE_Body:{timing_adv := 0}))
};
f_rsl_chan_act(g_pars.chan_mode, more_ies := addl_ies, act_type := t_RSL_IE_ActType_HO_SYNC);
/* don't perform immediate assignment here, as we're testing non-IA case */
/* enable dedicated mode */
f_l1ctl_est_dchan(L1CTL, g_pars);
/* Verify that no DL SACCH is being received */
f_sacch_missing(?);
/* Send handover Access Burst and expect SACCH to start */
f_L1CTL_RACH(L1CTL, ho_ref, chan_nr := g_pars.chan_nr);
f_sacch_present(si5);
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
/* Step 3: Activate SYNC HO channel with MS power IE and TA IE */
/* Activate channel on BTS side */
addl_ies := {
ho_ref_ie,
valueof(t_RSL_IE(RSL_IE_TIMING_ADVANCE, RSL_IE_Body:{timing_adv := 0})),
valueof(t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ts_RSL_IE_MS_Power(0)}))
};
f_rsl_chan_act(g_pars.chan_mode, more_ies := addl_ies, act_type := t_RSL_IE_ActType_HO_SYNC);
/* don't perform immediate assignment here, as we're testing non-IA case */
/* enable dedicated mode */
f_l1ctl_est_dchan(L1CTL, g_pars);
/* Verify that DL SACCH is being received */
f_sacch_present(si5);
/* Send handover Access Burst and expect SACCH to remain present */
f_L1CTL_RACH(L1CTL, ho_ref, chan_nr := g_pars.chan_nr);
f_sacch_present(si5);
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_sacch_chan_act_ho_sync() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i+1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_sacch_chan_act_ho_sync), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/***********************************************************************
* RACH Handling
***********************************************************************/
/* Send 1000 RACH requests and check their RA+FN on the RSL side */
private function f_TC_rach_content(boolean emerg) runs on test_CT {
f_init();
f_init_l1ctl();
f_l1_tune(L1CTL);
var GsmFrameNumber fn_last := 0;
var boolean test_failed := false;
for (var integer i := 0; i < 1000; i := i+1) {
var OCT1 ra;
if (emerg == true) {
ra := f_rnd_ra_emerg();
} else {
ra := f_rnd_ra_cs();
}
var GsmFrameNumber fn := f_L1CTL_RACH(L1CTL, oct2int(ra));
if (fn == fn_last) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Two RACH in same FN?!?");
}
fn_last := fn;
timer T := 5.0;
T.start;
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_CHAN_RQD(ra, fn, t_RslChanNr_RACH(0)))) {
T.stop;
}
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_CHAN_RQD(?, ?, ?, ?))) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected CHAN RQD");
}
[] RSL_CCHAN.receive { repeat; }
[] T.timeout {
test_failed := true;
log("[", i, "] Timeout waiting for CHAN RQD FN=", fn, " RA=", ra);
}
}
}
if (test_failed) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Some out of 1000 RACH requests timed out"));
}
setverdict(pass);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Normal variant */
testcase TC_rach_content() runs on test_CT {
f_TC_rach_content(emerg := false);
}
/* Emergency call variant */
testcase TC_rach_content_emerg() runs on test_CT {
f_TC_rach_content(emerg := true);
}
/* Send 1000 RACH Requests (flood ~ 89/s) and count if count(Abis) == count(Um) */
testcase TC_rach_count() runs on test_CT {
f_init();
f_init_l1ctl();
f_sleep(1.0);
f_l1_tune(L1CTL);
var GsmFrameNumber fn_last := 0;
for (var integer i := 0; i < 1000; i := i+1) {
var OCT1 ra := f_rnd_ra_cs();
var GsmFrameNumber fn := f_L1CTL_RACH(L1CTL, oct2int(ra));
if (fn == fn_last) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Two RACH in same FN?!?");
}
fn_last := fn;
}
var integer rsl_chrqd := 0;
timer T := 3.0;
T.start;
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_CHAN_RQD(?,?))) {
rsl_chrqd := rsl_chrqd + 1;
f_timer_safe_restart(T);
repeat;
}
[] RSL_CCHAN.receive { repeat; }
[] T.timeout { }
}
if (rsl_chrqd == 1000) {
setverdict(pass);
} else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Received only ", rsl_chrqd, " out of 1000 RACH"));
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
private function f_vty_load_ind_thresh(integer period := 10, integer bts_nr := 0) runs on test_CT {
var charstring bts_str := "bts " & int2str(bts_nr);
f_vty_config2(BSCVTY, {"network", bts_str}, "ccch load-indication-threshold " & int2str(period));
}
/* empirical value: Number of RACH slots per reporting interval (1s) on combined CCCH */
private template integer tr_rach_slots_per_interval := (90 .. 130);
/* Expect 0 RACH load on an idle BTS that has just started up */
testcase TC_rach_load_idle_thresh0() runs on test_CT {
var ASP_RSL_Unitdata rx_ud;
f_init_vty_bsc();
/* send load indications even at 0% load */
f_vty_load_ind_thresh(0);
f_vty_transceive(BSCVTY, "drop bts connection 0 oml");
f_sleep(2.0);
f_init();
/* Skip the first RACH LOAD IND, as it may not have the full slot count (BTS started less than 1s before) */
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RACH_LOAD_IND(?, ?, ?))) { }
[] RSL_CCHAN.receive { repeat }
}
timer T := 5.0;
T.start;
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RACH_LOAD_IND(tr_rach_slots_per_interval, 0, 0))) {
setverdict(pass);
repeat;
}
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RACH_LOAD_IND(?, ?, ?))) -> value rx_ud {
setverdict(fail, "Unexpected RACH LOAD IND: ", rx_ud);
repeat;
}
[] RSL_CCHAN.receive {
repeat;
}
[] T.timeout { }
}
f_vty_load_ind_thresh(10);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Expect no RACH load indications on an idle BTS that has just started up (default threshold 10%) */
testcase TC_rach_load_idle_below_thresh() runs on test_CT {
var ASP_RSL_Unitdata rx_ud;
f_init_vty_bsc();
f_init();
timer T := 5.0;
T.start;
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RACH_LOAD_IND(?, ?, ?))) -> value rx_ud {
setverdict(fail, "Unexpected RACH LOAD IND: ", rx_ud);
repeat;
}
[] RSL_CCHAN.receive {
repeat;
}
[] T.timeout {
setverdict(pass);
}
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Expect 0 RACH load on an idle BTS that has just started up */
testcase TC_rach_load_count() runs on test_CT {
var ASP_RSL_Unitdata rx_ud;
var integer load_access_count := 0;
f_init_vty_bsc();
/* send load indications even at 0% load */
f_vty_load_ind_thresh(0);
f_vty_transceive(BSCVTY, "drop bts connection 0 oml");
f_sleep(2.0);
f_init();
f_init_l1ctl();
f_sleep(1.0);
f_l1_tune(L1CTL);
/* Skip the first RACH LOAD IND, as it may not have the full slot count (BTS started less than 1s before) */
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RACH_LOAD_IND(?, ?, ?))) { }
[] RSL_CCHAN.receive { repeat }
}
var GsmFrameNumber fn_last := 0;
for (var integer i := 0; i < 1000; i := i+1) {
var OCT1 ra := f_rnd_ra_cs();
var GsmFrameNumber fn := f_L1CTL_RACH(L1CTL, oct2int(ra));
if (fn == fn_last) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Two RACH in same FN?!?");
}
fn_last := fn;
}
timer T := 5.0;
T.start;
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RACH_LOAD_IND(tr_rach_slots_per_interval, ?, ?)))
-> value rx_ud {
var RSL_IE_Body ie;
f_rsl_find_ie(rx_ud.rsl, RSL_IE_RACH_LOAD, ie);
load_access_count := load_access_count + ie.rach_load.access_count;
if (ie.rach_load.busy_count < ie.rach_load.access_count) {
setverdict(fail, "Access count cannot be < Busy count");
}
repeat;
}
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RACH_LOAD_IND(?, ?, ?))) -> value rx_ud {
setverdict(fail, "Unexpected RACH LOAD IND: ", rx_ud);
repeat;
}
[] RSL_CCHAN.receive {
repeat;
}
[] T.timeout { }
}
if (load_access_count == 1000) {
setverdict(pass);
} else {
setverdict(fail, "Load reports state ", load_access_count, " RACH, but we sent 1000");
}
f_vty_load_ind_thresh(10);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
private function f_rach_toffs(int16_t toffs256, boolean expect_pass) runs on test_CT {
var TrxcMessage ret;
/* tell fake_trx to use a given timing offset for all bursts */
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, ts_TRXC_FAKE_TIMING(toffs256));
f_sleep(0.5);
/* Transmit RACH request + wait for confirmation */
var OCT1 ra := f_rnd_ra_cs();
var GsmFrameNumber fn := f_L1CTL_RACH(L1CTL, oct2int(ra));
/* Check for expected result */
timer T := 1.5;
T.start;
alt {
[expect_pass] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_CHAN_RQD(ra, fn))) {
setverdict(pass);
}
[not expect_pass] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_CHAN_RQD(ra, fn))) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("RACH passed but was expected to be dropped: ", toffs256));
}
[] RSL_CCHAN.receive { repeat; }
[not expect_pass] T.timeout {
setverdict(pass);
}
[expect_pass] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Timeout waiting for CHAN RQD: FN=", fn, " RA=", ra));
}
}
}
/* Test if dropping of RACH Based on NM_ATT_MAX_TA works */
testcase TC_rach_max_ta() runs on test_CT {
f_init();
f_init_l1ctl();
f_l1_tune(L1CTL);
f_sleep(1.0);
/* default max-ta is 63 (full range of GSM timing advance */
/* We allow early arrival up to 2 symbols */
f_rach_toffs(-1*256, true);
f_rach_toffs(-2*256, true);
f_rach_toffs(-10*256, false);
/* 0 / 32 / 63 bits is legal / permitted */
f_rach_toffs(0, true);
f_rach_toffs(32*256, true);
f_rach_toffs(63*256, true);
/* more than 63 bits is not legal / permitted */
f_rach_toffs(64*256, false);
f_rach_toffs(127*256, false);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
private function f_TC_ho_rach(charstring id) runs on ConnHdlr {
var GsmFrameNumber fn;
var RSL_Message rm;
f_l1_tune(L1CTL);
RSL.clear;
/* Generate a random Handover Reference */
var integer ho_ref := oct2int(f_rnd_octstring(1));
/* Handover Reference IE (see 3GPP TS 48.058, 9.3.9) */
var RSL_IE ho_ref_ie := valueof(t_RSL_IE(RSL_IE_HANDO_REF,
RSL_IE_Body:{ handover_ref := ho_ref }));
/* Activate a channel on the BTS side (no encryption) */
f_rsl_chan_act(g_pars.chan_mode, more_ies := { ho_ref_ie },
act_type := t_RSL_IE_ActType_HO_SYNC);
/* Switch the MS side (e.g. trxcon) to a dedicated channel without
* waiting for Immediate Assignment and sending Access Burst */
f_l1ctl_est_dchan(L1CTL, g_pars);
/* Send handover Access Burst */
fn := f_L1CTL_RACH(L1CTL, ho_ref, chan_nr := g_pars.chan_nr);
/* TODO: test mismatching Handover Reference, and missing IE */
/* Wait for handover detection */
timer T := 3.0;
T.start;
alt {
[] RSL.receive(tr_RSL_HANDO_DET(g_pars.chan_nr)) -> value rm {
log("Handover RACH has been detected: ", rm);
setverdict(pass);
}
[] RSL.receive(tr_RSL_CHAN_RQD(?, ?, ?, ?)) -> value rm {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("RSL_CHAN_RQD was not expected: ", rm));
}
[] RSL.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Timeout waiting for handover RACH: FN=", fn, " RA=", ho_ref));
}
}
/* Release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
/* Test handover RACH detection */
testcase TC_ho_rach() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i + 1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": Starting for ", g_AllChannels[i]);
vc_conn := f_start_handler(refers(f_TC_ho_rach), pars);
vc_conn.done;
}
/* TODO: do the above in parallel, rather than sequentially? */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/***********************************************************************
* Measurement Processing / Reporting
***********************************************************************/
private template (value) LapdmAddressField
ts_LapdmAddr(template (value) LapdmSapi sapi,
template (value) boolean c_r) := {
spare := '0'B,
lpd := 0,
sapi := sapi,
c_r := c_r,
ea := true
}
private template (value) LapdmFrameAB
ts_LAPDm_AB(template (value) LapdmSapi sapi,
template (value) GsmRrL3Message l3,
template (value) boolean c_r := false,
template (value) boolean p := false) := {
addr := ts_LapdmAddr(sapi, c_r),
ctrl := ts_LapdmCtrlUI(valueof(p)),
len := 0, /* overwritten */
m := false,
el := 1,
payload := enc_GsmRrL3Message(valueof(l3))
}
/* handle incoming downlink SACCH and respond with uplink SACCH (meas res) */
private altstep as_l1_sacch() runs on ConnHdlr {
var template (value) LapdmFrameAB lb;
var L1ctlDlMessage l1_dl;
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(?))) -> value l1_dl {
log("SACCH received: ", l1_dl.payload.data_ind.payload);
lb := ts_LAPDm_AB(0, ts_MEAS_REP(g_pars.l1_pars.meas_valid,
g_pars.l1_pars.meas_ul.full.rxlev,
g_pars.l1_pars.meas_ul.sub.rxlev,
g_pars.l1_pars.meas_ul.full.rxqual,
g_pars.l1_pars.meas_ul.sub.rxqual));
log("LAPDm: ", lb);
var template (value) SacchL1Header l1h := ts_SacchL1Header(
g_pars.l1_pars.ms_power_level, false,
g_pars.l1_pars.ms_actual_ta);
/* According to 3GPP TS 44.018, section 10.5.2.20, we should pad with zeroes */
var octetstring l2 := f_pad_oct(enc_LapdmFrameAB(valueof(lb)), 21, '00'O);
log("Sending Measurement Report: ", l1h, l2);
L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(g_chan_nr, ts_RslLinkID_SACCH(0), l1h, l2));
repeat;
}
}
/* handle incoming downlink SACCH, decode the L1 header into the given record */
private altstep as_l1_sacch_l1h(inout SacchL1Header l1h,
boolean do_apply := true)
runs on ConnHdlr
{
var L1ctlDlMessage l1_dl;
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(?))) -> value l1_dl {
/* Parse the L1 SACCH header (MS Power Level & Timing Advance) */
l1h := dec_SacchL1Header(substr(l1_dl.payload.data_ind.payload, 0, 2));
log(%definitionId, "(): Rx SACCH L1 header: ", l1h);
if (do_apply) {
/* Update TA and MS power to follow what BTS requests */
f_L1CTL_PARAM(L1CTL, l1h.actual_ta, l1h.ms_power_lvl);
}
}
}
private altstep as_l1_dcch_loop() runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_DCCH(?))) -> value l1_dl {
log("DCCH received: ", l1_dl.payload.data_ind.payload);
var octetstring pl := '010301'O;
L1CTL.send(ts_L1CTL_DATA_REQ(g_chan_nr, ts_RslLinkID_DCCH(0),
f_pad_oct(pl, 23, '2B'O)));
repeat;
}
}
private altstep as_l1_tch_loop() runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
[] L1CTL.receive(tr_L1CTL_TRAFFIC_IND(g_chan_nr)) -> value l1_dl {
log("TCH received: ", l1_dl.payload.traffic_ind.data);
/* occasionaly inject FACCH frames into the uplink */
if (g_pars.l1_pars.facch_enabled == true and l1_dl.dl_info.frame_nr mod 5 == 0) {
var octetstring pl := '010301'O;
L1CTL.send(ts_L1CTL_DATA_REQ(g_chan_nr, ts_RslLinkID_DCCH(0),
f_pad_oct(pl, 23, '2B'O)));
} else {
L1CTL.send(ts_L1CTL_TRAFFIC_REQ(g_chan_nr, l1_dl.dl_info.link_id,
l1_dl.payload.traffic_ind.data));
}
repeat;
}
}
private type record MeasElem {
uint6_t rxlev,
uint3_t rxqual
}
private type record MeasElemFS {
MeasElem full,
MeasElem sub
}
private type record ConnL1Pars {
boolean dtx_enabled,
boolean toa256_enabled,
boolean meas_valid,
MeasElemFS meas_ul,
int16_t timing_offset_256syms,
uint4_t bs_power_level,
uint5_t ms_power_level,
uint8_t ms_actual_ta,
boolean facch_enabled
}
/* Convert tiing offset from 1/256th symbol to RSL Timing Offset */
private function toffs256s_to_rsl(int16_t toffs256s) return uint8_t {
return 63 + (toffs256s/256);
}
private function f_max(integer a, integer b) return integer {
if (a > b) {
return a;
} else {
return b;
}
}
private function f_min(integer a, integer b) return integer {
if (a < b) {
return a;
} else {
return b;
}
}
/* compute negative tolerance val-tolerance, ensure >= min */
private function f_tolerance_neg(integer val, integer min, integer tolerance) return integer {
val := val - tolerance;
return f_max(val, min);
}
/* compute positive tolerance val+tolerance, ensure <= max */
private function f_tolerance_pos(integer val, integer max, integer tolerance) return integer {
val := val + tolerance;
return f_min(val, max);
}
/* return a template of (val-tolerance .. val+tolerance) ensuring it is within (min .. max) */
private function f_tolerance(integer val, integer min, integer max, integer tolerance)
return template integer {
var template integer ret;
ret := (f_tolerance_neg(val, min, tolerance) .. f_tolerance_pos(val, max, tolerance));
return ret;
}
/* build a template for matching measurement results against */
private function f_build_meas_res_tmpl() runs on ConnHdlr return template RSL_Message {
var ConnL1Pars l1p := g_pars.l1_pars;
var template RSL_IE_UplinkMeas ul_meas := {
len := 3,
rfu := '0'B,
dtx_d := l1p.dtx_enabled,
rxlev_f_u := f_tolerance(l1p.meas_ul.full.rxlev, 0, 63, mp_tolerance_rxlev),
reserved1 := '00'B,
rxlev_s_u := f_tolerance(l1p.meas_ul.sub.rxlev, 0, 63, mp_tolerance_rxlev),
reserved2 := '00'B,
rxq_f_u := f_tolerance(l1p.meas_ul.full.rxqual, 0, 7, mp_tolerance_rxqual),
rxq_s_u := f_tolerance(l1p.meas_ul.sub.rxqual, 0, 7, mp_tolerance_rxqual),
supp_meas_info := omit
};
if (l1p.toa256_enabled) {
ul_meas.len := (3+8);
ul_meas.supp_meas_info := {
toa256_mean := f_tolerance(l1p.timing_offset_256syms, -63*256, 192*256, mp_tolerance_timing_offset_256syms),
toa256_min := ?,
toa256_max := ?,
toa256_std_dev := ?
}
}
var template RSL_IE_BS_Power bs_power := {
reserved := 0,
epc := false,
fpc := false,
power_level := l1p.bs_power_level
};
var template RSL_IE_L1Info l1_info := {
ms_power_lvl := l1p.ms_power_level,
fpc := false,
reserved := 0,
actual_ta := f_tolerance(l1p.ms_actual_ta, 0, 63, mp_tolerance_timing_offset_256syms/256)
};
var uint8_t offs := toffs256s_to_rsl(l1p.timing_offset_256syms);
var template uint8_t t_toffs := f_tolerance(offs, 0, 255, mp_tolerance_timing_offset_256syms/256);
return tr_RSL_MEAS_RES_OSMO(g_chan_nr, g_next_meas_res_nr, ul_meas, bs_power, l1_info,
?, t_toffs);
}
/* build a template for matching measurement results that do not contain any
* MS related measurement (l1_info, l3_info and ms timing offset). */
private function f_build_meas_res_tmpl_empty() runs on ConnHdlr return template RSL_Message {
var ConnL1Pars l1p := g_pars.l1_pars;
var template RSL_IE_UplinkMeas ul_meas := {
len := 3,
rfu := '0'B,
dtx_d := l1p.dtx_enabled,
rxlev_f_u := ?,
reserved1 := '00'B,
rxlev_s_u := ?,
reserved2 := '00'B,
rxq_f_u := ?,
rxq_s_u := ?,
supp_meas_info := omit
};
if (l1p.toa256_enabled) {
ul_meas.len := (3+8);
ul_meas.supp_meas_info := {
toa256_mean := f_tolerance(l1p.timing_offset_256syms, -63*256, 192*256, mp_tolerance_timing_offset_256syms),
toa256_min := ?,
toa256_max := ?,
toa256_std_dev := ?
}
}
var template RSL_IE_BS_Power bs_power := {
reserved := 0,
epc := false,
fpc := false,
power_level := l1p.bs_power_level
};
return tr_RSL_MEAS_RES_EMPTY(g_chan_nr, g_next_meas_res_nr, ul_meas, bs_power);
}
/* verify we regularly receive measurement reports with incrementing numbers */
private altstep as_meas_res(boolean verify_meas := true) runs on ConnHdlr {
var RSL_Message rsl;
var boolean chan_est := false;
[not verify_meas] RSL.receive(tr_RSL_MEAS_RES(?)) { repeat; }
/* Receive osmocom specific measurement reports. This is the normal
* case. Here we verify that the measurement reports we sent are
* comming back as we expect them. */
[] RSL.receive(f_build_meas_res_tmpl()) -> value rsl {
/* increment counter of next to-be-expected meas rep */
g_next_meas_res_nr := (g_next_meas_res_nr + 1) mod 256;
/* Re-start the timer expecting the next MEAS RES */
f_timer_safe_restart(g_Tmeas_exp);
/* The following two cases may only happen in the beginning
* of the channel establishment phase. Once we have received
* the "our" measurement report the first time, the channel
* is established and empty or hardcoded TRXCON reports must
* not occur anymore. */
chan_est := true;
repeat;
}
/* When the BTS has established the channel, the MS might need slightly
* more time to establish the channel and actually start sending. The
* result is then a measurement report that just lacks the measurement
* information of the MS. This is normal and we tolerate this behavior. */
[chan_est == false] RSL.receive(f_build_meas_res_tmpl_empty()) -> value rsl {
/* increment counter of next to-be-expected meas rep */
g_next_meas_res_nr := (g_next_meas_res_nr + 1) mod 256;
/* Re-start the timer expecting the next MEAS RES */
f_timer_safe_restart(g_Tmeas_exp);
repeat;
}
/* Due to the TDMA nature of GSM, TRXCON implements a way to emit dummy
* measurements if the TTCN3 side does not supply measurement input in
* time. In those cases TRXCON will either use a cached measurement
* report or a hardcoded one. If TRXCON picks the hardcoded measurement
* report the templates above will not match. We tolerate this
* behavior, but only once. */
[chan_est == false] RSL.receive(tr_RSL_MEAS_RES(g_chan_nr, g_next_meas_res_nr)) -> value rsl {
/* increment counter of next to-be-expected meas rep */
g_next_meas_res_nr := (g_next_meas_res_nr + 1) mod 256;
if (g_first_meas_res) {
g_first_meas_res := false;
repeat;
} else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Received unspecific MEAS RES ", rsl));
}
}
[] RSL.receive(tr_RSL_MEAS_RES(?)) -> value rsl {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Received unexpected MEAS RES ", rsl));
}
[g_Tmeas_exp.running] g_Tmeas_exp.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Didn't receive expected measurement result")
}
}
private function f_alg_id_to_l1ctl(RSL_AlgId rsl_alg_id) return uint8_t {
select (rsl_alg_id) {
case (RSL_ALG_ID_A5_0) { return 0; }
case (RSL_ALG_ID_A5_1) { return 1; }
case (RSL_ALG_ID_A5_2) { return 2; }
case (RSL_ALG_ID_A5_3) { return 3; }
case (RSL_ALG_ID_A5_4) { return 4; }
case (RSL_ALG_ID_A5_5) { return 5; }
case (RSL_ALG_ID_A5_6) { return 6; }
case (RSL_ALG_ID_A5_7) { return 7; }
case else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unknwon Algorithm ID");
/* Make compiler happy by calling mtc.stop here. It is already
* called in f_shutdown */
mtc.stop;
}
}
}
private function f_alg_id_to_l3(RSL_AlgId rsl_alg_id) return BIT3 {
select (rsl_alg_id) {
case (RSL_ALG_ID_A5_1) { return '000'B; }
case (RSL_ALG_ID_A5_2) { return '001'B; }
case (RSL_ALG_ID_A5_3) { return '010'B; }
case (RSL_ALG_ID_A5_4) { return '011'B; }
case (RSL_ALG_ID_A5_5) { return '100'B; }
case (RSL_ALG_ID_A5_6) { return '101'B; }
case (RSL_ALG_ID_A5_7) { return '110'B; }
case else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unknwon Algorithm ID");
/* Make compiler happy by calling mtc.stop here. It is already
* called in f_shutdown */
mtc.stop;
}
}
}
/* Send RACH request through l1CTL and wait for ChanReq on RSL BST->BSC */
private function f_rach_req_wait_chan_rqd(integer ra) runs on ConnHdlr return GsmFrameNumber {
var GsmFrameNumber fn;
timer T := 8.0;
/* advertise to RSL Emulation that we expect to receive confirmation from RACH */
RSL.send(ts_RSLDC_ChanRqd_anyFN(int2oct(ra,1)));
f_L1CTL_PARAM(L1CTL, g_pars.l1_pars.ms_actual_ta, g_pars.l1_pars.ms_power_level);
/* Send the actual RACH */
fn := f_L1CTL_RACH(L1CTL, ra);
T.start;
alt {
[] RSL.receive(tr_RSL_CHAN_RQD(int2oct(ra,1), fn)) { setverdict(pass, "Received CHAN-RQD from RACH REQ") }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Timeout waiting for CHAN-RQD from RACH REQ <", ra, ", ", fn, ">"));
}
}
T.stop
return fn;
}
/* Tune to a dedicated channel: L1CTL only */
private function f_l1ctl_est_dchan(L1CTL_PT pt, ConnHdlrPars pars) {
if (not pars.fhp.enabled) {
var TrxParsItem trx_pars := mp_trx_pars[pars.trx_nr];
pt.send(ts_L1CTL_DM_EST_REQ_H0(pars.chan_nr, pars.tsc,
trx_pars.arfcn));
} else {
pt.send(ts_L1CTL_DM_EST_REQ_H1(pars.chan_nr, pars.tsc,
pars.fhp.maio_hsn.hsn,
pars.fhp.maio_hsn.maio,
pars.fhp.ma));
}
}
/* Establish dedicated channel: L1CTL + RSL side */
private function f_est_dchan(boolean encr_enable := false, RSL_IE_List more_ies := {}) runs on ConnHdlr {
var ChannelDescription ch_desc;
/* Activate channel on BTS side */
f_rsl_chan_act(g_pars.chan_mode, encr_enable, more_ies);
/* Craft channel description (with or without frequency hopping parameters) */
if (g_pars.fhp.enabled) {
ch_desc := valueof(ts_ChanDescH1(g_pars.chan_nr, g_pars.fhp.maio_hsn, g_pars.tsc));
} else {
var TrxParsItem trx_pars := mp_trx_pars[g_pars.trx_nr];
ch_desc := valueof(ts_ChanDescH0(g_pars.chan_nr, trx_pars.arfcn, g_pars.tsc));
}
/* enable dedicated mode */
f_l1ctl_est_dchan(L1CTL, g_pars);
/* enable encryption, if requested */
if (encr_enable) {
var uint8_t alg_id := f_alg_id_to_l1ctl(g_pars.encr.alg_id);
f_L1CTL_CRYPTO_REQ(L1CTL, g_pars.chan_nr, alg_id, g_pars.encr.key);
}
/* Send TCH Mode Request to the L1 if needed */
if (match(g_pars.chan_mode.spd_ind, (RSL_SPDI_SPEECH, RSL_SPDI_DATA))) {
var L1ctlTchMode tch_mode;
select (g_pars.chan_mode.coding_alg_rate) {
case (RSL_CMOD_NO_RESOURCE) { tch_mode := L1CTL_CHAN_MODE_SIGN; }
case (RSL_CMOD_SP_GSM1) { tch_mode := L1CTL_CHAN_MODE_SPEECH_V1; }
case (RSL_CMOD_SP_GSM2) { tch_mode := L1CTL_CHAN_MODE_SPEECH_V2; }
case (RSL_CMOD_SP_GSM3) { tch_mode := L1CTL_CHAN_MODE_SPEECH_V3; }
case else {
log("RSL channel mode := ", g_pars.chan_mode.coding_alg_rate,
" is not supported by the L1, falling back to signalling");
tch_mode := L1CTL_CHAN_MODE_SIGN;
}
}
f_L1CTL_TCH_MODE(L1CTL, tch_mode);
}
g_first_meas_res := true;
}
/* Initialize and start the RTP emulation component for a ConnHdlr */
friend function f_rtpem_activate(inout octetstring payload,
RtpemConfig cfg := c_RtpemDefaultCfg,
RtpemMode mode := RTPEM_MODE_BIDIR)
runs on ConnHdlr {
/* Step 0: initialize, connect and start the emulation component */
vc_RTPEM := RTP_Emulation_CT.create(testcasename() & "-RTPEM");
map(vc_RTPEM:RTP, system:RTP);
map(vc_RTPEM:RTCP, system:RTCP);
connect(vc_RTPEM:CTRL, self:RTPEM_CTRL);
connect(vc_RTPEM:DATA, self:RTPEM_DATA);
vc_RTPEM.start(RTP_Emulation.f_main());
/* Step 1: configure the RTP parameters */
var integer payload_len := 0;
var octetstring hdr := ''O;
select (g_pars.chan_mode) {
case (tr_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM1)) /* TCH/FS */
{ payload_len := 33; hdr := 'D0'O; }
case (tr_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM1)) /* TCH/HS */
{ payload_len := 15; hdr := '00'O; }
case (tr_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM2)) /* TCH/EFS */
{ payload_len := 31; hdr := 'C0'O; }
case else { /* FIXME: also handle TCH/AFS and TCH/AHS */
setverdict(fail, "Unhandled RSL channel mode := ", g_pars.chan_mode);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
}
/* Pad the payload to conform the expected length */
payload := f_pad_oct(hdr & payload, payload_len, '00'O);
cfg.tx_fixed_payload := payload;
f_rtpem_configure(RTPEM_CTRL, cfg);
/* Step 2: bind the RTP emulation to the configured address */
var PortNumber rtpem_bind_port := mp_rtpem_bind_port;
f_rtpem_bind(RTPEM_CTRL, mp_rtpem_bind_ip, rtpem_bind_port);
/* Step 3: send CRCX with the configured address/port to the IUT */
var RSL_Message rsl_pdu := f_rsl_transceive_ret(
ts_RSL_IPA_CRCX(g_chan_nr, f_inet_addr(mp_rtpem_bind_ip), rtpem_bind_port),
tr_RSL_IPA_CRCX_ACK(g_chan_nr, ?, ?, ?),
"IPA CRCX ACK");
/* Step 4: connect to the IUT's address/port parsed from CRCX ACK */
var HostName local_addr := f_inet_ntoa(rsl_pdu.ies[2].body.ipa_local_ip);
var PortNumber local_port := rsl_pdu.ies[3].body.ipa_local_port;
f_rtpem_connect(RTPEM_CTRL, local_addr, local_port);
/* Step 5: set the given RTP emulation mode */
f_rtpem_mode(RTPEM_CTRL, mode);
}
/* establish DChan, verify existance + contents of measurement reports */
private function f_TC_meas_res_periodic(charstring id) runs on ConnHdlr {
f_l1_tune(L1CTL);
RSL.clear;
if (mp_bts_trxc_port != -1) {
f_trxc_fake_rssi(rxlev2dbm(mp_ul_rxlev_exp));
f_trxc_fake_toffs256(g_pars.l1_pars.timing_offset_256syms);
}
f_est_dchan();
/* run for a number of seconds, send SACCH + FACCH from MS side and verify
* RSL measurement reports on Abis side */
timer T := 8.0;
T.start;
alt {
[] as_l1_sacch();
[] as_meas_res();
[] as_l1_dcch_loop();
[] as_l1_tch_loop();
[] L1CTL.receive { repeat; }
[g_Tmeas_exp.running] T.timeout {
/* as_meas_res() would have done Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail) in case
* of any earlier errors, so if we reach this timeout, we're good */
setverdict(pass);
}
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "No MEAS RES received at all");
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
/* Wait until the BTS has reached full tx power (nominal tx power minus configured attenuation) */
private function f_wait_ramp_up() runs on ConnHdlr return integer {
var L1ctlDlMessage l1_dl;
var integer max_rx_lvl := mp_bts_tx_nom_pwr_exp - mp_bts_tx_pwr_att_exp;
timer Tup := 10.0;
Tup.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0), ?)) -> value l1_dl {
var GsmRxLev rx_lvl := l1_dl.dl_info.rx_level;
log("Received rx_level=", rx_lvl);
if (rx_lvl != max_rx_lvl) {
repeat;
}
Tup.stop;
}
[] L1CTL.receive { repeat; }
[] Tup.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Didn't reach full power ", max_rx_lvl));
}
}
return max_rx_lvl;
}
/* verify BTS ramps power up to full tx power (nominal tx power minus configured attenuation) */
private function f_verify_ramp_up() runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
var integer initial_rx_lvl := -1;
var integer last_rx_lvl := -1;
var integer max_rx_lvl := mp_bts_tx_nom_pwr_exp - mp_bts_tx_pwr_att_exp;
timer T := 2.0;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0), ?)) -> value l1_dl {
var GsmRxLev rx_lvl := l1_dl.dl_info.rx_level;
log("Received rx_level=", rx_lvl);
if (initial_rx_lvl == -1) {
initial_rx_lvl := rx_lvl;
last_rx_lvl := rx_lvl;
/* Expect a somehow low value during first received messages */
if (initial_rx_lvl >= max_rx_lvl / 2) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Detected high initial tx power during ramp up: ",
initial_rx_lvl , ", full power is", max_rx_lvl));
}
}
/* received Rx level bigger than maximum allowed power by CN */
if (rx_lvl > max_rx_lvl) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Detected Tx power higher than full power: ",
rx_lvl , " > ", max_rx_lvl));
}
/* Make sure it never decreases, since we are rumping up */
if (last_rx_lvl > rx_lvl) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Detected Tx power decrease during ramp up: ",
last_rx_lvl , " -> ", rx_lvl));
}
if (rx_lvl == max_rx_lvl and not T.running) {
/* We reached the maximum power, start timer and receive
/* a few more to make sure we don't surpass it */
log("Reached full power, wating a bit more until success");
T.start;
}
last_rx_lvl := rx_lvl;
repeat;
}
[] L1CTL.receive { repeat; }
[] T.timeout { }
}
/* We didn't increase tx power during ramp up */
if (initial_rx_lvl < last_rx_lvl) {
log("Tx power increased during ramp up: ", initial_rx_lvl , " -> ", last_rx_lvl);
} else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("No Tx power increase during whole ramp up: ",
initial_rx_lvl , " -> ", last_rx_lvl));
}
}
/* verify BTS ramps power down to rx_level 0 */
private function f_verify_ramp_down(integer max_rx_lvl) runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
var integer last_rx_lvl := max_rx_lvl;
timer Tdown := 5.0;
Tdown.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0), ?)) -> value l1_dl {
var GsmRxLev rx_lvl := l1_dl.dl_info.rx_level;
log("Received rx_level=", rx_lvl);
/* received Rx level bigger than maximum allowed power by CN */
if (rx_lvl > max_rx_lvl) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Detected Tx power higher than full power: ",
rx_lvl , " > ", max_rx_lvl));
}
/* Make sure it never increases, since we are rumping down */
if (last_rx_lvl < rx_lvl) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Detected Tx power increase during ramp up: ",
last_rx_lvl , " -> ", rx_lvl));
}
last_rx_lvl := rx_lvl;
if (last_rx_lvl != 0) {
repeat;
}
/* we reached power level 0, we are done */
Tdown.stop;
}
[] L1CTL.receive { repeat; }
[] Tdown.timeout { }
}
/* We didn't increase tx power during ramp down */
if (max_rx_lvl > last_rx_lvl) {
log("Tx power decreased during ramp down: ", max_rx_lvl , " -> ", last_rx_lvl);
} else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("No Tx power decrease during whole ramp down: ",
max_rx_lvl , " -> ", last_rx_lvl));
}
}
/* Verify Tx power reduction and ramping up during BTS bring up */
private function f_TC_tx_power_start_ramp_up_bcch(charstring id) runs on ConnHdlr {
f_l1_tune(L1CTL);
RSL.clear;
f_verify_ramp_up();
setverdict(pass);
}
testcase TC_tx_power_start_ramp_up_bcch() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_tx_power_start_ramp_up_bcch), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Verify Tx power reduction and ramping downd uring BTS bring shutdown due to Abis link failure */
private function f_TC_tx_power_start_ramp_down_bcch(charstring id) runs on ConnHdlr {
f_connhdlr_init_vty_bsc();
f_l1_tune(L1CTL);
RSL.clear;
/* Wait until BTS is started and at full power */
var integer max_rx_lvl := f_wait_ramp_up();
log("Reached nominal level ", max_rx_lvl, ", shutting down OML link");
f_vty_transceive(BSCVTY, "drop bts connection 0 oml");
f_verify_ramp_down(max_rx_lvl);
setverdict(pass);
}
testcase TC_tx_power_start_ramp_down_bcch() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_tx_power_start_ramp_down_bcch), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Verify Tx power:
* + ramping down during ADM state UNLOCKED->LOCKED
* + ramping up during ADM state LOCKED->UNLOCKED
*/
private function f_TC_tx_power_ramp_adm_state_change(charstring id) runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
var integer last_rx_lvl;
f_connhdlr_init_vty_bsc();
f_l1_tune(L1CTL);
RSL.clear;
/* Wait until BTS is started and at full power */
var integer max_rx_lvl := f_wait_ramp_up();
log("Reached nominal level ", max_rx_lvl, ", changing ADM state to LOCKED");
log("ADM STATE UNLOCKED->LOCKED");
f_vty_enter_cfg_trx(BSCVTY);
f_vty_transceive(BSCVTY, "rf_locked 1");
last_rx_lvl := max_rx_lvl;
f_verify_ramp_down(max_rx_lvl);
/* Let some time after we received 0dBm, then check we don't receive BCCH
* anymore because scheduler has stopped after ramping down */
f_sleep(2.0);
L1CTL.clear;
timer Tlocked := 2.0;
Tlocked.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0), ?)) -> value l1_dl {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Received data_ind during rf_locked: ", l1_dl));
}
[] L1CTL.receive { repeat; }
[] Tlocked.timeout { setverdict(pass, "Didn't receive data_ind while in rf_locked state."); }
}
log("ADM STATE LOCKED->UNLOCKED");
f_vty_transceive(BSCVTY, "rf_locked 0");
f_verify_ramp_up();
setverdict(pass);
}
testcase TC_tx_power_ramp_adm_state_change() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_tx_power_ramp_adm_state_change), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
private function f_check_meas_bs_power_level(integer level) runs on ConnHdlr {
timer T := 8.0;
T.start;
var RSL_Message rsl;
alt {
[] as_l1_sacch();
[] L1CTL.receive { repeat; }
[] RSL.receive(tr_RSL_MEAS_RES(g_chan_nr, ?, ?, ?)) -> value rsl {
if (rsl.ies[3].body.bs_power.power_level == level) {
setverdict(pass)
} else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Received wrong BS power level in MEAS RES ", rsl));
}
}
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "No MEAS RES received at all");
}
}
}
/* See if the RSL MEASurement RESult contains expeced BS power level
* set _during_ the CHANnel ACTIVation procedure. */
private function f_TC_rsl_bs_pwr_static_ass(charstring id) runs on ConnHdlr {
f_l1_tune(L1CTL);
RSL.clear;
if (mp_bts_trxc_port != -1) {
f_trxc_fake_rssi(rxlev2dbm(mp_ul_rxlev_exp));
f_trxc_fake_toffs256(g_pars.l1_pars.timing_offset_256syms);
}
var uint4_t pwr_var := 1;
var template (value) RSL_IE_BS_Power bs_power := ts_RSL_IE_BS_Power(pwr_var);
var template (value) RSL_IE pwr := t_RSL_IE(RSL_IE_BS_POWER, RSL_IE_Body:{bs_power := bs_power});
f_est_dchan(more_ies :={valueof(pwr)});
f_check_meas_bs_power_level(pwr_var);
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
/* See if the RSL MEASurement RESult contains expeced BS power level
* set _after_ the CHANnel ACTIVation procedure. */
private function f_TC_rsl_bs_pwr_static_power_control(charstring id) runs on ConnHdlr {
f_l1_tune(L1CTL);
RSL.clear;
if (mp_bts_trxc_port != -1) {
f_trxc_fake_rssi(rxlev2dbm(mp_ul_rxlev_exp));
f_trxc_fake_toffs256(g_pars.l1_pars.timing_offset_256syms);
}
var uint4_t pwr_var := 1;
var template (value) RSL_IE_BS_Power bs_power := ts_RSL_IE_BS_Power(pwr_var);
f_est_dchan();
RSL.send(ts_RSL_BS_PWR_CTRL(g_chan_nr, bs_power));
f_check_meas_bs_power_level(pwr_var);
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_rsl_bs_pwr_static_ass() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN, trx_nr := 1));
vc_conn := f_start_handler(refers(f_TC_rsl_bs_pwr_static_ass), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_rsl_bs_pwr_static_power_control() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN, trx_nr := 1));
vc_conn := f_start_handler(refers(f_TC_rsl_bs_pwr_static_power_control), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Target level -100, first rssi -90, ms power 7, expected increase to 7+6 within 6 seconds,
* second rssi -110, ms power 7+6, expected decrease to 7 within 6 seconds.
* These power levels are valid for all bands and require no special handling. */
private function f_TC_rsl_ms_pwr_dyn_ass_updown(charstring id) runs on ConnHdlr {
var uint5_t pwr_var := 7;
var SacchL1Header l1h;
f_trxc_fake_rssi(rxlev2dbm(10));
f_l1_tune(L1CTL);
RSL.clear;
var RSL_IE_List addl_ies;
var template (value) RSL_IE_MS_Power_Parameters pp := (ts_RSL_IE_MS_Power_Parameters(''O));
addl_ies := {
valueof(t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ts_RSL_IE_MS_Power(pwr_var)})),
valueof(t_RSL_IE(RSL_IE_MS_POWER_PARAM, RSL_IE_Body:{ms_power_params := pp}))
};
/* establish with power parameters */
f_est_dchan(more_ies := addl_ies);
/* set a high value to ensure L1 power control level increases */
f_trxc_fake_rssi(rxlev2dbm(20));
timer T2 := 6.0;
T2.start;
alt {
[] as_l1_sacch_l1h(l1h) {
if (l1h.ms_power_lvl < (pwr_var + 6)) {
repeat;
}
T2.stop;
}
[] L1CTL.receive { repeat; }
[] T2.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Power Level in L1 header has not increased sufficiently");
}
}
/* set a low value to ensure L1 power control level decreases */
f_trxc_fake_rssi(rxlev2dbm(0));
timer T4 := 6.0;
T4.start;
alt {
[] as_l1_sacch_l1h(l1h) {
if (l1h.ms_power_lvl > pwr_var) {
repeat;
}
T4.stop;
setverdict(pass, "Power level in L1 decreased/increased as expected");
}
[] L1CTL.receive { repeat; }
[] T4.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Power Level in L1 header has not decreased sufficiently");
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
/* check that we do not exceed the max power */
private function f_TC_rsl_ms_pwr_dyn_max(charstring id) runs on ConnHdlr {
var uint5_t pwr_var := 7;
var SacchL1Header l1h;
/* set a low value to ensure power increases */
f_trxc_fake_rssi(rxlev2dbm(10));
f_l1_tune(L1CTL);
RSL.clear;
var RSL_IE_List addl_ies;
var template (value) RSL_IE_MS_Power_Parameters pp := (ts_RSL_IE_MS_Power_Parameters(''O));
addl_ies := {
valueof(t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ts_RSL_IE_MS_Power(pwr_var)})),
valueof(t_RSL_IE(RSL_IE_MS_POWER_PARAM, RSL_IE_Body:{ms_power_params := pp}))
};
/* establish with power parameters */
f_est_dchan(more_ies := addl_ies);
timer T1 := 10.0;
T1.start;
alt {
[] as_l1_sacch_l1h(l1h) { repeat; }
[] L1CTL.receive { repeat; }
[] T1.timeout {
if (not isbound(l1h)) {
setverdict(fail, "No SACCH blocks were received");
} else if (l1h.ms_power_lvl != pwr_var) {
setverdict(fail, "Power level in L1 header should not have changed");
}
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
/* see if we reach the band max power */
private function f_TC_rsl_ms_pwr_dyn_up(charstring id) runs on ConnHdlr {
var SacchL1Header l1h;
var uint5_t pwr_var := 15;
var uint5_t pwr_max_var := f_get_max_power_from_band();
/* set a low value to ensure power increases */
f_trxc_fake_rssi(rxlev2dbm(10));
f_l1_tune(L1CTL);
RSL.clear;
var template (value) RSL_IE_MS_Power ms_power := ts_RSL_IE_MS_Power(pwr_var);
var template (value) RSL_IE pwr := t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ms_power});
/* establish with fixed power level */
f_est_dchan(more_ies :={valueof(pwr)});
/* check our initial power level */
f_wait_for_l1_power_level(pwr_var);
/* update power param to enable power loop
48.058 The maximum power to be used is indicated in the BS and MS Power elements respectively. */
RSL.send(ts_RSL_MS_PWR_CTRL_with_pp(g_chan_nr, pwr_max_var));
/* wait, then check that our power level was reduced */
timer T1 := 10.0;
T1.start;
alt {
[] as_l1_sacch_l1h(l1h) { repeat; }
[] L1CTL.receive { repeat; }
[] T1.timeout {
if (not isbound(l1h)) {
setverdict(fail, "No SACCH blocks were received");
} else if (f_power_level_is_highest_dbm(l1h.ms_power_lvl)) {
setverdict(pass, "Power level in L1 header reduced as expected");
} else {
setverdict(fail, "Power level := ", l1h.ms_power_lvl, " did not ",
"reach the expected value := ", pwr_max_var);
}
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
/* see if we reach the band min power */
private function f_TC_rsl_ms_pwr_dyn_down(charstring id) runs on ConnHdlr {
var SacchL1Header l1h;
/* set a high value to ensure power decreases */
f_trxc_fake_rssi(rxlev2dbm(50));
f_l1_tune(L1CTL);
RSL.clear;
var uint5_t pwr_var := 5;
var uint5_t pwr_target_val := 15;
var template (value) RSL_IE_MS_Power ms_power := ts_RSL_IE_MS_Power(pwr_var);
var template (value) RSL_IE pwr := t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ms_power});
/* establish with fixed power level */
f_est_dchan(more_ies :={valueof(pwr)});
/* check our initial power level */
f_wait_for_l1_power_level(pwr_var);
/* update power param to enable power loop
as per spec the supplied ms power IE should set the max allowed power...*/
RSL.send(ts_RSL_MS_PWR_CTRL_with_pp(g_chan_nr, pwr_target_val));
/* wait, then check that our power level was increased */
timer T1 := 10.0;
T1.start;
alt {
[] as_l1_sacch_l1h(l1h) { repeat; }
[] L1CTL.receive { repeat; }
[] T1.timeout {
if (not isbound(l1h)) {
setverdict(fail, "No SACCH blocks were received");
} else if (f_power_level_is_lowest_dbm(l1h.ms_power_lvl)) {
setverdict(pass, "Power level increased to lowest power value");
} else {
setverdict(fail, "Power level NOT increased to lowest power value");
}
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
/* See if the power level remains constant when MS Power Parameters IE
* is _absent_ in the CHANnel ACTIVation message. */
private function f_TC_rsl_ms_pwr_dyn_active(charstring id) runs on ConnHdlr {
var SacchL1Header l1h;
/* set a high value to ensure power decreases */
f_trxc_fake_rssi(rxlev2dbm(50));
f_l1_tune(L1CTL);
RSL.clear;
var uint5_t pwr_var := 5;
var template (value) RSL_IE_MS_Power ms_power := ts_RSL_IE_MS_Power(pwr_var);
var template (value) RSL_IE pwr := t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ms_power});
/* establish with fixed power level */
f_est_dchan(more_ies :={valueof(pwr)});
/* check our initial power level */
f_wait_for_l1_power_level(pwr_var);
/* wait, then check that our power level did not change */
timer T1 := 10.0;
T1.start;
alt {
[] as_l1_sacch_l1h(l1h, do_apply := false) {
if (l1h.ms_power_lvl != pwr_var) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"BS power control should not be active unless we receive a power parameters IE!");
}
repeat;
}
[] L1CTL.receive { repeat; }
[] T1.timeout { setverdict(pass); }
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
/* See if the power level remains constant when MS Power Parameters IE
* is _absent_ in the CHANnel ACTIVation and MS Power Control messages. */
private function f_TC_rsl_ms_pwr_dyn_active2(charstring id) runs on ConnHdlr {
var SacchL1Header l1h;
/* set a high value to ensure power decreases */
f_trxc_fake_rssi(rxlev2dbm(50));
f_l1_tune(L1CTL);
RSL.clear;
var uint5_t pwr_var := 5;
var template (value) RSL_IE_MS_Power ms_power := ts_RSL_IE_MS_Power(pwr_var);
var template (value) RSL_IE pwr := t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ms_power});
/* establish with fixed power level */
f_est_dchan(more_ies :={valueof(pwr)});
/* check our initial power level */
f_wait_for_l1_power_level(pwr_var);
/* pwr control without power params IE, should NOT activate MS power control*/
RSL.send(ts_RSL_MS_PWR_CTRL(g_chan_nr, ms_power));
/* wait, then check that our power level did not change */
timer T1 := 10.0;
T1.start;
alt {
[] as_l1_sacch_l1h(l1h, do_apply := false) {
if (l1h.ms_power_lvl != pwr_var) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"BS power control should not be active unless we receive a power parameters IE!");
}
repeat;
}
[] L1CTL.receive { repeat; }
[] T1.timeout { setverdict(pass); }
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
private function f_wait_for_l1_power_level(integer level) runs on ConnHdlr {
var SacchL1Header l1h;
timer T0 := 10.0;
T0.start;
alt {
[] as_l1_sacch_l1h(l1h, do_apply := false) {
if (l1h.ms_power_lvl != level) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Power level in L1 header != signaled (RSL) power level.");
}
}
[] L1CTL.receive { repeat; }
[] T0.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Timeout waiting for initial power level");
}
}
T0.stop;
}
private function f_power_level_is_lowest_dbm(integer level) runs on ConnHdlr return boolean {
var IntegerRecord min_dbm_level;
var IntegerRecord max_dbm_level;
var IntegerRecord x := f_power_from_band(g_pars.bts0_band, min_dbm_level, max_dbm_level);
for (var integer i := 0; i < sizeof(min_dbm_level); i := i+1) {
if (min_dbm_level[i] == level) {
return true;
}
}
return false;
}
private function f_power_level_is_highest_dbm(integer level) runs on ConnHdlr return boolean {
var IntegerRecord min_dbm_level;
var IntegerRecord max_dbm_level;
var IntegerRecord x := f_power_from_band(g_pars.bts0_band, min_dbm_level, max_dbm_level);
for (var integer i := 0; i < sizeof(max_dbm_level); i := i+1) {
if (max_dbm_level[i] == level) {
return true;
}
}
return false;
}
private function f_get_max_power_from_band() runs on ConnHdlr return integer {
var IntegerRecord min_dbm_level;
var IntegerRecord max_dbm_level;
var IntegerRecord x := f_power_from_band(g_pars.bts0_band, min_dbm_level, max_dbm_level);
return max_dbm_level[0];
}
type charstring BtsBand ("GSM450","GSM480","GSM750","GSM810","GSM850","GSM900","DCS1800","PCS1900");
private template charstring BtsBand_allGSM := pattern "GSM???";
private function f_power_from_band(in BtsBand band, out IntegerRecord min_dbm_level, out IntegerRecord max_dbm_level) return IntegerRecord {
// 45.005 4.1.1
var IntegerRecord gsm_power :={31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3,
2, 1, 0};
var IntegerRecord dcs_power :={28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15,
14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 31, 30, 29};
var IntegerRecord pcs_power :={15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 31, 30};
var IntegerRecord rv;
select(band){
case (BtsBand_allGSM){
rv := gsm_power;
min_dbm_level := {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19} ;
max_dbm_level := {2, 1, 0};
}
case("DCS1800"){
rv := dcs_power;
min_dbm_level := {28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15};
max_dbm_level := {0, 29}; // let's cheat here, assume MS_TXPWR_MAX_CCH might be being broadcast, so maybe no 29,30,31
}
case("PCS1900"){
rv := pcs_power;
min_dbm_level := {15};
max_dbm_level := {30};
}
case else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unsupported band: " & band);
}
}
return rv;
}
private function f_vty_get_bts0_band() runs on test_CT return BtsBand {
return f_vty_transceive_match_regex(BTSVTY, "show bts 0", "BTS 0 is of*type* in band (\w+),*", 0);
}
testcase TC_rsl_ms_pwr_dyn_ass_updown() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "phy 0", "osmotrx ms-power-loop -100");
for (var integer tn := 1; tn <= 1; tn := tn+1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ts_RSL_ChanMode_SIGN));
pars.bts0_band := f_vty_get_bts0_band();
vc_conn := f_start_handler(refers(f_TC_rsl_ms_pwr_dyn_ass_updown), pars, trxc_comp := true);
vc_conn.done;
}
f_vty_config(BTSVTY, "phy 0", "no osmotrx ms-power-loop");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_rsl_ms_pwr_dyn_up() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "phy 0", "osmotrx ms-power-loop -10");
for (var integer tn := 1; tn <= 1; tn := tn+1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ts_RSL_ChanMode_SIGN));
pars.bts0_band := f_vty_get_bts0_band();
vc_conn := f_start_handler(refers(f_TC_rsl_ms_pwr_dyn_up), pars, trxc_comp := true);
vc_conn.done;
}
f_vty_config(BTSVTY, "phy 0", "no osmotrx ms-power-loop");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_rsl_ms_pwr_dyn_max() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "phy 0", "osmotrx ms-power-loop -10");
for (var integer tn := 1; tn <= 1; tn := tn+1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ts_RSL_ChanMode_SIGN));
pars.bts0_band := f_vty_get_bts0_band();
vc_conn := f_start_handler(refers(f_TC_rsl_ms_pwr_dyn_max), pars, trxc_comp := true);
vc_conn.done;
}
f_vty_config(BTSVTY, "phy 0", "no osmotrx ms-power-loop");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_rsl_ms_pwr_dyn_down() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "phy 0", "osmotrx ms-power-loop -100");
for (var integer tn := 1; tn <= 1; tn := tn+1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ts_RSL_ChanMode_SIGN));
pars.bts0_band := f_vty_get_bts0_band();
vc_conn := f_start_handler(refers(f_TC_rsl_ms_pwr_dyn_down), pars, trxc_comp := true);
vc_conn.done;
}
f_vty_config(BTSVTY, "phy 0", "no osmotrx ms-power-loop");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_rsl_ms_pwr_dyn_active() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "phy 0", "osmotrx ms-power-loop -100");
for (var integer tn := 1; tn <= 1; tn := tn+1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ts_RSL_ChanMode_SIGN));
pars.bts0_band := f_vty_get_bts0_band();
vc_conn := f_start_handler(refers(f_TC_rsl_ms_pwr_dyn_active), pars, trxc_comp := true);
vc_conn.done;
}
f_vty_config(BTSVTY, "phy 0", "no osmotrx ms-power-loop");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_rsl_ms_pwr_dyn_active2() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "phy 0", "osmotrx ms-power-loop -100");
for (var integer tn := 1; tn <= 1; tn := tn+1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ts_RSL_ChanMode_SIGN));
pars.bts0_band := f_vty_get_bts0_band();
vc_conn := f_start_handler(refers(f_TC_rsl_ms_pwr_dyn_active2), pars, trxc_comp := true);
vc_conn.done;
}
f_vty_config(BTSVTY, "phy 0", "no osmotrx ms-power-loop");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
function f_TC_meas_res_speech_tchf(boolean facch_enabled) runs on test_CT {
var template RSL_IE_ChannelMode ch_mode;
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
ch_mode := ts_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM1);
for (var integer tn := 1; tn <= 1; tn := tn + 1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ch_mode));
pars.l1_pars.facch_enabled := facch_enabled;
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_meas_res_speech_tchf() runs on test_CT {
f_TC_meas_res_speech_tchf(false);
}
testcase TC_meas_res_speech_tchf_facch() runs on test_CT {
f_TC_meas_res_speech_tchf(true);
}
function f_TC_meas_res_speech_tchh(boolean facch_enabled) runs on test_CT {
var template RSL_IE_ChannelMode ch_mode;
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
ch_mode := ts_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM1);
for (var integer ss := 0; ss <= 1; ss := ss + 1) {
pars := valueof(t_Pars(t_RslChanNr_Lm(5, ss), ch_mode));
pars.l1_pars.facch_enabled := facch_enabled;
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_meas_res_speech_tchh() runs on test_CT {
f_TC_meas_res_speech_tchh(false)
}
testcase TC_meas_res_speech_tchh_facch() runs on test_CT {
f_TC_meas_res_speech_tchh(true)
}
testcase TC_meas_res_speech_tchh_toa256() runs on test_CT {
var template RSL_IE_ChannelMode ch_mode;
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "bts 0", "supp-meas-info toa256");
ch_mode := ts_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM1);
for (var integer ss := 0; ss <= 1; ss := ss + 1) {
pars := valueof(t_Pars(t_RslChanNr_Lm(5, ss), ch_mode));
pars.l1_pars.toa256_enabled := true;
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_meas_res_sign_tchf() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "bts 0", "no supp-meas-info toa256");
for (var integer tn := 1; tn <= 4; tn := tn+1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ts_RSL_ChanMode_SIGN(RSL_CHRT_TCH_F)));
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_meas_res_sign_tchh() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "bts 0", "no supp-meas-info toa256");
for (var integer ss := 0; ss <= 1; ss := ss+1) {
pars := valueof(t_Pars(t_RslChanNr_Lm(5, ss), ts_RSL_ChanMode_SIGN(RSL_CHRT_TCH_H)));
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_meas_res_sign_sdcch4() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "bts 0", "no supp-meas-info toa256");
for (var integer ss := 0; ss <= 3; ss := ss+1) {
pars := valueof(t_Pars(t_RslChanNr_SDCCH4(0, ss), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_meas_res_sign_sdcch8() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "bts 0", "no supp-meas-info toa256");
for (var integer ss := 0; ss <= 7; ss := ss+1) {
pars := valueof(t_Pars(t_RslChanNr_SDCCH8(6, ss), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_meas_res_sign_tchh_toa256() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
f_vty_config(BTSVTY, "bts 0", "supp-meas-info toa256");
for (var integer ss := 0; ss <= 1; ss := ss+1) {
pars := valueof(t_Pars(t_RslChanNr_Lm(5, ss), ts_RSL_ChanMode_SIGN(RSL_CHRT_TCH_H)));
pars.l1_pars.toa256_enabled := true;
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Make sure that we always get RSL MEASurement RESult messages regardless
* of what is sent on SACCH: (RR) Measurement Report or SAPI=3 data (SMS). */
private function f_TC_meas_res_sapi3(charstring id) runs on ConnHdlr {
timer Texec := 8.0;
timer Timpf := 2.0;
timer Tmr;
f_l1_tune(L1CTL);
RSL.clear;
f_est_dchan();
L1CTL.clear;
/* Establish the main SAPI=0 link on DCCH first */
f_tx_lapdm(ts_LAPDm_SABM(0, cr_MO_CMD, true, ''O), ts_RslLinkID_DCCH(0));
/* Give more time for the first RSL MEASurement RESult */
Tmr.start(0.480 * 2.0);
Texec.start; /* EXECution timer */
Timpf.start; /* IMPFung timer */
alt {
/* We expect RSL MEASurement RESult messages every ~480ms (plus some guard) */
[] RSL.receive(tr_RSL_MEAS_RES(g_pars.chan_nr)) {
/* Reschedule the MEAS RES timer */
Tmr.start(0.480 + 0.120);
repeat;
}
[] RSL.receive { repeat; }
[] Tmr.timeout {
setverdict(fail, "Timeout waiting for RSL MEAS RES");
}
/* Inject some SAPI=3 traffic on SACCH every 2 seconds */
[] Timpf.timeout {
f_tx_lapdm(ts_LAPDm_SABM(3, cr_MO_CMD, true, ''O), ts_RslLinkID_SACCH(3));
log("Injected SAPI=3 traffic on SACCH");
Timpf.start;
repeat;
}
/* We're good if survived so far */
[] Texec.timeout {
setverdict(pass);
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_meas_res_speech_tchf_sapi3() runs on test_CT {
var template RSL_IE_ChannelMode ch_mode;
var template ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
ch_mode := ts_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM1);
pars := t_Pars(t_RslChanNr_Bm(1), ch_mode);
vc_conn := f_start_handler(refers(f_TC_meas_res_sapi3), valueof(pars));
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_meas_res_speech_tchh_sapi3() runs on test_CT {
var template RSL_IE_ChannelMode ch_mode;
var template ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
ch_mode := ts_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM1);
pars := t_Pars(t_RslChanNr_Lm(5, 0), ch_mode);
vc_conn := f_start_handler(refers(f_TC_meas_res_sapi3), valueof(pars));
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* establish DChan, and send MS POWER CONTROL messages via RSL, verify that
* the BTS is forwarding those values to the MS via the SACCH L1 header. */
private function f_tc_rsl_ms_pwr_ctrl(charstring id) runs on ConnHdlr {
var SacchL1Header l1h;
var RSL_IE_MS_Power ms_power;
var RSL_Message rsl;
var uint5_t power_level := 0;
f_l1_tune(L1CTL);
RSL.clear;
f_est_dchan();
ms_power.reserved := 0;
ms_power.fpc_epc := false;
/* Send the first power control command. This will disable any BTS/TRX
* internal power control and switch the MS (which is not in scope of
* this test) to a constant power level. We start with a power level
* of 0 */
ms_power.power_level := power_level;
rsl := valueof(ts_RSL_MS_PWR_CTRL(g_chan_nr, ms_power));
RSL.send(rsl);
alt {
[] as_l1_sacch_l1h(l1h, do_apply := false) {
if (l1h.ms_power_lvl != power_level) {
setverdict(fail, "Power level := ", l1h.ms_power_lvl, "does not ",
"match the signaled (RSL) power level := ", power_level);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Signal a new power level via RSL for the next turn. */
if (power_level < 31) {
power_level := power_level + 1;
ms_power.power_level := power_level;
rsl := valueof(ts_RSL_MS_PWR_CTRL(g_chan_nr, ms_power));
RSL.send(rsl);
repeat;
}
}
/* Ignore all other blocks */
[] L1CTL.receive { repeat; }
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
setverdict(pass);
}
testcase TC_rsl_ms_pwr_ctrl() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer tn := 1; tn <= 4; tn := tn+1) {
pars := valueof(t_Pars(t_RslChanNr_Bm(tn), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_tc_rsl_ms_pwr_ctrl), pars);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* establish DChan, verify that the BTS sets the TA in the first SACCH L1 header.
TA for the IMM ASS messages is still controlled by g_pars.l1_pars.ms_actual_ta! */
private function f_tc_rsl_chan_initial_ta(charstring id) runs on ConnHdlr {
var uint5_t ta_to_test := 16;
var SacchL1Header l1h;
f_l1_tune(L1CTL);
RSL.clear;
/* tell fake_trx to use a given timing offset for all bursts */
f_trxc_fake_toffs256(ta_to_test*256);
f_est_dchan(more_ies :={valueof(t_RSL_IE(RSL_IE_TIMING_ADVANCE, RSL_IE_Body:{timing_adv := ta_to_test}))} );
alt {
[] as_l1_sacch_l1h(l1h, do_apply := false) {
if (l1h.actual_ta != ta_to_test) {
setverdict(fail, "TA in L1 header does not match the signaled (RSL) TA.");
}
}
/* Ignore all other blocks */
[] L1CTL.receive { repeat; }
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
setverdict(pass);
}
testcase TC_rsl_chan_initial_ta() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_tc_rsl_chan_initial_ta), pars,
pcu_comp := false, trxc_comp := true);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* establish DChan, verify that the BTS sets MS power in the first SACCH L1 header. */
private function f_tc_rsl_chan_initial_ms_pwr(charstring id) runs on ConnHdlr {
var uint5_t ms_power_level := 7;
var SacchL1Header l1h;
var RSL_IE_MS_Power ms_power;
ms_power.reserved := 0;
ms_power.fpc_epc := false;
ms_power.power_level := ms_power_level;
f_l1_tune(L1CTL);
RSL.clear;
f_est_dchan(more_ies :={valueof(t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{ms_power := ms_power}))} );
timer T := 1.0;
T.start;
alt {
/* Pick all SACCH blocks for checking */
[] as_l1_sacch_l1h(l1h, do_apply := false) {
if (l1h.ms_power_lvl != ms_power_level) {
setverdict(fail, "Power level := ", l1h.ms_power_lvl, "does not ",
"match the signaled (RSL) power level := ", ms_power_level);
}
}
/* Ignore all other blocks */
[] L1CTL.receive { repeat; }
[] T.timeout {
setverdict(fail, "Power Level in L1 header does not match the signaled (RSL) MS Power Level.");
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
setverdict(pass);
}
testcase TC_rsl_chan_initial_ms_pwr() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_testmatrix_each_chan(pars, refers(f_tc_rsl_chan_initial_ms_pwr));
}
/* Test if a channel without valid uplink bursts generates RSL CONN FAIL IND (TS 48.058 4.10) */
private function f_TC_conn_fail_crit(charstring id) runs on ConnHdlr {
f_l1_tune(L1CTL);
RSL.clear;
f_est_dchan();
f_sleep(2.0);
L1CTL.send(ts_L1CTL_DM_REL_REQ(g_chan_nr));
timer T := 40.0;
T.start;
alt {
[] RSL.receive(tr_RSL_CONN_FAIL_IND(g_chan_nr, ?)) {
setverdict(pass)
}
[] RSL.receive { repeat };
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "No CONN FAIL IND received");
}
}
f_rsl_chan_deact();
}
testcase TC_conn_fail_crit() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
pars := valueof(t_Pars(t_RslChanNr_SDCCH8(6, 3), ts_RSL_ChanMode_SIGN));
pars.t_guard := 60.0;
vc_conn := f_start_handler(refers(f_TC_conn_fail_crit), pars);
vc_conn.done;
}
/***********************************************************************
* Paging
***********************************************************************/
private function tmsi_is_dummy(TMSIP_TMSI_V tmsi) return boolean {
if (tmsi == 'FFFFFFFF'O) {
return true;
} else {
return false;
}
}
private type record allowedFn { integer frame_nr }
private template allowedFn bs_ag_blks_res_0 := { frame_nr := (6, 12, 16, 22, 26, 32, 36, 42, 46) }
private template allowedFn bs_ag_blks_res_1 := { frame_nr := (12, 16, 22, 26, 32, 36, 42, 46) }
private template allowedFn bs_ag_blks_res_2 := { frame_nr := (16, 22, 26, 32, 36, 42, 46) }
private template allowedFn bs_ag_blks_res_3 := { frame_nr := (22, 26, 32, 36, 42, 46) }
private template allowedFn bs_ag_blks_res_4 := { frame_nr := (26, 32, 36, 42, 46) }
private template allowedFn bs_ag_blks_res_5 := { frame_nr := (32, 36, 42, 46) }
private template allowedFn bs_ag_blks_res_6 := { frame_nr := (36, 42, 46) }
private template allowedFn bs_ag_blks_res_7 := { frame_nr := (42, 46) }
private function check_pch_fn(integer frame_nr, integer bs_ag_blks_res) runs on test_CT
{
var integer frame_nr_51;
frame_nr_51 := frame_nr mod 51
var allowedFn fn_check;
fn_check.frame_nr := frame_nr_51;
if (bs_ag_blks_res < 0 or bs_ag_blks_res > 7) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "bs_ag_blks_res out of valid range (0..7)");
return;
}
if (bs_ag_blks_res == 0 and match(fn_check, bs_ag_blks_res_0)) {
return;
}
if (bs_ag_blks_res == 1 and match(fn_check, bs_ag_blks_res_1)) {
return;
}
if (bs_ag_blks_res == 2 and match(fn_check, bs_ag_blks_res_2)) {
return;
}
if (bs_ag_blks_res == 3 and match(fn_check, bs_ag_blks_res_3)) {
return;
}
if (bs_ag_blks_res == 4 and match(fn_check, bs_ag_blks_res_4)) {
return;
}
if (bs_ag_blks_res == 5 and match(fn_check, bs_ag_blks_res_5)) {
return;
}
if (bs_ag_blks_res == 6 and match(fn_check, bs_ag_blks_res_6)) {
return;
}
if (bs_ag_blks_res == 7 and match(fn_check, bs_ag_blks_res_7)) {
return;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "received paging on AGCH");
return;
}
private altstep as_l1_count_paging(inout integer num_paging_rcv_msgs,
inout integer num_paging_rcv_ids,
PagingTestCfg cfg)
runs on test_CT {
var L1ctlDlMessage dl;
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0), ?, c_DummyUI)) {
repeat;
}
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
var octetstring without_plen :=
substr(dl.payload.data_ind.payload, 1, lengthof(dl.payload.data_ind.payload)-1);
var PDU_ML3_NW_MS rr := dec_PDU_ML3_NW_MS(without_plen);
check_pch_fn(dl.dl_info.frame_nr, cfg.bs_ag_blks_res);
if (match(rr, tr_PAGING_REQ1(tr_MI_LV(t_MI_NoIdentity(?))))) {
/* Ignore empty RR Paging Request (PCH filling) messages.
* TODO: does it make sense to count them? */
} else if (match(rr, tr_PAGING_REQ1)) {
num_paging_rcv_msgs := num_paging_rcv_msgs + 1;
num_paging_rcv_ids := num_paging_rcv_ids + 1;
if (ispresent(rr.msgs.rrm.pagingReq_Type1.mobileIdentity2)) {
num_paging_rcv_ids := num_paging_rcv_ids + 1;
}
} else if (match(rr, tr_PAGING_REQ2)) {
num_paging_rcv_msgs := num_paging_rcv_msgs + 1;
if (not tmsi_is_dummy(rr.msgs.rrm.pagingReq_Type2.mobileIdentity1)) {
num_paging_rcv_ids := num_paging_rcv_ids + 1;
}
if (not tmsi_is_dummy(rr.msgs.rrm.pagingReq_Type2.mobileIdentity2)) {
num_paging_rcv_ids := num_paging_rcv_ids + 1;
}
if (ispresent(rr.msgs.rrm.pagingReq_Type2.mobileIdentity3)) {
num_paging_rcv_ids := num_paging_rcv_ids + 1;
}
} else if (match(rr, tr_PAGING_REQ3)) {
num_paging_rcv_msgs := num_paging_rcv_msgs + 1;
if (not tmsi_is_dummy(rr.msgs.rrm.pagingReq_Type3.mobileIdentity1)) {
num_paging_rcv_ids := num_paging_rcv_ids + 1;
}
if (not tmsi_is_dummy(rr.msgs.rrm.pagingReq_Type3.mobileIdentity2)) {
num_paging_rcv_ids := num_paging_rcv_ids + 1;
}
if (not tmsi_is_dummy(rr.msgs.rrm.pagingReq_Type3.mobileIdentity3)) {
num_paging_rcv_ids := num_paging_rcv_ids + 1;
}
if (not tmsi_is_dummy(rr.msgs.rrm.pagingReq_Type3.mobileIdentity4)) {
num_paging_rcv_ids := num_paging_rcv_ids + 1;
}
}
repeat;
}
}
private type record PagingTestCfg {
boolean combined_ccch,
integer bs_ag_blks_res,
float load_factor,
/* Mix in a paging request through the PCU socket for every Nth (ps_load_modulus)
* paging command that is issued via RSL. */
integer ps_load_modulus,
/* Wait until the paging queue inside the BTS is congested before starting to add
* pagings via the PCU socket (0 = disabled) */
boolean ps_wait_cong,
/* Maximum time to wait until the paging queue is drained at the end of the test.
* This usually takes about 15s (size: 200, ~ 13 per s -> 15s), it is recommended
* to set this value to 18s, however the draining process may take significantly
* longer when the queue contains paging requests for PS as those are implemented
* as immediate assignments and require an entire MAC block alone. */
float queue_drain_timeout,
boolean exp_load_ind,
boolean exp_overload,
boolean use_tmsi
}
private type record PagingTestState {
integer num_paging_sent,
integer num_paging_rcv_msgs,
integer num_paging_rcv_ids,
integer num_overload,
/* When free space inside the paging queue reaches more than 2 thirds
* of its capacity cong_detected is set to true. */
boolean cong_detected
}
/* Helper function for paging related testing */
private function f_TC_paging(PagingTestCfg cfg) runs on test_CT return PagingTestState {
/* Using the PCU socket affects the timing of the paging processing. We only
* activate the PCU socket if we do paging load tests that include additional
* paging load that is introduced through the PCU socket. */
if (cfg.ps_load_modulus > 0) {
f_init_with_pcuif();
} else {
f_init();
}
f_init_l1ctl();
f_l1_tune(L1CTL);
var octetstring imm_ass := f_rnd_octstring(23);
var ASP_RSL_Unitdata load_ind;
var PagingTestState st := {
num_paging_sent := 0,
num_paging_rcv_msgs := 0,
num_paging_rcv_ids := 0,
num_overload := 0,
cong_detected := false
};
var float max_pch_blocks_per_sec := f_pch_block_rate_est(cfg.combined_ccch, cfg.bs_ag_blks_res);
var float max_pch_imsi_per_sec;
if (cfg.use_tmsi) {
max_pch_imsi_per_sec := max_pch_blocks_per_sec * 4.0; /* Type 3 */
} else {
max_pch_imsi_per_sec := max_pch_blocks_per_sec * 2.0; /* Type 1 */
}
var float pch_blocks_per_sec := max_pch_imsi_per_sec * cfg.load_factor;
var float interval := 1.0 / pch_blocks_per_sec;
var float time_total := 20.0;
var integer pkt_total := float2int(time_total * pch_blocks_per_sec);
log("pch_blocks_total=", pkt_total," pch_blocks_per_sec=", pch_blocks_per_sec, " interval=", interval);
timer T_total := 300.0; /* big value (far bigger than time_total), used to count elapsed time */
T_total.start;
timer T_itv := 0.0;
T_itv.start;
while (st.num_paging_sent < pkt_total) {
alt {
/* check for presence of CCCH LOAD IND (paging load) */
[cfg.exp_overload] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_PAGING_LOAD_IND(0))) {
st.num_overload := st.num_overload + 1;
repeat;
}
[not cfg.exp_overload] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_PAGING_LOAD_IND(0))) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected PCH Overload");
}
[cfg.exp_load_ind] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_PAGING_LOAD_IND)) -> value load_ind {
log("Rx LOAD_IND");
/* Detect paging congestion: The logic inside the BTS will diagnose the
* paging queue as congested when the fill state is more than 66% (which
* is two thirds fill state, or approx. 66 of 200 paging slots. When a
* paging congestion is detected pagings that are issued through the PCU
* socket (PS) are dropped in order to prefer the CS related pagings. */
if (load_ind.rsl.ies[1].body.paging_load < 66) {
st.cong_detected := true;
}
/* FIXME: analyze/verify interval + contents */
repeat;
}
/* ignore other RSL messages like RF RESource INDication */
[] RSL_CCHAN.receive { repeat; }
/* check if paging requests arrive on Um side */
[] as_l1_count_paging(st.num_paging_rcv_msgs, st.num_paging_rcv_ids, cfg);
[] L1CTL.receive { repeat; }
/* Only relevant when testing including with PCU sock connected */
[] PCU.receive { repeat; }
[] T_itv.timeout {
/* Send paging cmds based on elapsed time */
var integer new_sent := f_min(pkt_total, float2int(T_total.read * pch_blocks_per_sec) + 1);
while (st.num_paging_sent < new_sent) {
var MobileIdentityV mi;
/* build mobile Identity */
if (cfg.use_tmsi) {
mi := valueof(t_MI_TMSI(f_rnd_octstring(4)));
} else {
mi := valueof(ts_MI_IMSI(f_gen_imsi(st.num_paging_sent)));
}
/* Send RSL PAGING COMMAND */
RSL_CCHAN.send(ts_ASP_RSL_UD(ts_RSL_PAGING_CMD(mi, st.num_paging_sent mod 4)));
/* Add additional pagings through the PCU socket interface. */
if (cfg.ps_load_modulus > 0 and st.num_paging_sent mod cfg.ps_load_modulus == 0) {
if (st.cong_detected == true or cfg.ps_wait_cong == false) {
f_PCUIF_tx_imm_ass_pch(PCU, g_pcu_conn_id, imm_ass, '123459987'H, wait_for_cnf := false);
}
}
st.num_paging_sent := st.num_paging_sent + 1;
}
if (st.num_paging_sent < pkt_total) {
/* Wait for interval to next PAGING COMMAND */
var float time_now := T_total.read;
var float next_sched := int2float(st.num_paging_sent)*interval;
if (next_sched > time_now) {
T_itv.start(next_sched - time_now);
} else {
T_itv.start(0.0);
}
} else {
/* We are done, no need to keep counting */
T_total.stop;
}
}
[] T_total.timeout { }
}
}
timer T_wait := cfg.queue_drain_timeout
T_wait.start;
alt {
[] as_l1_count_paging(st.num_paging_rcv_msgs, st.num_paging_rcv_ids, cfg);
[] L1CTL.receive { repeat; }
/* 65535 == empty paging queue, we can terminate*/
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_PAGING_LOAD_IND(65535))) { }
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_PAGING_LOAD_IND)) -> value load_ind { repeat; }
/* ignore other RSL messages like RF RESource INDication */
[] RSL_CCHAN.receive { repeat; }
/* Only relevant when testing including with PCU sock connected */
[] PCU.receive { repeat; }
[] T_wait.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Waiting for paging queue, last detected fill state: ", load_ind.rsl.ies[1].body.paging_load));
}
}
log("num_paging_sent=", st.num_paging_sent, " rcvd_msgs=", st.num_paging_rcv_msgs,
" rcvd_ids=", st.num_paging_rcv_ids);
return st;
}
/* Create ~ 80% paging load (IMSI only) sustained for about 20s, verifying that
* - the number of Mobile Identities on Um PCH match the number of pages on RSL
* - that CCCH LOAD IND (PCH) are being generated
* - that CCCH LOAD IND (PCH) [no load] is received after paging flood is over */
testcase TC_paging_imsi_80percent() runs on test_CT {
var SystemInformation si3 := valueof(ts_SI3_default);
var PagingTestCfg cfg := {
combined_ccch := true,
bs_ag_blks_res := si3.payload.si3.ctrl_chan_desc.bs_ag_blks_res,
load_factor := 0.8,
ps_load_modulus := 0,
ps_wait_cong := false,
queue_drain_timeout := 18.0,
exp_load_ind := true,
exp_overload := false,
use_tmsi := false
};
var PagingTestState st := f_TC_paging(cfg);
if (st.num_paging_sent != st.num_paging_rcv_ids) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Expected ", st.num_paging_sent, " pagings but have ",
st.num_paging_rcv_ids));
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Create ~ 80% paging load (TMSI only) sustained for about 20s, verifying that
* - the number of Mobile Identities on Um PCH match the number of pages on RSL
* - that CCCH LOAD IND (PCH) are being generated
* - that CCCH LOAD IND (PCH) [no load] is received after paging flood is over */
testcase TC_paging_tmsi_80percent() runs on test_CT {
var SystemInformation si3 := valueof(ts_SI3_default);
var PagingTestCfg cfg := {
combined_ccch := true,
bs_ag_blks_res := si3.payload.si3.ctrl_chan_desc.bs_ag_blks_res,
load_factor := 0.8,
ps_load_modulus := 0,
ps_wait_cong := false,
queue_drain_timeout := 18.0,
exp_load_ind := true,
exp_overload := false,
use_tmsi := true
};
var PagingTestState st := f_TC_paging(cfg);
if (st.num_paging_sent != st.num_paging_rcv_ids) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Expected ", st.num_paging_sent, " pagings but have ",
st.num_paging_rcv_ids));
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Create ~ 200% paging load (IMSI only) sustained for about 20s, verifying that
* - the number of Mobile Identities on Um PCH are ~ 82% of the number of pages on RSL
* - that CCCH LOAD IND (PCH) are being generated and reach 0 at some point
* - that CCCH LOAD IND (PCH) [no load] is received after paging flood is over */
testcase TC_paging_imsi_200percent() runs on test_CT {
var SystemInformation si3 := valueof(ts_SI3_default);
var PagingTestCfg cfg := {
combined_ccch := true,
bs_ag_blks_res := si3.payload.si3.ctrl_chan_desc.bs_ag_blks_res,
load_factor := 2.0,
ps_load_modulus := 0,
ps_wait_cong := false,
queue_drain_timeout := 18.0,
exp_load_ind := true,
exp_overload := true,
use_tmsi := false
};
var PagingTestState st := f_TC_paging(cfg);
/* We expect about 80-85% to pass, given that we can fill the paging buffer of 200
* slots and will fully drain that buffer before returning */
var template integer tpl := (st.num_paging_sent*78/100 .. st.num_paging_sent *85/100);
if (not match(st.num_paging_rcv_ids, tpl)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Expected ", tpl, " pagings but have ", st.num_paging_rcv_ids));
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Create ~ 200% paging load (TMSI only) sustained for about 20s, verifying that
* - the number of Mobile Identities on Um PCH are ~ 82% of the number of pages on RSL
* - that CCCH LOAD IND (PCH) are being generated and reach 0 at some point
* - that CCCH LOAD IND (PCH) [no load] is received after paging flood is over */
testcase TC_paging_tmsi_200percent() runs on test_CT {
var SystemInformation si3 := valueof(ts_SI3_default);
var PagingTestCfg cfg := {
combined_ccch := true,
bs_ag_blks_res := si3.payload.si3.ctrl_chan_desc.bs_ag_blks_res,
load_factor := 2.0,
ps_load_modulus := 0,
ps_wait_cong := false,
queue_drain_timeout := 18.0,
exp_load_ind := true,
exp_overload := true,
use_tmsi := true
};
var PagingTestState st := f_TC_paging(cfg);
/* We expect about 70% to pass, given that we can fill the paging buffer of 200
* slots and will fully drain that buffer before returning */
var template integer tpl := (st.num_paging_sent*64/100 .. st.num_paging_sent *72/100);
if (not match(st.num_paging_rcv_ids, tpl)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Expected ", tpl, " pagings but have ", st.num_paging_rcv_ids));
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Same as above, but with additional paging load created by PS pagings that
* are issued through the PCU socket. The additional load is introduced when
* the paging queue is already two thirds full. Since the BTS implements a
* prioritization logic that drops the pagings from the PCU under in those
* congestive sitautions the behaviour on the PS side is expected to be
* unaffected and match the behavior of TC_paging_imsi_200percent() */
testcase TC_paging_imsi_200percent_with_ps() runs on test_CT {
var SystemInformation si3 := valueof(ts_SI3_default);
var PagingTestCfg cfg := {
combined_ccch := true,
bs_ag_blks_res := si3.payload.si3.ctrl_chan_desc.bs_ag_blks_res,
load_factor := 2.0,
ps_load_modulus := 16,
ps_wait_cong := true,
queue_drain_timeout := 18.0,
exp_load_ind := true,
exp_overload := true,
use_tmsi := false
};
var PagingTestState st := f_TC_paging(cfg);
/* We expect about 80-85% to pass, given that we can fill the paging buffer of 200
* slots and will fully drain that buffer before returning */
var template integer tpl := (st.num_paging_sent*78/100 .. st.num_paging_sent *85/100);
if (not match(st.num_paging_rcv_ids, tpl)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Expected ", tpl, " pagings but have ", st.num_paging_rcv_ids));
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/***********************************************************************
* Immediate Assignment / AGCH
***********************************************************************/
private const MobileAllocationLV c_MA_null := {
len := 0,
ma := ''B
}
private function f_fmt_ia_stats(integer num_tx, integer num_rx, integer num_del) return charstring {
return int2str(num_tx) & " sent, "
& int2str(num_rx) & " received, "
& int2str(num_del) & " deleted";
}
private function f_TC_imm_ass(integer num_total, float sleep_s, float exp_pass) runs on test_CT {
var L1ctlDlMessage l1_dl;
timer T := 10.0;
var integer num_tx := 0;
var integer num_rx := 0;
var integer num_del := 0;
var charstring res_str;
var float rx_ratio;
f_init();
f_init_l1ctl();
f_l1_tune(L1CTL);
for (var integer i := 0; i < num_total; i := i+1) {
var ChannelDescription ch_desc := valueof(ts_ChanDescH0(ts_RslChanNr_SDCCH4(0, 0),
mp_trx_pars[0].arfcn,
mp_tsc_def));
var GsmRrMessage ia := valueof(ts_IMM_ASS(42, i, 5, ch_desc, c_MA_null));
var octetstring ia_enc := enc_GsmRrMessage(ia);
RSL_CCHAN.send(ts_ASP_RSL_UD(ts_RSL_IMM_ASSIGN(ia_enc, 0)));
num_tx := num_tx+1;
f_sleep(sleep_s);
}
/* FIXME: check if imm.ass arrive on Um side */
T.start;
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_DELETE_IND(?, 0))) {
num_del := num_del+1;
repeat;
}
[] RSL_CCHAN.receive {
repeat;
}
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0), ?)) -> value l1_dl {
var GsmRrMessage rr := dec_GsmRrMessage(l1_dl.payload.data_ind.payload);
if (not match(rr, tr_IMM_ASS(42, ?, 5, ?, ?))) {
/* FIXME: Why are we seeing paging requests on PCH/AGCH? */
//Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected IMM-ASS values on AGCH: ", rr));
} else {
num_rx := num_rx+1;
}
repeat;
}
[] L1CTL.receive { repeat; }
[] T.timeout { }
}
res_str := f_fmt_ia_stats(num_tx, num_rx, num_del);
log("AGCH test: " & res_str);
if (num_rx + num_del != num_tx) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "RX + DEL != TX ?!?: " & res_str);
}
rx_ratio := int2float(num_rx) / int2float(num_tx);
if (rx_ratio < exp_pass*0.8 or rx_ratio > exp_pass*1.2) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "RX ratio ("&float2str(rx_ratio)&") far from expected ("&float2str(exp_pass)&") " & res_str);
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* send a long burst of 1000 IMM.ASS with 20ms spacing (50 per s); expect 75% of them to be deleted */
testcase TC_imm_ass_1000_20ms() runs on test_CT {
f_TC_imm_ass(1000, 0.02, 0.25);
}
/* send a short burst of 200 IMM.ASS without any spacing; expect 95% of them to be deleted */
testcase TC_imm_ass_200_0ms() runs on test_CT {
f_TC_imm_ass(200, 0.0, 0.05);
}
/* send 150 IMM.ASS at rate of 13/s; expect none of them to be deleted */
testcase TC_imm_ass_200_76ms() runs on test_CT {
f_TC_imm_ass(150, 0.076, 1.00);
}
/***********************************************************************
* BCCH
***********************************************************************/
/* tuple of Frame Number + decoded SI */
private type record SystemInformationFn {
GsmFrameNumber frame_number,
SystemInformation si
}
/* an arbitrary-length vector of decoded SI + gsmtap header */
private type record of SystemInformationFn SystemInformationVector;
/* an array of SI-vectors indexed by TC value */
private type SystemInformationVector SystemInformationVectorPerTc[8];
/* determine if a given SI vector contains given SI type at least once */
private function f_si_vecslot_contains(SystemInformationVector arr,
RrMessageType key,
boolean bcch_ext := false)
return boolean {
for (var integer i:= 0; i< sizeof(arr); i := i + 1) {
var integer fn_mod51 := arr[i].frame_number mod 51;
if (not bcch_ext and fn_mod51 == 2 or
bcch_ext and fn_mod51 == 6) {
if (arr[i].si.header.message_type == key) {
return true;
}
}
}
return false;
}
/* ensure a given TC slot of the SI vector contains given SI type at least once at TC */
private function f_ensure_si_vec_contains(SystemInformationVectorPerTc arr,
integer tc, RrMessageType key,
boolean ext_bcch := false)
{
if (not f_si_vecslot_contains(arr[tc], key, ext_bcch)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("No ", key, " in TC=", tc, "!"));
}
}
/* check if a given SI vector contains given SI type at least once on any TC */
private function f_si_vec_contains(SystemInformationVectorPerTc arr,
RrMessageType key)
return boolean {
for (var integer tc:= 0; tc < sizeof(arr); tc := tc + 1) {
if (f_si_vecslot_contains(arr[tc], key) or
f_si_vecslot_contains(arr[tc], key, true)) {
return true;
}
}
return false;
}
/* determine if a given SI vector contains given SI type at least N of M times */
private function f_si_vecslot_contains_n_of_m(SystemInformationVector arr,
RrMessageType key,
boolean bcch_ext := false,
integer n := 1, integer m := 4)
return boolean {
var integer count := 0;
if (sizeof(arr) < m) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Error: Insufficient SI in array: "
& int2str(sizeof(arr)) & " < " & int2str(m));
}
for (var integer i:= 0; i < m; i := i + 1) {
var integer fn_mod51 := arr[i].frame_number mod 51;
if (not bcch_ext and fn_mod51 == 2 or
bcch_ext and fn_mod51 == 6) {
if (arr[i].si.header.message_type == key) {
count := count + 1;
}
}
}
if (count >= n) {
return true;
} else {
return false;
}
}
/* ensure a given TC slot of the SI vector contains given SI type at least N out of M times at TC */
private function f_ensure_si_vec_contains_n_of_m(SystemInformationVectorPerTc arr,
integer tc, RrMessageType key,
boolean ext_bcch := false,
integer n, integer m)
{
if (not f_si_vecslot_contains_n_of_m(arr[tc], key, ext_bcch, n, m)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Not ", n, "/", m, " of ", key, " in TC=", tc, "!"));
}
}
/* determine if a given SI vector contains given SI type at least once */
private function f_si_vecslot_contains_only(SystemInformationVector arr,
RrMessageType key,
boolean bcch_ext := false)
return boolean {
for (var integer i:= 0; i< sizeof(arr); i := i + 1) {
var integer fn_mod51 := arr[i].frame_number mod 51;
if (not bcch_ext and fn_mod51 == 2 or
bcch_ext and fn_mod51 == 6) {
if (arr[i].si.header.message_type != key) {
return false;
}
}
}
return true;
}
/* ensure a given TC slot of the SI vector contains only given SI type */
private function f_ensure_si_vec_contains_only(SystemInformationVectorPerTc arr,
integer tc, RrMessageType key,
boolean ext_bcch := false)
{
if (not f_si_vecslot_contains_only(arr[tc], key, ext_bcch)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Not all ", key, " in TC=", tc, "!"));
}
}
/* SI configuration of cell, against which we validate actual SI messages */
private type set SystemInformationConfig {
boolean bcch_extended,
boolean si1_present,
boolean si2bis_present,
boolean si2ter_present,
boolean si2quater_present,
boolean si7_present,
boolean si8_present,
boolean si9_present,
boolean si13_present,
boolean si13alt_present,
boolean si15_present,
boolean si16_present,
boolean si17_present,
boolean si2n_present,
boolean si21_present,
boolean si22_present
}
/* validate the SI scheduling according to TS 45.002 version 14.1.0 Release 14, Section 6.3.1.3 */
private function f_validate_si_scheduling(SystemInformationConfig cfg,
SystemInformationVectorPerTc si_per_tc)
{
var integer i;
for (i := 0; i < sizeof(si_per_tc); i := i + 1) {
if (sizeof(si_per_tc[i]) == 0) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("No SI messages for TC=", i));
}
}
if (cfg.si1_present) {
/* ii) System Information Type 1 needs to be sent if frequency hopping is in use or
* when the NCH is present in a cell. If the MS finds another message on BCCH Norm
* when TC = 0, it can assume that System Information Type 1 is not in use. */
f_ensure_si_vec_contains(si_per_tc, 0, SYSTEM_INFORMATION_TYPE_1);
/* make sure *ALL* contain SI1 */
f_ensure_si_vec_contains_only(si_per_tc, 0, SYSTEM_INFORMATION_TYPE_1);
}
f_ensure_si_vec_contains(si_per_tc, 1, SYSTEM_INFORMATION_TYPE_2);
/* iii) A SI 2 message will be sent at least every time TC = 1 */
f_ensure_si_vec_contains(si_per_tc, 2, SYSTEM_INFORMATION_TYPE_3);
f_ensure_si_vec_contains(si_per_tc, 6, SYSTEM_INFORMATION_TYPE_3);
f_ensure_si_vec_contains(si_per_tc, 3, SYSTEM_INFORMATION_TYPE_4);
f_ensure_si_vec_contains(si_per_tc, 7, SYSTEM_INFORMATION_TYPE_4);
/* iii) System information type 2 bis or 2 ter messages are sent if needed, as determined by the
* system operator. If only one of them is needed, it is sent when TC = 5. If both are
* needed, 2bis is sent when TC = 5 and 2ter is sent at least once within any of 4
* consecutive occurrences of TC = 4. */
if (cfg.si2bis_present and not cfg.si2ter_present) {
f_ensure_si_vec_contains(si_per_tc, 5, SYSTEM_INFORMATION_TYPE_2bis);
} else if (cfg.si2ter_present and not cfg.si2bis_present) {
f_ensure_si_vec_contains(si_per_tc, 5, SYSTEM_INFORMATION_TYPE_2ter);
} else if (cfg.si2ter_present and cfg.si2bis_present) {
f_ensure_si_vec_contains(si_per_tc, 5, SYSTEM_INFORMATION_TYPE_2bis);
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_2ter, false, 1, 4);
}
if (cfg.si7_present or cfg.si8_present) {
/* vi) Use of System Information type 7 and 8 is not always necessary. It is necessary
* if System Information type 4 does not contain all information needed for cell
* selection and reselection. */
if (not cfg.bcch_extended) {
testcase.stop("Error: SI7/SI8 require BCCH Extd.");
}
if (cfg.si7_present) {
f_ensure_si_vec_contains(si_per_tc, 7, SYSTEM_INFORMATION_TYPE_7, true);
}
if (cfg.si8_present) {
f_ensure_si_vec_contains(si_per_tc, 3, SYSTEM_INFORMATION_TYPE_8, true);
}
}
if (cfg.si2quater_present) {
/* iii) System information type 2 quater is sent if needed, as determined by the system
* operator. If sent on BCCH Norm, it shall be sent when TC = 5 if neither of 2bis
* and 2ter are used, otherwise it shall be sent at least once within any of 4
* consecutive occurrences of TC = 4. If sent on BCCH Ext, it is sent at least once
* within any of 4 consecutive occurrences of TC = 5. */
if (not (cfg.bcch_extended)) {
if (not (cfg.si2bis_present or cfg.si2ter_present)) {
f_ensure_si_vec_contains(si_per_tc, 5, SYSTEM_INFORMATION_TYPE_2quater);
} else {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_2quater, false, 1, 4);
}
} else {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 5, SYSTEM_INFORMATION_TYPE_2quater, true, 1, 4);
}
}
if (cfg.si9_present) {
/* vi) System Information type 9 is sent in those blocks with TC = 4 which are specified
* in system information type 3 as defined in 3GPP TS 44.018. */
f_ensure_si_vec_contains(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_9); // FIXME SI3
}
if (cfg.si13_present) {
/* vii) System Information type 13 is only related to the GPRS service. System Information
* Type 13 need only be sent if GPRS support is indicated in one or more of System
* Information Type 3 or 4 or 7 or 8 messages. These messages also indicate if the
* message is sent on the BCCH Norm or if the message is transmitted on the BCCH Ext.
* In the case that the message is sent on the BCCH Norm, it is sent at least once
* within any of 4 consecutive occurrences of TC=4. */
if (not cfg.bcch_extended) {
log("not-bccch-extended");
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_13, false, 1, 4);
} else {
log("bccch-extended");
f_ensure_si_vec_contains(si_per_tc, 0, SYSTEM_INFORMATION_TYPE_13, true);
}
if (f_si_vec_contains(si_per_tc, SYSTEM_INFORMATION_TYPE_13alt)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Cannot have SI13alt and SI13");
}
}
if (cfg.si16_present or cfg.si17_present) {
/* viii) System Information type 16 and 17 are only related to the SoLSA service. They
* should not be sent in a cell where network sharing is used (see rule xv). */
if (cfg.si22_present) {
testcase.stop("Error: Cannot have SI16/SI17 and SI22!");
}
if (f_si_vec_contains(si_per_tc, SYSTEM_INFORMATION_TYPE_22)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Cannot have SI16/SI17 and SI22!");
}
if (not cfg.bcch_extended) {
testcase.stop("Error: SI16/SI17 requires BCCH Extd!");
}
if (cfg.si16_present) {
f_ensure_si_vec_contains(si_per_tc, 6, SYSTEM_INFORMATION_TYPE_16, true);
}
if (cfg.si17_present) {
f_ensure_si_vec_contains(si_per_tc, 2, SYSTEM_INFORMATION_TYPE_17, true);
}
}
/* ix) System Information type 18 and 20 are sent in order to transmit non-GSM
* broadcast information. The frequency with which they are sent is determined by the
* system operator. System Information type 9 identifies the scheduling of System
* Information type 18 and 20 messages. */
/* x) System Information Type 19 is sent if COMPACT neighbours exist. If System
* Information Type 19 is present, then its scheduling shall be indicated in System
* Information Type 9. */
if (cfg.si15_present) {
/* xi) System Information Type 15 is broadcast if dynamic ARFCN mapping is used in the
* PLMN. If sent on BCCH Norm, it is sent at least once within any of 4 consecutive
* occurrences of TC = 4. If sent on BCCH Ext, it is sent at least once within any of
* 4 consecutive occurrences of TC = 1. */
if (not cfg.bcch_extended) {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_15, false, 1, 4);
} else {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 1, SYSTEM_INFORMATION_TYPE_15, true, 1, 4);
}
}
if (cfg.si13alt_present) {
/* xii) System Information type 13 alt is only related to the GERAN Iu mode. System
* Information Type 13 alt need only be sent if GERAN Iu mode support is indicated in
* one or more of System Information Type 3 or 4 or 7 or 8 messages and SI 13 is not
* broadcast. These messages also indicate if the message is sent on the BCCH Norm or
* if the message is transmitted on the BCCH Ext. In the case that the message is sent
* on the BCCH Norm, it is sent at least once within any of 4 consecutive occurrences
* of TC = 4. */
if (cfg.si13_present) {
testcase.stop("Error: Cannot have SI13alt and SI13");
}
if (f_si_vec_contains(si_per_tc, SYSTEM_INFORMATION_TYPE_13)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Cannot have SI13alt and SI13");
}
if (not cfg.bcch_extended) {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_13alt, false, 1, 4);
} else {
f_ensure_si_vec_contains(si_per_tc, 0, SYSTEM_INFORMATION_TYPE_13alt, true);
}
}
if (cfg.si2n_present) {
/* xiii) System Information Type 2n is optionally sent on BCCH Norm or BCCH Ext if needed,
* as determined by the system operator. In the case that the message is sent on the
* BCCH Norm, it is sent at least once within any of 4 consecutive occurrences of TC =
* 4. If the message is sent on BCCH Ext, it is sent at least once within any of 2
* consecutive occurrences of TC = 4. */
if (not cfg.bcch_extended) {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_2n, false, 1, 4);
} else {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_2n, true, 2, 4);
}
}
if (cfg.si21_present) {
/* xiv) System Information Type 21 is optionally sent on BCCH Norm or BCCH Ext, as
* determined by the system operator. If Extended Access Barring is in use in the cell
* then this message is sent at least once within any of 4 consecutive occurrences of
* TC = 4 regardless if it is sent on BCCH Norm or BCCH Ext. If BCCH Ext is used in a
* cell then this message shall only be sent on BCCH Ext. */
if (not cfg.bcch_extended) {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_21, false, 1, 4);
} else {
f_ensure_si_vec_contains_n_of_m(si_per_tc, 4, SYSTEM_INFORMATION_TYPE_21, true, 1, 4);
if (f_si_vecslot_contains(si_per_tc[4], SYSTEM_INFORMATION_TYPE_21)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Cannot have SI21 on BCCH Norm if BCCH Extd enabled!");
}
}
}
if (cfg.si22_present) {
/* xv) System Information Type 22 is sent if network sharing is in use in the cell. It
* should not be sent in a cell where SoLSA is used (see rule viii). System
* Information Type 22 instances shall be sent on BCCH Ext within any occurrence of TC
* =2 and TC=6. */
if (cfg.si16_present or cfg.si17_present) {
testcase.stop("Error: Cannot have SI16/SI17 and SI22!");
}
if (f_si_vec_contains(si_per_tc, SYSTEM_INFORMATION_TYPE_16) or
f_si_vec_contains(si_per_tc, SYSTEM_INFORMATION_TYPE_17)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Cannot have SI16/SI17 and SI22!");
}
if (not cfg.bcch_extended) {
testcase.stop("Error: SI22 requires BCCH Extd!");
} else {
f_ensure_si_vec_contains_only(si_per_tc, 2, SYSTEM_INFORMATION_TYPE_22, true);
f_ensure_si_vec_contains_only(si_per_tc, 6, SYSTEM_INFORMATION_TYPE_22, true);
}
}
}
/* sample Systme Information for specified duration via L1CTL */
private function f_l1_sample_si(L1CTL_PT pt, float duration := 8.0)
return SystemInformationVectorPerTc {
timer T := duration;
var SystemInformationVectorPerTc si_per_tc;
var L1ctlDlMessage l1_dl;
/* initialize all per-TC vectors empty */
for (var integer i:= 0; i < sizeof(si_per_tc); i := i+1) {
si_per_tc[i] := {};
}
/* flush all previous L1 queued msgs */
pt.clear;
T.start;
alt {
[] pt.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0), ?)) -> value l1_dl {
var SystemInformationFn sig := { frame_number := l1_dl.dl_info.frame_nr };
if (dec_SystemInformationSafe(l1_dl.payload.data_ind.payload, sig.si) != 0) {
log("Ignoring non-RR or invalid SI ", l1_dl);
repeat;
}
var integer tc := f_gsm_compute_tc(sig.frame_number);
log("SI received at TC=", tc, ": ", sig.si);
/* append to the per-TC bucket */
si_per_tc[tc] := si_per_tc[tc] & { sig };
repeat;
}
[] pt.receive { repeat; }
[] T.timeout { }
}
for (var integer i:= 0; i < sizeof(si_per_tc); i := i+1) {
log(testcasename(), ": TC=", i, " has #of SI=", sizeof(si_per_tc[i]));
}
log("si_per_tc=", si_per_tc);
return si_per_tc;
}
/* helper function: Set given SI via RSL + validate scheduling.
* CALLER MUST MAKE SURE TO CHANGE GLOBAL si_cfg! */
private function f_TC_si_sched(float duration := 8.0) runs on test_CT {
var SystemInformationVectorPerTc si_per_tc;
f_init_l1ctl();
f_l1_tune(L1CTL);
/* Sample + Validate Scheduling */
si_per_tc := f_l1_sample_si(L1CTL, duration);
f_validate_si_scheduling(si_cfg, si_per_tc);
setverdict(pass);
}
testcase TC_si_sched_default() runs on test_CT {
f_init();
/* 2+3+4 are mandatory and set in f_init() */
f_TC_si_sched();
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_si_sched_1() runs on test_CT {
f_init();
si_cfg.si1_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_1, '5506198fb38000000000000000000000000000e504002b'O);
f_TC_si_sched();
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_si_sched_2bis() runs on test_CT {
f_init();
si_cfg.si2bis_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2bis, '550602bfe809b3ff00000000000000000000007900002b'O);
f_TC_si_sched();
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_si_sched_2ter() runs on test_CT {
f_init();
si_cfg.si2ter_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2ter, '010603bf66b0aa0a00000002000000000000002b2b2b2b'O);
f_TC_si_sched();
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_si_sched_2ter_2bis() runs on test_CT {
f_init();
si_cfg.si2bis_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2bis, '550602bfe809b3ff00000000000000000000007900002b'O);
si_cfg.si2ter_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2ter, '010603bf66b0aa0a00000002000000000000002b2b2b2b'O);
f_TC_si_sched();
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_si_sched_2quater() runs on test_CT {
f_init();
si_cfg.si2quater_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2quater, '050607b10004864982eddb8d555867ee3c95540b2b2b2b'O);
f_TC_si_sched(16.0);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_si_sched_13() runs on test_CT {
/* NOTE: PCUIF connection is not used in this test case, but
* without it the IUT would not broadcast SI13 (see OS#3075). */
f_init_with_pcuif();
si_cfg.si13_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_13, '0106009000185a6fc9e08410ab2b2b2b2b2b2b2b2b2b2b'O);
f_TC_si_sched();
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_si_sched_13_2bis_2ter_2quater() runs on test_CT {
/* NOTE: PCUIF connection is not used in this test case, but
* without it the IUT would not broadcast SI13 (see OS#3075). */
f_init_with_pcuif();
si_cfg.si2bis_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2bis, '550602bfe809b3ff00000000000000000000007900002b'O);
si_cfg.si2ter_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2ter, '010603bf66b0aa0a00000002000000000000002b2b2b2b'O);
si_cfg.si2quater_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2quater, '050607b10004864982eddb8d555867ee3c95540b2b2b2b'O);
si_cfg.si13_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_13, '0106009000185a6fc9e08410ab2b2b2b2b2b2b2b2b2b2b'O);
f_TC_si_sched(16.0);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_bcch_info() runs on test_CT {
f_init();
/* FIXME: enable / disable individual BCCH info */
//ts_RSL_BCCH_INFO(si_type, info);
/* expect no ERROR REPORT after either of them *
/* negative test: ensure ERROR REPORT on unsupported types */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/***********************************************************************
* Low-Level Protocol Errors / ERROR REPORT
***********************************************************************/
private function f_exp_err_rep(template RSL_Cause cause) runs on test_CT {
timer T := 5.0;
T.start;
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_ERROR_REPORT(cause))) {
setverdict(pass);
}
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_ERROR_REPORT(?))) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Wrong cause in RSL ERR REP");
}
[] RSL_CCHAN.receive {
repeat;
}
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for RSL ERR REP");
}
}
}
/* Provoke a protocol error (message too short) and match on ERROR REPORT */
testcase TC_rsl_protocol_error() runs on test_CT {
f_init();
var RSL_Message rsl := valueof(ts_RSL_BCCH_INFO(RSL_SYSTEM_INFO_1, ''O));
rsl.ies := omit;
RSL_CCHAN.send(ts_ASP_RSL_UD(rsl));
f_exp_err_rep(RSL_ERR_PROTO);
}
/* Provoke a mandatory IE error and match on ERROR REPORT */
testcase TC_rsl_mand_ie_error() runs on test_CT {
f_init();
var RSL_Message rsl := valueof(ts_RSL_BCCH_INFO(RSL_SYSTEM_INFO_1, ''O));
rsl.ies := { rsl.ies[0] };
RSL_CCHAN.send(ts_ASP_RSL_UD(rsl));
f_exp_err_rep(RSL_ERR_MAND_IE_ERROR);
}
/* Provoke an IE content error and match on ERROR REPORT */
testcase TC_rsl_ie_content_error() runs on test_CT {
f_init();
var RSL_Message rsl := valueof(ts_RSL_BCCH_INFO(RSL_SYSTEM_INFO_1, ''O));
rsl.ies[1].body.sysinfo_type := RSL_SYSTEM_INFO_5;
RSL_CCHAN.send(ts_ASP_RSL_UD(rsl));
f_exp_err_rep(RSL_ERR_IE_CONTENT);
}
/* attempt to activate channel with wrong RSL Message Discriminator IE */
function f_TC_chan_act_wrong_mdisc(charstring id) runs on ConnHdlr {
var template RSL_Message rsl := ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode);
rsl.msg_disc := ts_RSL_MsgDisc(RSL_MDISC_RESERVED, false);
RSL.send(rsl);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_err_rep_wrong_mdisc() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars := valueof(t_Pars(ts_RslChanNr_SDCCH4(0,0), ts_RSL_ChanMode_SIGN));
f_init();
vc_conn := f_start_handler(refers(f_TC_chan_act_wrong_mdisc), pars);
vc_conn.done;
f_exp_err_rep(RSL_ERR_MSG_DISCR);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Send messages with wrong message type */
private function f_TC_wrong_msg_type_dchan(charstring id) runs on ConnHdlr {
var template (value) RSL_Message rsl_tx;
rsl_tx := ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode);
rsl_tx.msg_type := RSL_MT_NOT_CMD;
RSL.send(rsl_tx);
f_rslem_unregister(0, g_chan_nr);
}
private function f_TC_wrong_msg_type_rll(charstring id) runs on ConnHdlr {
var template (value) RSL_Message rsl_tx;
/* we first have to activate the dedicated channel */
f_rsl_chan_act(g_pars.chan_mode);
/* ... to then send an invalid RLL message */
rsl_tx := ts_RSL_UNITDATA_REQ(g_chan_nr, ts_RslLinkID_DCCH(0), '0102'O);
rsl_tx.msg_type := RSL_MT_CBCH_LOAD_IND;
RSL.send(rsl_tx);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_err_rep_wrong_msg_type() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars := valueof(t_Pars(ts_RslChanNr_SDCCH4(0,0), ts_RSL_ChanMode_SIGN));
var template (value) RSL_Message rsl_tx;
f_init();
/* Common Channel with wrong message type */
RSL_CCHAN.clear;
rsl_tx := valueof(ts_RSL_BCCH_INFO(RSL_SYSTEM_INFO_1, ''O));
rsl_tx.msg_type := RSL_MT_LOCATION_INFO;
RSL_CCHAN.send(ts_ASP_RSL_UD(rsl_tx));
f_exp_err_rep(RSL_ERR_MSG_TYPE);
/* TRX Management */
RSL_CCHAN.clear;
rsl_tx := ts_RSL_SACCH_FILL(RSL_SYSTEM_INFO_5, ''O);
rsl_tx.msg_type := RSL_MT_UNIT_DATA_IND;
RSL_CCHAN.send(ts_ASP_RSL_UD(rsl_tx));
f_exp_err_rep(RSL_ERR_MSG_TYPE);
/* Dedicated Channel */
RSL_CCHAN.clear;
vc_conn := f_start_handler(refers(f_TC_wrong_msg_type_dchan), pars);
vc_conn.done;
f_exp_err_rep(RSL_ERR_MSG_TYPE);
/* RLL */
RSL_CCHAN.clear;
vc_conn := f_start_handler(refers(f_TC_wrong_msg_type_rll), pars);
vc_conn.done;
f_exp_err_rep(RSL_ERR_MSG_TYPE);
}
/* Send messages in wrong sequence (RLL to an inactive lchan) */
private function f_TC_err_rep_wrong_sequence(charstring id) runs on ConnHdlr {
RSL.send(ts_RSL_UNITDATA_REQ(g_chan_nr, ts_RslLinkID_DCCH(0), '0102'O));
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_err_rep_wrong_sequence() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars := valueof(t_Pars(ts_RslChanNr_SDCCH4(0,0), ts_RSL_ChanMode_SIGN));
f_init();
RSL_CCHAN.clear;
vc_conn := f_start_handler(refers(f_TC_err_rep_wrong_sequence), pars);
vc_conn.done;
f_exp_err_rep(RSL_ERR_MSG_SEQ);
}
/***********************************************************************
* IPA CRCX/MDCX/DLCS media stream handling
***********************************************************************/
/* Send IPA DLCX to inactive lchan */
private function f_TC_ipa_dlcx_not_active(charstring id) runs on ConnHdlr {
f_rsl_transceive(ts_RSL_IPA_DLCX(g_chan_nr, 0), tr_RSL_IPA_DLCX_ACK(g_chan_nr, ?, ?),
"IPA DLCX ACK");
}
testcase TC_ipa_dlcx_not_active() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_init();
var ConnHdlr vc_conn := f_start_handler(refers(f_TC_ipa_dlcx_not_active), pars);
vc_conn.done;
}
/* Send IPA CRCX twice to inactive lchan */
private function f_TC_ipa_crcx_twice_not_active(charstring id) runs on ConnHdlr {
f_rsl_transceive(ts_RSL_IPA_CRCX(g_chan_nr), tr_RSL_IPA_CRCX_ACK(g_chan_nr, ?, ?, ?),
"IPA CRCX ACK");
f_rsl_transceive(ts_RSL_IPA_CRCX(g_chan_nr), tr_RSL_IPA_CRCX_NACK(g_chan_nr, RSL_ERR_RES_UNAVAIL),
"IPA CRCX NACK");
}
testcase TC_ipa_crcx_twice_not_active() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_init();
var ConnHdlr vc_conn := f_start_handler(refers(f_TC_ipa_crcx_twice_not_active), pars);
vc_conn.done;
}
/* Regular sequence of CRCX/MDCX/DLCX */
private function f_TC_ipa_crcx_mdcx_dlcx_not_active(charstring id) runs on ConnHdlr {
f_rsl_transceive(ts_RSL_IPA_CRCX(g_chan_nr), tr_RSL_IPA_CRCX_ACK(g_chan_nr, ?, ?, ?),
"IPA CRCX ACK");
var OCT4 remote_ip := f_rnd_octstring(4);
var uint16_t remote_port := f_rnd_int(c_UINT16_MAX);
var uint7_t rtp_pt2 := f_rnd_int(127);
var uint16_t fake_conn_id := 23; /* we're too lazy to read it out from the CRCX ACK above */
f_rsl_transceive(ts_RSL_IPA_MDCX(g_chan_nr, fake_conn_id, remote_ip, remote_port, rtp_pt2),
tr_RSL_IPA_MDCX_ACK(g_chan_nr, ?, ?, ?, rtp_pt2),
"IPA MDCX ACK");
f_rsl_transceive(ts_RSL_IPA_DLCX(g_chan_nr, fake_conn_id), tr_RSL_IPA_DLCX_ACK(g_chan_nr, ?, ?),
"IPA DLCX ACK");
}
testcase TC_ipa_crcx_mdcx_dlcx_not_active() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_init();
var ConnHdlr vc_conn := f_start_handler(refers(f_TC_ipa_crcx_mdcx_dlcx_not_active), pars);
vc_conn.done;
}
/* Sequence of CRCX, 2x MDCX, DLCX */
private function f_TC_ipa_crcx_mdcx_mdcx_dlcx_not_active(charstring id) runs on ConnHdlr {
f_rsl_transceive(ts_RSL_IPA_CRCX(g_chan_nr), tr_RSL_IPA_CRCX_ACK(g_chan_nr, ?, ?, ?),
"IPA CRCX ACK");
var OCT4 remote_ip := f_rnd_octstring(4);
var uint16_t remote_port := f_rnd_int(c_UINT16_MAX);
var uint7_t rtp_pt2 := f_rnd_int(127);
var uint16_t fake_conn_id := 23; /* we're too lazy to read it out from the CRCX ACK above */
f_rsl_transceive(ts_RSL_IPA_MDCX(g_chan_nr, fake_conn_id, remote_ip, remote_port, rtp_pt2),
tr_RSL_IPA_MDCX_ACK(g_chan_nr, ?, ?, ?, rtp_pt2),
"IPA MDCX ACK");
/* Second MDCX */
remote_ip := f_rnd_octstring(4);
remote_port := f_rnd_int(c_UINT16_MAX);
f_rsl_transceive(ts_RSL_IPA_MDCX(g_chan_nr, fake_conn_id, remote_ip, remote_port, rtp_pt2),
tr_RSL_IPA_MDCX_ACK(g_chan_nr, ?, ?, ?, rtp_pt2),
"IPA MDCX ACK");
f_rsl_transceive(ts_RSL_IPA_DLCX(g_chan_nr, fake_conn_id), tr_RSL_IPA_DLCX_ACK(g_chan_nr, ?, ?),
"IPA DLCX ACK");
}
testcase TC_ipa_crcx_mdcx_mdcx_dlcx_not_active() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_init();
var ConnHdlr vc_conn := f_start_handler(refers(f_TC_ipa_crcx_mdcx_mdcx_dlcx_not_active), pars);
vc_conn.done;
}
/* IPA CRCX on SDCCH/4 and SDCCH/8 (doesn't make sense) */
private function f_TC_ipa_crcx_sdcch_not_active(charstring id) runs on ConnHdlr {
f_rsl_transceive(ts_RSL_IPA_CRCX(g_chan_nr), tr_RSL_IPA_CRCX_NACK(g_chan_nr, ?),
"IPA CRCX NACK");
}
testcase TC_ipa_crcx_sdcch_not_active() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_SDCCH4(0,1), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_ipa_crcx_sdcch_not_active), pars);
vc_conn.done;
pars := valueof(t_Pars(t_RslChanNr_SDCCH8(6,5), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_ipa_crcx_sdcch_not_active), pars);
vc_conn.done;
}
/***********************************************************************
* PCU Socket related tests
***********************************************************************/
/* Verify no RTS before ACT_REQ; verify RTS after ACT_REQ */
friend function f_TC_pcu_act_req(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, boolean exp_success)
runs on test_CT {
timer T := 3.0;
/* we don't expect any RTS.req before PDCH are active */
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RTS_REQ(bts_nr))) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "PCU RTS.req before PDCH active?");
}
[] PCU.receive { repeat; }
[] T.timeout { }
}
/* Send PDCH activate request for known PDCH timeslot */
PCU.send(t_SD_PCUIF(g_pcu_conn_id, ts_PCUIF_ACT_REQ(bts_nr, trx_nr, ts_nr)));
/* we now expect RTS.req for this timeslot (only) */
T.start;
alt {
[exp_success] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RTS_REQ(bts_nr, trx_nr, ts_nr))) {
setverdict(pass);
}
[not exp_success] PCU.receive(t_SD_PCUIF(g_pcu_conn_id,
tr_PCUIF_RTS_REQ(bts_nr, trx_nr, ts_nr))) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected RTS.req for supposedly failing activation");
}
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RTS_REQ)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "RTS.req for wrong TRX/TS");
}
[] PCU.receive { repeat; }
[exp_success] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for PCU RTS.req");
}
[not exp_success] T.timeout {
setverdict(pass);
}
}
}
/* verify no more RTS after DEACT_REQ */
friend function f_TC_pcu_deact_req(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
runs on test_CT {
timer T := 3.0;
/* Send PDCH activate request for known PDCH timeslot */
PCU.send(t_SD_PCUIF(g_pcu_conn_id, ts_PCUIF_DEACT_REQ(bts_nr, trx_nr, ts_nr)));
/* wait for some time as there is no PCUIF_DEACT_RESP or the like, so we don't know
* when it will actually have been executed in the BTS */
f_sleep(1.0);
PCU.clear;
/* we now expect no RTS.req for this timeslot */
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RTS_REQ(bts_nr, trx_nr, ts_nr))) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received unexpected PCU RTS.req");
}
[] PCU.receive { repeat; }
[] T.timeout {
setverdict(pass);
}
}
}
friend function f_init_with_pcuif() runs on test_CT {
f_init();
map(self:PCU, system:PCU);
f_init_pcu(PCU, testcasename(), g_pcu_conn_id, g_pcu_last_info);
PCU.send(t_SD_PCUIF(g_pcu_conn_id, ts_PCUIF_TXT_IND(0, PCU_VERSION, testcasename())));
}
/* PDCH activation via PCU socket; check for presence of RTS.req */
testcase TC_pcu_act_req() runs on test_CT {
f_init_with_pcuif();
f_TC_pcu_act_req(0, 0, 7, true);
}
/* PDCH activation via PCU socket on non-PDCU timeslot */
testcase TC_pcu_act_req_wrong_ts() runs on test_CT {
f_init_with_pcuif();
f_TC_pcu_act_req(0, 0, 1, false);
}
/* PDCH activation via PCU socket on wrong BTS */
testcase TC_pcu_act_req_wrong_bts() runs on test_CT {
f_init_with_pcuif();
f_TC_pcu_act_req(23, 0, 7, false);
}
/* PDCH activation via PCU socket on wrong TRX */
testcase TC_pcu_act_req_wrong_trx() runs on test_CT {
f_init_with_pcuif();
f_TC_pcu_act_req(0, 23, 7, false);
}
/* PDCH deactivation via PCU socket; check for absence of RTS.req */
testcase TC_pcu_deact_req() runs on test_CT {
f_init_with_pcuif();
/* Activate PDCH */
f_TC_pcu_act_req(0, 0, 7, true);
f_sleep(1.0);
/* and De-Activate again */
f_TC_pcu_deact_req(0, 0, 7);
}
/* Attempt to deactivate a PDCH on a non-PDCH timeslot */
testcase TC_pcu_deact_req_wrong_ts() runs on test_CT {
f_init_with_pcuif();
f_TC_pcu_deact_req(0, 0, 1);
}
/* Test the PCU->BTS Version and BTS->PCU SI13 handshake */
function f_TC_pcu_ver_siXX(octetstring si, RSL_IE_SysinfoType rsl_si_type) runs on test_CT {
var PCUIF_send_data sd;
timer T:= 3.0;
f_init_with_pcuif();
/* Set SI13 via RSL */
f_rsl_bcch_fill_raw(rsl_si_type, si);
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_DATA_IND(0, 0, 0, ?, PCU_IF_SAPI_BCCH))) -> value sd {
if (substr(sd.data.u.data_ind.data, 0, lengthof(si)) == si) {
setverdict(pass);
} else {
repeat;
}
}
[] PCU.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for SI");
}
}
}
/* Test the PCU->BTS Version and BTS->PCU SI1 handshake */
testcase TC_pcu_ver_si1() runs on test_CT {
const octetstring si1 := '550111132A252B27CC29AA11BB33CC'O;
f_TC_pcu_ver_siXX(si1, RSL_SYSTEM_INFO_1);
}
/* Test the PCU->BTS Version and BTS->PCU SI3 handshake */
testcase TC_pcu_ver_si3() runs on test_CT {
const octetstring si3 := '49012223242526272929AABBCC'O;
f_TC_pcu_ver_siXX(si3, RSL_SYSTEM_INFO_3);
}
/* Test the PCU->BTS Version and BTS->PCU SI13 handshake */
testcase TC_pcu_ver_si13() runs on test_CT {
const octetstring si13 := '01010203040506070909'O;
f_TC_pcu_ver_siXX(si13, RSL_SYSTEM_INFO_13);
}
private const octetstring c_PCU_DATA := '000102030405060708090a0b0c0d0e0f10111213141516'O;
/* helper function to send a PCU DATA.req */
friend function f_pcu_data_req(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
uint8_t block_nr, uint32_t fn, PCUIF_Sapi sapi, octetstring data)
runs on test_CT
{
PCU.send(t_SD_PCUIF(g_pcu_conn_id,
ts_PCUIF_DATA_REQ(bts_nr, trx_nr, ts_nr, block_nr, fn, sapi, data)));
}
/* helper function to wait for RTS.ind for given SAPI on given BTS/TRX/TS and then send */
friend function f_pcu_wait_rts_and_data_req(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
PCUIF_Sapi sapi, octetstring data)
runs on test_CT
{
var PCUIF_send_data sd;
timer T := 3.0;
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id,
tr_PCUIF_RTS_REQ(bts_nr, trx_nr, ts_nr, sapi))) -> value sd {
f_pcu_data_req(bts_nr, trx_nr, ts_nr, sd.data.u.rts_req.block_nr,
sd.data.u.rts_req.fn, sapi, data);
}
[] PCU.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for RTS.ind");
}
}
}
/* Send DATA.req on invalid BTS */
testcase TC_pcu_data_req_wrong_bts() runs on test_CT {
var TfiUsfArr tua := f_TfiUsfArrInit();
var octetstring data := '0000'O & f_rnd_octstring(21);
f_virtphy_common();
f_TC_pcu_act_req(0, 0, 7, true);
f_TfiUsfArrSet(tua, 7, 0);
f_L1CTL_TBF_CFG(L1CTL, false, tua);
f_sleep(1.0);
f_pcu_to_l1(23, 0, 7, PCU_IF_SAPI_PDTCH, data, false, false);
}
/* Send DATA.req on invalid TRX */
testcase TC_pcu_data_req_wrong_trx() runs on test_CT {
var TfiUsfArr tua := f_TfiUsfArrInit();
var octetstring data := '0000'O & f_rnd_octstring(21);
f_virtphy_common();
f_TC_pcu_act_req(0, 0, 7, true);
f_TfiUsfArrSet(tua, 7, 0);
f_L1CTL_TBF_CFG(L1CTL, false, tua);
f_sleep(1.0);
f_pcu_to_l1(0, 100, 7, PCU_IF_SAPI_PDTCH, data, false, false);
}
/* Send DATA.req on invalid timeslot */
testcase TC_pcu_data_req_wrong_ts() runs on test_CT {
var TfiUsfArr tua := f_TfiUsfArrInit();
var octetstring data := '0000'O & f_rnd_octstring(21);
f_virtphy_common();
f_TC_pcu_act_req(0, 0, 7, true);
f_TfiUsfArrSet(tua, 7, 0);
f_L1CTL_TBF_CFG(L1CTL, false, tua);
f_sleep(1.0);
f_pcu_to_l1(0, 0, 70, PCU_IF_SAPI_PDTCH, data, false, false);
}
/* Send DATA.req on timeslot that hasn't been activated */
testcase TC_pcu_data_req_ts_inactive() runs on test_CT {
var TfiUsfArr tua := f_TfiUsfArrInit();
var octetstring data := '0000'O & f_rnd_octstring(21);
f_virtphy_common();
f_TfiUsfArrSet(tua, 7, 0);
f_L1CTL_TBF_CFG(L1CTL, false, tua);
f_sleep(1.0);
f_pcu_to_l1(0, 0, 7, PCU_IF_SAPI_PDTCH, data, false, false);
}
private function f_pcu_to_l1(uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
PCUIF_Sapi sapi, octetstring data, boolean expect_data := true,
boolean wait_rts := true)
runs on test_CT {
timer T := 5.0;
var L1ctlDlMessage rx_dl;
PCU.clear;
if (wait_rts) {
f_pcu_wait_rts_and_data_req(bts_nr, trx_nr, ts_nr, sapi, data);
} else {
f_pcu_data_req(bts_nr, trx_nr, ts_nr, 0, 0, sapi, data);
}
T.start;
alt {
[expect_data] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PDCH(ts_nr), ?, data)) {
/* FIXME: why is fn of DATA_IND different to fn of RTS / DATA_REQ above? */
setverdict(pass);
}
[not expect_data] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PDCH(ts_nr), ?, data)) -> value rx_dl {
setverdict(fail, "Received unexpected ", rx_dl);
}
[] L1CTL.receive {
repeat;
}
[expect_data] T.timeout {
setverdict(fail, "Timeout waiting for ", data);
}
[not expect_data] T.timeout {
setverdict(pass);
}
}
}
private function f_disable_dynamic_ts() runs on test_CT
{
f_init_vty_bsc();
/* I'm not quite sure why we need this with osmo-bts-virtual. Somehow it deosn't seem to
* support dynamic timeslots? But it uses the same scheduler as osmo-bts-trx ?!? */
f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 3"}, "phys_chan_config TCH/F");
f_vty_config2(BSCVTY, {"network", "bts 0", "trx 0", "timeslot 4"}, "phys_chan_config TCH/F");
f_init_with_pcuif();
}
private function f_virtphy_common() runs on test_CT {
f_disable_dynamic_ts();
f_init_l1ctl();
f_l1_tune(L1CTL);
}
testcase TC_pcu_data_req_pdtch() runs on test_CT {
var TfiUsfArr tua := f_TfiUsfArrInit();
var octetstring data := '0000'O & f_rnd_octstring(21);
f_virtphy_common();
f_TC_pcu_act_req(0, 0, 7, true);
f_TfiUsfArrSet(tua, 7, 0);
f_L1CTL_TBF_CFG(L1CTL, false, tua);
f_sleep(1.0);
f_pcu_to_l1(0, 0, 7, PCU_IF_SAPI_PDTCH, data); //c_PCU_DATA);
}
/* FIXME: PTTCH has nothing to do with TBFs */
testcase TC_pcu_data_req_ptcch() runs on test_CT {
var TfiUsfArr tua := f_TfiUsfArrInit();
var octetstring data := '0000'O & f_rnd_octstring(21);
f_virtphy_common();
f_TC_pcu_act_req(0, 0, 7, true);
f_TfiUsfArrSet(tua, 7, 0);
f_L1CTL_TBF_CFG(L1CTL, false, tua);
f_sleep(1.0);
f_pcu_to_l1(0, 0, 7, PCU_IF_SAPI_PTCCH, data);
}
private function f_TC_pcu_ptcch_ul(uint16_t ra)
runs on test_CT {
var template PCUIF_Message pcu_rach_ind;
var PCUIF_send_data sd;
var GsmFrameNumber fn;
timer T;
/* Send an Access Burst on PTCCH/U over the Um-interface */
fn := f_L1CTL_RACH(L1CTL, ra := ra, combined := 0, offset := 0,
chan_nr := ts_RslChanNr_PDCH(7),
link_id := ts_RslLinkID_OSMO_PTCCH(0));
pcu_rach_ind := tr_PCUIF_RACH_IND(bts_nr := 0, trx_nr := 0, ts_nr := 7,
ra := ra, fn := fn, sapi := PCU_IF_SAPI_PTCCH);
/* Expect a RACH.ind on the PCU interface (timeout is one multi-frame) */
T.start(52.0 * 4.615 / 1000.0);
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, pcu_rach_ind)) -> value sd {
log("Rx an Access Burst on the PCU interface: ", sd.data);
setverdict(pass);
T.stop;
}
[] PCU.receive { repeat; }
[] T.timeout {
setverdict(fail, "Timeout waiting for RACH.ind on the PCU interface");
/* Keep going, that's not the end of the world */
}
}
}
testcase TC_pcu_ptcch() runs on test_CT {
var L1ctlDlMessage dl;
var octetstring data;
timer T;
f_init_with_pcuif();
f_init_l1ctl();
f_l1_tune(L1CTL);
/* Activate PDCH channel on TS7 */
f_TC_pcu_act_req(0, 0, 7, true);
/* Tune trxcon to that PDCH channel */
var ConnHdlrPars pars := valueof(t_Pars(ts_RslChanNr_PDCH(7), ts_RSL_ChanMode_SIGN));
if (mp_freq_hop_enabled and mp_transceiver_num > 1)
{ f_resolve_fh_params(pars.fhp, pars.chan_nr.tn); }
f_l1ctl_est_dchan(L1CTL, pars);
/* Verify PTCCH/U: send several access bursts, make sure they're received */
for (var integer i := 0; i < 16; i := i + 1) {
log("Sending an Access Burst towards the L1CTL interface");
f_TC_pcu_ptcch_ul(oct2int(f_rnd_ra_ps()));
}
/* Generate a random payload for PTCCH/D (23 octets, CS-1) */
data := f_rnd_octstring(23);
/* Verify PTCCH/D: send a random data block, make sure it's received */
log("Sending a PTCCH/D block towards the PCU interface: ", data);
f_pcu_wait_rts_and_data_req(0, 0, 7, PCU_IF_SAPI_PTCCH, data);
/* PTCCH/D period is 2 multi-frames (2 * 52 * 4.615 ms), but
* let's give it more time in case if we miss the beginning. */
T.start(2.0 * 2.0 * 52.0 * 4.615 / 1000.0);
alt {
/* PDCH is considered as traffic in trxcon => expect TRAFFIC.ind */
[] L1CTL.receive(tr_L1CTL_TRAFFIC_IND(chan_nr := t_RslChanNr_PDCH(7),
link_id := tr_RslLinkID_OSMO_PTCCH(?),
frame := data)) -> value dl {
log("Rx PTCCH/D data (traffic) block on L1CTL: ", dl);
setverdict(pass);
T.stop;
}
/* Other PHYs (e.g. virt_phy) may consider PDCH as data => expect DATA.ind */
[] L1CTL.receive(tr_L1CTL_DATA_IND(chan_nr := t_RslChanNr_PDCH(7),
link_id := tr_RslLinkID_OSMO_PTCCH(?),
l2_data := data)) -> value dl {
log("Rx PTCCH/D data block on L1CTL: ", dl);
setverdict(pass);
T.stop;
}
[] L1CTL.receive { repeat; }
[] T.timeout {
setverdict(fail, "Timeout waiting for DATA.ind on L1CTL");
}
}
}
/* Send AGCH from PCU; check it appears on Um side */
testcase TC_pcu_data_req_agch() runs on test_CT {
timer T := 3.0;
f_init_with_pcuif();
f_init_l1ctl();
f_l1_tune(L1CTL);
f_TC_pcu_act_req(0, 0, 7, true);
f_pcu_data_req(0, 0, 7, 0, 0, PCU_IF_SAPI_AGCH, c_PCU_DATA);
T.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0), ?, c_PCU_DATA)) {
setverdict(pass);
}
[] L1CTL.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for PCU-originated AGCH block on Um");
}
}
}
/* Send AGCH from PCU; check it appears on Um side */
testcase TC_pcu_data_req_pch() runs on test_CT {
timer T := 3.0;
f_init_with_pcuif();
f_init_l1ctl();
f_l1_tune(L1CTL);
f_TC_pcu_act_req(0, 0, 7, true);
/* three characters prefix: last 3 digits of IMSI as ASCII */
f_pcu_data_req(0, 0, 7, 0, 0, PCU_IF_SAPI_PCH, '313233'O & c_PCU_DATA);
T.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0), ?, c_PCU_DATA)) {
setverdict(pass);
}
[] L1CTL.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for PCU-originated PCH block on Um");
}
}
}
/* Send IMM.ASS from PCU for PCH; check it appears on Um side */
testcase TC_pcu_data_req_imm_ass_pch() runs on test_CT {
var octetstring imm_ass := f_rnd_octstring(23);
f_init_with_pcuif();
f_init_l1ctl();
f_l1_tune(L1CTL);
/* append 3 last imsi digits so BTS can compute pagng group */
var uint32_t fn := f_PCUIF_tx_imm_ass_pch(PCU, g_pcu_conn_id, imm_ass, '123459987'H);
timer T := 0.5;
T.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0), ?, imm_ass)) {
/* TODO: verify paging group */
setverdict(pass);
}
[] L1CTL.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for PCU-originated AGCH block on Um");
}
}
}
/* Send RACH from Um side, expect it to show up on PCU socket */
testcase TC_pcu_rach_content() runs on test_CT {
f_init_with_pcuif();
f_init_l1ctl();
f_l1_tune(L1CTL);
var GsmFrameNumber fn_last := 0;
for (var integer i := 0; i < 1000; i := i+1) {
var OCT1 ra := f_rnd_ra_ps();
var GsmFrameNumber fn := f_L1CTL_RACH(L1CTL, oct2int(ra));
if (fn == fn_last) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Two RACH in same FN?!?");
}
fn_last := fn;
timer T := 2.0;
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RACH_IND(0, 0, 0, oct2int(ra), 0, ?, fn))) {
T.stop;
}
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RACH_IND)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected RACH IND");
}
[] PCU.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for RACH IND");
}
}
}
setverdict(pass);
}
/* Send extended (11-bit, TS1 & TS2) RACH bursts from the Um side,
* expect them to show up on PCU socket (with proper BURST_TYPE_*). */
testcase TC_pcu_ext_rach_content() runs on test_CT {
var template PCUIF_Message pcu_rach_ind;
var GsmFrameNumber fn_last := 0;
var L1ctlRachSynchSeq synch_seq;
var PCUIF_BurstType pcu_bt;
var GsmFrameNumber fn;
var BIT11 ra11;
f_init_with_pcuif();
f_init_l1ctl();
f_l1_tune(L1CTL);
for (var integer i := 0; i < 1000; i := i+1) {
/* We need to test both TS1 & TS2 */
if (i rem 2 == 0) {
synch_seq := RACH_SYNCH_SEQ_TS1;
pcu_bt := BURST_TYPE_1;
} else {
synch_seq := RACH_SYNCH_SEQ_TS2;
pcu_bt := BURST_TYPE_2;
}
ra11 := f_rnd_ra11_ps();
fn := f_L1CTL_EXT_RACH(L1CTL, bit2int(ra11), synch_seq);
if (fn == fn_last) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Two RACH in same FN?!?");
}
fn_last := fn;
/* Compose the expected message */
pcu_rach_ind := tr_PCUIF_RACH_IND(
bts_nr := 0, trx_nr := 0, ts_nr := 0,
ra := bit2int(ra11),
is_11bit := 1,
burst_type := pcu_bt,
fn := fn);
timer T := 2.0;
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, pcu_rach_ind)) {
T.stop;
}
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RACH_IND)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected RACH IND");
}
[] PCU.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for RACH IND");
}
}
}
setverdict(pass);
}
private function f_TC_pcu_data_ind_lqual_cb(int16_t lqual_cb_exp, int16_t thresh)
runs on test_CT {
var template PCUIF_send_data sdt;
var PCUIF_send_data sd;
var int16_t lqual_cb;
timer T := 1.0;
/* PCUIF_DATA.ind is encapsulated into a supplementary record */
sdt := t_SD_PCUIF_MSGT(g_pcu_conn_id, PCU_IF_MSG_DATA_IND);
/* Send a random PDTCH frame over Um */
L1CTL.send(ts_L1CTL_TRAFFIC_REQ(ts_RslChanNr_PDCH(7), ts_RslLinkID_DCCH(0),
'0000'O & f_rnd_octstring(21)));
T.start;
alt {
/* If expected link quality is above the threshold */
[lqual_cb_exp >= thresh] PCU.receive(sdt) -> value sd {
lqual_cb := sd.data.u.data_ind.lqual_cb;
log("Rx PCUIF_DATA.ind (lqual_cb=", lqual_cb, ")");
/* Make sure the actual link quality matches the expected value */
if (not match(lqual_cb, lqual_cb_exp)) {
setverdict(fail, log2str("Link quality ", lqual_cb, " does not match ",
"expected value ", lqual_cb_exp));
} else {
setverdict(pass);
}
}
/* If expected link quality is below the threshold */
[lqual_cb_exp < thresh] PCU.receive(sdt) -> value sd {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Rx unexpected PCUIF_DATA.ind: ", sd.data));
}
/* Ignore PCUIF_RTS.req and PCUIF_TIME.ind */
[] PCU.receive { repeat; }
[lqual_cb_exp < thresh] T.timeout {
log("Rx nothing, as expected");
setverdict(pass);
}
[lqual_cb_exp >= thresh] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Timeout waiting for PCUIF_DATA.ind");
}
}
}
/* Verify C/I (Carrier-to-Interference ratio) processing of PDTCH frames */
testcase TC_pcu_data_ind_lqual_cb() runs on test_CT {
f_init_with_pcuif();
PCU.clear;
f_init_l1ctl();
f_l1_tune(L1CTL);
/* Activate a PDCH channel on TS7 */
f_TC_pcu_act_req(0, 0, 7, true);
/* Tune trxcon to that PDCH channel on TS7 */
var ConnHdlrPars pars := valueof(t_Pars(ts_RslChanNr_PDCH(7), ts_RSL_ChanMode_SIGN));
if (mp_freq_hop_enabled and mp_transceiver_num > 1)
{ f_resolve_fh_params(pars.fhp, pars.chan_nr.tn); }
f_l1ctl_est_dchan(L1CTL, pars);
/* C/I in centiBels, test range: -256 .. +1280, step 128 */
for (var int16_t i := -256; i <= 1280; i := i + 128) {
var TrxcMessage ret;
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, ts_TRXC_FAKE_CI(i));
/* FIXME: OsmoBTS may have different threshold (see MIN_QUAL_NORM) */
f_TC_pcu_data_ind_lqual_cb(i, thresh := 0);
}
}
/* Send PAGING via RSL, expect it to shw up on PCU socket */
testcase TC_pcu_paging_from_rsl() runs on test_CT {
f_init_with_pcuif();
for (var integer i := 0; i < 100; i := i+1) {
var MobileIdentityLV mi_lv;
var octetstring mi_lv_enc;
var MobileIdentityV mi;
timer T := 3.0;
if (i < 50) {
mi := valueof(t_MI_TMSI(f_rnd_octstring(4)));
} else {
mi := valueof(ts_MI_IMSI(f_gen_imsi(i)));
}
/* Fancy encoding for PCUIF */
mi_lv := valueof(ts_MI_LV(mi));
mi_lv_enc := enc_MobileIdentityLV(mi_lv);
mi_lv_enc := f_pad_oct(mi_lv_enc, 9, '00'O);
/* Send RSL PAGING COMMAND */
RSL_CCHAN.send(ts_ASP_RSL_UD(ts_RSL_PAGING_CMD(mi, i mod 4)));
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_PAG_REQ(0, mi_lv_enc))) {
}
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_PAG_REQ)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected PAGING REQ");
}
[] PCU.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for PAGING REQ");
}
}
}
setverdict(pass);
}
/* test for periodic TIME_IND; check number of FN expired and number of TIME_IND within frames */
testcase TC_pcu_time_ind() runs on test_CT {
var PCUIF_send_data pcu_sd;
var integer num_time_ind := 0;
var integer first_fn, last_fn;
var float test_duration := 5.0;
timer T;
f_init_with_pcuif();
f_TC_pcu_act_req(0, 0, 7, true);
PCU.clear;
T.start(test_duration);
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_TIME_IND(0, ?))) -> value pcu_sd {
num_time_ind := num_time_ind + 1;
if (not isbound(first_fn)) {
first_fn := pcu_sd.data.u.time_ind.fn;
}
last_fn := pcu_sd.data.u.time_ind.fn;
repeat;
}
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_TIME_IND(?, ?))) -> value pcu_sd {
setverdict(fail, "Received unexpected PCUIF_TIME_IND: ", pcu_sd.data);
repeat;
}
[] PCU.receive {
repeat;
}
[] T.timeout {}
}
var integer fn_expired := last_fn - first_fn;
log(fn_expired, " fn expired with ", num_time_ind, " PCU_TIME.ind");
/* verify the number of frames expired matches our expectation */
const float c_GSM_FN_DURATION_MS := 4.61538;
var float fn_expected := test_duration * 1000.0 / c_GSM_FN_DURATION_MS;
var template integer t_fn_expected := f_tolerance(float2int(fn_expected), 1, 100000, 20);
if (not match(fn_expired, t_fn_expected)) {
setverdict(fail, "Number of TDMA Frames (", fn_expired, ") not matching ", t_fn_expected);
}
/* There are three TIME.ind in every fn MOD 13 */
var float time_ind_expected := int2float(fn_expired) * 3.0 / 13.0;
/* Add some tolerance */
var template integer t_time_ind_exp := f_tolerance(float2int(time_ind_expected), 1, 100000, 5);
if (not match(num_time_ind, t_time_ind_exp)) {
setverdict(fail, "Number of TIME.ind (", num_time_ind, ") not matching ", t_time_ind_exp);
}
setverdict(pass);
}
/* test for periodic RTS_REQ; check number of FN expired and number of RTS_IND per SAPI */
testcase TC_pcu_rts_req() runs on test_CT {
var PCUIF_send_data pcu_sd;
var integer first_fn, last_fn;
var integer num_rts_pdtch := 0;
var integer num_rts_ptcch := 0;
var float test_duration := 5.0;
timer T;
f_init_with_pcuif();
f_TC_pcu_act_req(0, 0, 7, true);
PCU.clear;
T.start(test_duration);
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RTS_REQ(0, 0, 7, PCU_IF_SAPI_PDTCH, ?, ?)))
-> value pcu_sd {
num_rts_pdtch := num_rts_pdtch + 1;
if (not isbound(first_fn)) {
first_fn := pcu_sd.data.u.rts_req.fn;
}
last_fn := pcu_sd.data.u.rts_req.fn;
repeat;
}
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RTS_REQ(0, 0, 7, PCU_IF_SAPI_PTCCH, ?, ?)))
-> value pcu_sd {
num_rts_ptcch := num_rts_ptcch + 1;
if (not isbound(first_fn)) {
first_fn := pcu_sd.data.u.rts_req.fn;
}
last_fn := pcu_sd.data.u.rts_req.fn;
repeat;
}
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_RTS_REQ)) -> value pcu_sd {
setverdict(fail, "Received unexpected PCUIF_RTS_REQ: ", pcu_sd.data);
repeat;
}
[] PCU.receive {
repeat;
}
[] T.timeout {}
}
var integer fn_expired := last_fn - first_fn;
log(fn_expired, " fn expired with num_rts_pdtch=", num_rts_pdtch,
", num_rts_ptcch=", num_rts_ptcch);
/* verify the number of frames expired matches our expectation */
const float c_GSM_FN_DURATION_MS := 4.61538;
var float fn_expected := test_duration * 1000.0 / c_GSM_FN_DURATION_MS;
var template integer t_fn_expected := f_tolerance(float2int(fn_expected), 1, 100000, 20);
if (not match(fn_expired, t_fn_expected)) {
setverdict(fail, "Number of TDMA Frames (", fn_expired, ") not matching ", t_fn_expected);
}
/* PTCCH is in pos. 12 + 38 of 52-multiframe, but four slots/bursts required per block */
var float ptcch_expected := int2float(fn_expired) / 52.0 * 0.5;
var template integer t_ptcch_exp := f_tolerance(float2int(ptcch_expected), 1, 100000, 1);
if (not match(num_rts_ptcch, t_ptcch_exp)) {
setverdict(fail, "Number of RTS.ind for PTCCH (", num_rts_ptcch, ") not matching ",
t_ptcch_exp);
}
/* We have 12 PDTCH blocks every 52-multiframe */
var float pdtch_expected := int2float(fn_expired) / 52.0 * 12.0;
var template integer t_pdtch_exp := f_tolerance(float2int(pdtch_expected), 1, 100000, 2);
if (not match(num_rts_pdtch, t_pdtch_exp)) {
setverdict(fail, "Number of RTS.ind for PDTCH (", num_rts_pdtch, ") not matching ",
t_pdtch_exp);
}
setverdict(pass);
}
/* test for generating Abis side OML ALERT from the PCU */
testcase TC_pcu_oml_alert() runs on test_CT {
var PCUIF_send_data pcu_sd;
var integer num_time_ind := 0;
var integer first_fn, last_fn;
var float test_duration := 5.0;
timer T;
f_init_with_pcuif();
f_TC_pcu_act_req(0, 0, 7, true);
/* re-connect CTRL port from BTS to BSC */
f_ipa_ctrl_stop();
f_ipa_ctrl_start_client(mp_bsc_ctrl_ip, mp_bsc_ctrl_port);
/* Send that OML Alert */
PCU.send(t_SD_PCUIF(g_pcu_conn_id, ts_PCUIF_TXT_IND(0, PCU_OML_ALERT, testcasename())));
/* This requires https://gerrit.osmocom.org/#/c/osmo-bsc/+/14177 to be merged */
f_ctrl_exp_trap(IPA_CTRL, "bts.0.oml_failure_report", ?);
setverdict(pass);
}
/* test for forwarding of RR SUSPEND from CS lchan to PCU via PCU socket */
private function f_TC_rr_suspend_req(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer pcu_conn_id := -1;
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0));
var RoutingAreaIdentificationV rai := f_RAI('262'H, '42F'H, '2342'O, '55'O);
var OCT4 tlli := '01020304'O;
var OCT1 cause := '23'O;
timer T := 5.0;
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_l1_tune(L1CTL);
RSL.clear;
f_est_dchan();
L1CTL.clear;
f_est_rll_mo(link_id.sapi, link_id, '23420815'O);
var PDU_ML3_MS_NW susp_req := valueof(ts_RRM_GprsSuspReq(tlli, rai, cause));
var octetstring l3 := enc_PDU_ML3_MS_NW(susp_req);
f_tx_lapdm(ts_LAPDm_I(link_id.sapi, cr_MO_CMD, true, 1, 0, l3), link_id);
/* ConnHdlr has terminated after sending the RR SUSP REQ over a dedicaed channel */
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_SUSP_REQ(0, tlli, ?, oct2int(cause)))) {
setverdict(pass);
}
[] PCU.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_SUSP_REQ(?, ?, ?, ?))) {
setverdict(fail, "Received unexpected PCUIF SUSPE REQ: ");
}
[] PCU.receive {
repeat;
}
[] T.timeout {
setverdict(fail, "Timeout waiting for SUSP REQ on PCUIF");
}
}
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_pcu_rr_suspend() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init_with_pcuif();
pars := valueof(t_Pars(t_RslChanNr_SDCCH4(0,3), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_rr_suspend_req), pars, true);
vc_conn.done;
}
/* Ensure that PCUIF socket can accept only a single connection */
testcase TC_pcu_socket_connect_multi() runs on test_CT {
var boolean connected := false;
var UD_connect_result cr;
var integer cid;
timer T := 5.0;
var template UD_Result tr_ok := { result_code := SUCCESS, err := omit };
var template UD_Result tr_err := { result_code := ERROR, err := ? };
/* this (among other things) establishes the first connection to the PCUIF socket */
f_init_with_pcuif();
/* try to establish a second connection */
PCU.send(UD_connect:{mp_pcu_socket, -1});
T.start;
alt {
/* the IUT will first accept() the new connection, and close() it immediately */
[not connected] PCU.receive(UD_connect_result:{ id := ?, result := tr_ok }) -> value cr {
log("BTS has accept()ed connection");
connected := true;
cid := cr.id;
repeat;
}
[connected] PCU.receive(UD_connect_result:{ id := cid, result := tr_err }) {
log("BTS has close()d connection");
setverdict(pass);
}
/* ignore other messages related to the first connection */
[] PCU.receive { repeat; }
[] T.timeout {
setverdict(fail, "Timeout waiting for connection result");
}
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Ensure that PCUIF socket can disconnect + reconnect */
testcase TC_pcu_socket_reconnect() runs on test_CT {
/* this (among other things) establishes the first connection to the PCUIF socket */
f_init_with_pcuif();
f_sleep(1.0);
f_pcuif_close(PCU, g_pcu_conn_id);
g_pcu_conn_id := -1;
f_sleep(1.0);
/* re-connect */
PCU.clear;
f_init_pcu(PCU, testcasename(), g_pcu_conn_id, g_pcu_last_info);
setverdict(pass);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Ensure that GPRS capability is not advertised before PCU socket conncet */
private function f_get_si(L1CTL_PT pt, RrMessageType si_type)
runs on test_CT return SystemInformation {
var L1ctlDlMessage l1_dl;
var SystemInformation si;
var integer rc;
timer T := 5.0;
T.start;
alt {
[] pt.receive(tr_L1CTL_DATA_IND(t_RslChanNr_BCCH(0), ?)) -> value l1_dl {
rc := dec_SystemInformationSafe(l1_dl.payload.data_ind.payload, si);
if (rc != 0 or si.header.message_type != si_type) {
repeat;
}
}
[] pt.receive {
repeat;
}
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Timeout waiting for ", si_type));
}
}
return si;
}
/* Check if GPRS Indicator is present in RR System Information of a given type */
private function f_si_has_gprs_indicator(RrMessageType si_type)
runs on test_CT return boolean {
var SystemInformation si := f_get_si(L1CTL, si_type);
if (si_type == SYSTEM_INFORMATION_TYPE_3) {
return si.payload.si3.rest_octets.gprs_ind.presence == '1'B;
} else if (si_type == SYSTEM_INFORMATION_TYPE_4) {
return si.payload.si4.rest_octets.gprs_ind.presence == '1'B;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unhandled SI type");
return false;
}
/* Make sure that GPRS Indicator is absent when the PCU is not connected */
private function f_TC_pcu_socket_noconnect(RrMessageType si_type)
runs on test_CT {
f_init();
f_init_l1ctl();
f_l1_tune(L1CTL);
f_sleep(2.0);
L1CTL.clear;
if (f_si_has_gprs_indicator(si_type)) {
setverdict(fail, si_type, " indicates GPRS even before PCU socket connected");
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_pcu_socket_noconnect_nosi3gprs() runs on test_CT {
f_TC_pcu_socket_noconnect(SYSTEM_INFORMATION_TYPE_3);
}
testcase TC_pcu_socket_noconnect_nosi4gprs() runs on test_CT {
f_TC_pcu_socket_noconnect(SYSTEM_INFORMATION_TYPE_4);
}
/* Ensure that GPRS capability is advertised after PCU socket connect */
private function f_TC_pcu_socket_connect(RrMessageType si_type)
runs on test_CT {
/* this (among other things) establishes the first connection to the PCUIF socket */
f_init_with_pcuif();
f_init_l1ctl();
f_l1_tune(L1CTL);
f_sleep(2.0);
L1CTL.clear;
if (not f_si_has_gprs_indicator(si_type)) {
setverdict(fail, si_type, " indicates no GPRS despite PCU socket connected");
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_pcu_socket_connect_si3gprs() runs on test_CT {
f_TC_pcu_socket_connect(SYSTEM_INFORMATION_TYPE_3);
}
testcase TC_pcu_socket_connect_si4gprs() runs on test_CT {
f_TC_pcu_socket_connect(SYSTEM_INFORMATION_TYPE_4);
}
/* Ensure that GPRS capability is no longer advertised after PCU socket disconnect */
private function f_TC_pcu_socket_disconnect(RrMessageType si_type)
runs on test_CT {
/* this (among other things) establishes the first connection to the PCUIF socket */
f_init_with_pcuif();
f_init_l1ctl();
f_l1_tune(L1CTL);
f_pcuif_close(PCU, g_pcu_conn_id);
g_pcu_conn_id := -1;
f_sleep(1.0);
/* re-connect */
PCU.clear;
f_init_pcu(PCU, testcasename(), g_pcu_conn_id, g_pcu_last_info);
f_sleep(2.0);
L1CTL.clear;
if (f_si_has_gprs_indicator(si_type)) {
setverdict(fail, si_type, " indicates GPRS after PCU socket disconnected");
} else {
setverdict(pass);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_pcu_socket_disconnect_nosi3gprs() runs on test_CT {
f_TC_pcu_socket_disconnect(SYSTEM_INFORMATION_TYPE_3);
}
testcase TC_pcu_socket_disconnect_nosi4gprs() runs on test_CT {
f_TC_pcu_socket_disconnect(SYSTEM_INFORMATION_TYPE_4);
}
/* Verify that the cell_id of SI3 (TS 04.08 9.1.35) and other values are passed properly to the PCU socket (OS#3854) */
testcase TC_pcu_socket_verify_info_ind() runs on test_CT {
var SystemInformation si3 := valueof(ts_SI3_default);
f_init_with_pcuif();
/* actually give the BTS some time to fully come up and to send a PCU INFO IND with the correct
* information */
timer T := 2.0;
T.start;
alt {
[] as_pcu_info_ind(PCU, g_pcu_conn_id, g_pcu_last_info) { repeat; }
[] T.timeout {}
}
/* Verify cell_id */
var uint16_t cell_id_si3 := si3.payload.si3.cell_id;
var uint16_t cell_id_pcu := g_pcu_last_info.u.info_ind.cell_id;
if (cell_id_si3 != cell_id_pcu) {
setverdict(fail, "Expected cell_id '", cell_id_si3, "' and got '", cell_id_pcu, "'. This either means,",
" that the BTS is sending the wrong cell_id, or that the BTS sent it too early",
" (OS#4179)");
}
/* Verify LAC */
var uint16_t lac_si3 := si3.payload.si3.lai.lac;
var uint16_t lac_pcu := g_pcu_last_info.u.info_ind.lac;
if (lac_si3 != lac_pcu) {
setverdict(fail, "Expected LAC ", lac_si3, " got: ", lac_pcu);
}
setverdict(pass);
}
/* Verify hopping parameters in the INFO.ind message (version >= 10) */
testcase TC_pcu_info_ind_fh_params() runs on test_CT {
var PCUIF_info_ind info_ind;
var FreqHopPars fhp;
f_init_with_pcuif();
info_ind := g_pcu_last_info.u.info_ind;
for (var integer i := 0; i < mp_transceiver_num; i := i + 1) {
for (var integer tn := 0; tn < 8; tn := tn + 1) {
if (info_ind.trx[i].pdch_mask[tn] != '1'B) {
/* Skip inactive timeslots */
continue;
}
if (mp_freq_hop_enabled and mp_transceiver_num > 1)
{ f_resolve_fh_params(fhp, tn, trx_nr := i); }
else
{ fhp.enabled := false; }
var template PCUIF_InfoTrxTs tr_ts;
if (fhp.enabled) {
tr_ts := tr_PCUIF_InfoTrxTsH1(
hsn := fhp.maio_hsn.hsn,
maio := fhp.maio_hsn.maio,
ma := f_pad_bit(fhp.ma_map.ma, 64, '0'B),
ma_bit_len := mp_transceiver_num);
} else {
tr_ts := tr_PCUIF_InfoTrxTsH0;
}
var PCUIF_InfoTrxTs ts := info_ind.trx[i].ts[tn];
log("Checking timeslot #", tn, " of trx#", i, ": ", ts);
if (not match(ts, tr_ts)) {
setverdict(fail, "Hopping parameters do not match: ",
"received ", ts, " vs expected ", tr_ts);
} else {
setverdict(pass);
}
}
}
}
/* Verify IPv4 NSVC address in the INFO.ind message */
testcase TC_pcu_socket_nsvc_ipv4() runs on test_CT {
f_init_vty_bsc();
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 0 remote ip 127.127.127.127");
f_vty_transceive(BSCVTY, "drop bts connection 0 oml");
f_init_with_pcuif();
var PCUIF_RemoteAddr remote_addr := g_pcu_last_info.u.info_ind.remote_addr;
var template PCUIF_RemoteAddr tr_remote_addr := {
addr_type := { PCUIF_ADDR_TYPE_IPV4, ? },
addr := { f_pad_oct(f_inet_addr("127.127.127.127"), 16, '00'O), ? }
};
if (not match(remote_addr, tr_remote_addr)) {
setverdict(fail, "NSVC address ", remote_addr, " does not match ", tr_remote_addr);
}
setverdict(pass);
}
/* Verify IPv4 NSVC address in the INFO.ind message */
testcase TC_pcu_socket_nsvc_ipv6() runs on test_CT {
f_init_vty_bsc();
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 0 remote ip fd00::ca:ff:ee");
f_init_with_pcuif();
var PCUIF_RemoteAddr remote_addr := g_pcu_last_info.u.info_ind.remote_addr;
var template PCUIF_RemoteAddr tr_remote_addr := {
addr_type := { PCUIF_ADDR_TYPE_IPV6, ? },
addr := { f_inet6_addr("fd00::ca:ff:ee"), ? }
};
if (not match(remote_addr, tr_remote_addr)) {
setverdict(fail, "NSVC address ", remote_addr, " does not match ", tr_remote_addr);
}
setverdict(pass);
}
/* Verify coding of two NSVCs in the INFO.ind message */
testcase TC_pcu_socket_two_nsvc() runs on test_CT {
f_init_vty_bsc();
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 0 nsvci 1234");
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 0 local udp port 1234");
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 0 remote ip 127.127.127.127");
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 0 remote udp port 1234");
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 1 nsvci 5678");
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 1 local udp port 5678");
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 1 remote ip fd00::ca:ff:ee");
f_vty_config2(BSCVTY, {"network", "bts 0"} , "gprs nsvc 1 remote udp port 5678");
f_init_with_pcuif();
var PCUIF_info_ind info_ind := g_pcu_last_info.u.info_ind;
var PCUIF_RemoteAddr remote_addr := info_ind.remote_addr;
var template PCUIF_RemoteAddr tr_remote_addr := {
addr_type := { PCUIF_ADDR_TYPE_IPV4, PCUIF_ADDR_TYPE_IPV6 },
addr := {
f_pad_oct(f_inet_addr("127.127.127.127"), 16, '00'O),
f_inet6_addr("fd00::ca:ff:ee")
}
};
if (not match(info_ind.nsvci, { 1234, 5678 }))
{ setverdict(fail, "NSVCI ", info_ind.nsvci, " does not match { 1234, 5678 }"); }
if (not match(remote_addr, tr_remote_addr))
{ setverdict(fail, "NSVC address ", remote_addr, " does not match ", tr_remote_addr); }
if (not match(info_ind.local_port, { 1234, 5678 }))
{ setverdict(fail, "NSVC lport ", info_ind.local_port, " does not match { 1234, 5678 }"); }
if (not match(info_ind.remote_port, { 1234, 5678 }))
{ setverdict(fail, "NSVC rport ", info_ind.remote_port, " does not match { 1234, 5678 }"); }
setverdict(pass);
}
/* Verify periodic interference reports on PDCH */
testcase TC_pcu_interf_ind() runs on test_CT {
var template PCUIF_Message tr_interf_ind;
var template PCUIF_interf ts_interf;
var integer interf_ind_num := 0;
var boolean first := true;
var PCUIF_send_data sd;
timer T;
/* Set the averaging/reporting period to 480ms */
f_init_vty_bsc();
f_vty_cfg_bts(BSCVTY, 0, { "interference-meas avg-period 1" });
f_vty_transceive(BSCVTY, "drop bts connection 0 oml");
f_init_with_pcuif();
f_TC_pcu_act_req(0, 0, 7, true);
/* We need trxcon for NOPE indications */
f_init_l1ctl();
f_l1_tune(L1CTL);
/* Expect -120 .. -90 dBm on TS7 of BTS0/TRX0 */
ts_interf := { 0, 0, 0, 0, 0, 0, 0, (90..120) };
tr_interf_ind := tr_PCUIF_INTERF_IND(
bts_nr := 0,
trx_nr := 0,
fn := ?,
interf := ts_interf
);
T.start(0.480 * 1.5);
alt {
/* The first interference report may contain unreliable values, so we ignore it */
[first] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_INTERF_IND(0, 0))) {
/* 4 SACCH periods => 4 reports (plus some guard time) */
T.start(0.480 * 4.0 + 0.480 / 2.0);
first := false;
repeat;
}
/* Subsequent interference reports shall match our expectations */
[not first] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_interf_ind)) -> value sd {
/* Check TDMA frame number period */
if (sd.data.u.interf_ind.fn mod 104 != 0) {
setverdict(fail, "Odd TDMA frame number := ",
sd.data.u.interf_ind.fn);
}
interf_ind_num := interf_ind_num + 1;
if (interf_ind_num < 4)
{ repeat; }
}
[not first] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_INTERF_IND(0, 0))) -> value sd {
setverdict(fail, "Received unexpected interference report: ", sd.data);
}
[] PCU.receive { repeat; }
[] T.timeout {
setverdict(fail, "Timeout waiting for interference reports");
}
}
/* Reset the averaging/reporting period back to default */
f_vty_cfg_bts(BSCVTY, 0, { "interference-meas avg-period 6" });
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/***********************************************************************
* Osmocom Style Dynamic Timeslot Support
***********************************************************************/
private altstep as_pcuif_check_pdch_mask(integer pcu_conn_id, BIT1 exp,
integer bts_nr, integer trx_nr)
runs on ConnHdlr {
var PCUIF_send_data sd;
[] PCU.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_INFO_IND(bts_nr, ?))) -> value sd {
var BIT8 pdch_mask := sd.data.u.info_ind.trx[trx_nr].pdch_mask;
if (substr(pdch_mask, g_chan_nr.tn, 1) != exp) {
repeat;
}
}
}
private function f_dyn_osmo_pdch_act(integer pcu_conn_id, integer bts_nr, integer trx_nr)
runs on ConnHdlr {
/* Expect BTS to immediately acknowledge activation as PDCH */
PCU.clear;
f_rsl_chan_act(g_pars.chan_mode);
/* expect INFO_IND on PCU interface listing TS as PDCH */
timer T_wait := 2.0;
T_wait.start;
alt {
[] as_pcuif_check_pdch_mask(pcu_conn_id, '1'B, bts_nr, trx_nr);
[] PCU.receive { repeat; }
[] T_wait.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Timeout waiting for PCUIF_INFO_IND PDCH_MASK to be '1' on TS", g_chan_nr.tn));
}
}
/* try to activate this PDCH from the PCU point of view */
PCU.send(t_SD_PCUIF(pcu_conn_id, ts_PCUIF_ACT_REQ(bts_nr, trx_nr, g_chan_nr.tn)));
/* FIXME: is there a response? */
}
private function f_dyn_osmo_pdch_deact(integer pcu_conn_id, integer bts_nr, integer trx_nr)
runs on ConnHdlr {
/* Send RSL CHAN REL (deactivate) */
PCU.clear;
RSL.send(ts_RSL_RF_CHAN_REL(g_chan_nr));
/* expect BTS to ask PCU to deactivate the channel */
timer T_wait := 2.0;
T_wait.start;
alt {
[] as_pcuif_check_pdch_mask(pcu_conn_id, '0'B, bts_nr, trx_nr);
[] PCU.receive { repeat; }
[] T_wait.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Timeout waiting for PCUIF_INFO_IND PDCH_MASK to be '0' on TS", g_chan_nr.tn));
}
}
/* Emulate PCU asking BTS to deactivate PDCH */
PCU.send(t_SD_PCUIF(pcu_conn_id, ts_PCUIF_DEACT_REQ(bts_nr, trx_nr, g_chan_nr.tn)));
alt {
[] RSL.receive(tr_RSL_RF_CHAN_REL_ACK(g_chan_nr)) {
setverdict(pass);
}
[] RSL.receive { repeat; }
}
}
/* Activate Osmocom-style dynamic PDCH from BSC side */
private function f_TC_dyn_osmo_pdch_act_deact(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_dyn_osmo_pdch_act(pcu_conn_id, bts_nr, trx_nr);
f_sleep(3.0);
f_dyn_osmo_pdch_deact(pcu_conn_id, bts_nr, trx_nr);
setverdict(pass);
}
testcase TC_dyn_osmo_pdch_act_deact() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_PDCH(4), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_osmo_pdch_act_deact), pars, true);
vc_conn.done;
}
/* send a RF CHAN REL for PDCH on an osmocom dynamci PDCH that's already inactive */
private function f_TC_dyn_osmo_pdch_unsol_deact(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer pcu_conn_id := -1;
f_init_pcu(PCU, id, pcu_conn_id, first_info);
RSL.send(ts_RSL_RF_CHAN_REL(g_chan_nr));
/* since the lchan is already released, we don't expect any PCU changes, just a rel ack. */
RSL.receive(tr_RSL_RF_CHAN_REL_ACK(g_chan_nr));
setverdict(pass);
}
testcase TC_dyn_osmo_pdch_unsol_deact() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_PDCH(4), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_osmo_pdch_unsol_deact), pars, true);
vc_conn.done;
}
/* try to RSL CHAN ACT a PDCH on an osmocom-style PDCH that's already active */
private function f_TC_dyn_osmo_pdch_double_act(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_dyn_osmo_pdch_act(pcu_conn_id, bts_nr, trx_nr);
/* Send a second Chan Activ and expect it to be NACKed */
f_rsl_transceive(ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode), tr_RSL_CHAN_ACT_NACK(g_chan_nr),
"RSL CHAN ACT NACK");
setverdict(pass);
}
testcase TC_dyn_osmo_pdch_double_act() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_PDCH(4), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_osmo_pdch_double_act), pars, true);
vc_conn.done;
}
/* try to RSL CHAN ACT a TCH/F on an osmocom-style PDCH */
private function f_TC_dyn_osmo_pdch_tchf_act(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
var RslChannelNr chan_nr := valueof(t_RslChanNr_Bm(g_chan_nr.tn));
/* register for the TCH/F channel number */
f_rslem_register(0, chan_nr);
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_rsl_transceive(ts_RSL_CHAN_ACT(chan_nr, g_pars.chan_mode), tr_RSL_CHAN_ACT_ACK(chan_nr),
"RSL CHAN ACT");
setverdict(pass);
}
testcase TC_dyn_osmo_pdch_tchf_act() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_PDCH(4), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_osmo_pdch_tchf_act), pars, true);
vc_conn.done;
}
/* try to RSL CHAN ACT the TCH/H on an osmocom-style PDCH */
private function f_TC_dyn_osmo_pdch_tchh_act(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
var RslChannelNr chan_nr[2] := { valueof(t_RslChanNr_Lm(g_chan_nr.tn, 0)),
valueof(t_RslChanNr_Lm(g_chan_nr.tn, 1)) };
/* register for the TCH/H channel numbers */
f_rslem_register(0, chan_nr[0]);
f_rslem_register(0, chan_nr[1]);
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_rsl_transceive(ts_RSL_CHAN_ACT(chan_nr[1], g_pars.chan_mode),
tr_RSL_CHAN_ACT_ACK(chan_nr[1]), "RSL CHAN ACT [1]");
f_rsl_transceive(ts_RSL_CHAN_ACT(chan_nr[0], g_pars.chan_mode),
tr_RSL_CHAN_ACT_ACK(chan_nr[0]), "RSL CHAN ACT [0]");
setverdict(pass);
}
testcase TC_dyn_osmo_pdch_tchh_act() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_PDCH(4), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_osmo_pdch_tchh_act), pars, true);
vc_conn.done;
}
/* try to RSL CHAN ACT the SDCCH8 on an osmocom-style PDCH */
private function f_TC_dyn_osmo_pdch_sdcch8_act(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
var RslChannelNr chan_nr[8] := { valueof(t_RslChanNr_SDCCH8(g_chan_nr.tn, 0)),
valueof(t_RslChanNr_SDCCH8(g_chan_nr.tn, 1)),
valueof(t_RslChanNr_SDCCH8(g_chan_nr.tn, 2)),
valueof(t_RslChanNr_SDCCH8(g_chan_nr.tn, 3)),
valueof(t_RslChanNr_SDCCH8(g_chan_nr.tn, 4)),
valueof(t_RslChanNr_SDCCH8(g_chan_nr.tn, 5)),
valueof(t_RslChanNr_SDCCH8(g_chan_nr.tn, 6)),
valueof(t_RslChanNr_SDCCH8(g_chan_nr.tn, 7)) };
/* register for the TCH/H channel numbers */
for (var integer i := 0; i < lengthof(chan_nr); i := i + 1) {
f_rslem_register(0, chan_nr[i]);
}
f_init_pcu(PCU, id, pcu_conn_id, first_info);
for (var integer i := 0; i < lengthof(chan_nr); i := i + 1) {
f_rsl_transceive(ts_RSL_CHAN_ACT(chan_nr[i], g_pars.chan_mode),
tr_RSL_CHAN_ACT_ACK(chan_nr[i]), "RSL CHAN ACT [" & int2str(i) & "]");
}
setverdict(pass);
}
testcase TC_dyn_osmo_pdch_sdcch8_act() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_PDCH(4), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_osmo_pdch_sdcch8_act), pars, true);
vc_conn.done;
}
/* Reproduce a race condition described in OS#5245 */
private function f_rsl_chan_act_deact(charstring id) runs on ConnHdlr {
f_rsl_chan_act(g_pars.chan_mode);
f_sleep(1.0);
f_rsl_chan_deact();
setverdict(pass);
}
testcase TC_dyn_osmo_pdch_tchh_race_act() runs on test_CT {
var ConnHdlrPars pars[2];
var ConnHdlr vc_conn[2];
var TrxcMessage rsp;
f_init();
/* Configure an artificial delay of 200 ms for TRXC RSP messages */
rsp := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id,
ts_TRXC_FAKE_TRXC_DELAY(200));
/* Activate all sub-channels of TCH/H on TS4 immediately in hope that the second
* CHANnel ACTIVation message will be handled before the PHY responds to 'SETSLOT' */
for (var integer i := 0; i < sizeof(pars); i := i + 1) {
/* TS4 is an Osmocom style dynamic timeslot, we want it to be TCH/H */
pars[i] := valueof(t_Pars(t_RslChanNr_Lm(4, i), ts_RSL_ChanMode_SIGN));
vc_conn[i] := f_start_handler(refers(f_rsl_chan_act_deact),
pars[i], l1ctl := false);
}
/* Wait for all components to finish */
for (var integer i := 0; i < sizeof(pars); i := i + 1) {
vc_conn[i].done;
}
/* Disable the artificial delay for TRXC RSP messages */
rsp := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id,
ts_TRXC_FAKE_TRXC_DELAY(0));
}
testcase TC_dyn_osmo_pdch_sdcch8_race_act() runs on test_CT {
var ConnHdlrPars pars[8];
var ConnHdlr vc_conn[8];
var TrxcMessage rsp;
f_init();
/* Configure an artificial delay of 200 ms for TRXC RSP messages */
rsp := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id,
ts_TRXC_FAKE_TRXC_DELAY(200));
/* Activate all sub-channels of SDCCH/8 on TS4 immediately in hope that subsequent
* CHANnel ACTIVation messages will be handled before the PHY responds to 'SETSLOT' */
for (var integer i := 0; i < sizeof(pars); i := i + 1) {
/* TS4 is an Osmocom style dynamic timeslot, we want it to be SDCCH/8 */
pars[i] := valueof(t_Pars(t_RslChanNr_SDCCH8(4, i), ts_RSL_ChanMode_SIGN));
vc_conn[i] := f_start_handler(refers(f_rsl_chan_act_deact),
pars[i], l1ctl := false);
}
/* Wait for all components to finish */
for (var integer i := 0; i < sizeof(pars); i := i + 1) {
vc_conn[i].done;
}
/* Disable the artificial delay for TRXC RSP messages */
rsp := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id,
ts_TRXC_FAKE_TRXC_DELAY(0));
}
/***********************************************************************
* IPA Style Dynamic Timeslot Support
***********************************************************************/
private function f_dyn_ipa_pdch_act(integer pcu_conn_id, integer bts_nr, integer trx_nr)
runs on ConnHdlr {
/* Expect BTS to immediately acknowledge activation as PDCH */
PCU.clear;
RSL.send(ts_RSL_IPA_PDCH_ACT(g_chan_nr));
/* expect INFO_IND on PCU interface listing TS as PDCH */
timer T_wait := 2.0;
T_wait.start;
alt {
[] as_pcuif_check_pdch_mask(pcu_conn_id, '1'B, bts_nr, trx_nr);
[] PCU.receive { repeat; }
[] T_wait.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Timeout waiting for PCUIF_INFO_IND PDCH_MASK to be '1' on TS", g_chan_nr.tn));
}
}
/* try to activate this PDCH from the PCU point of view */
PCU.send(t_SD_PCUIF(pcu_conn_id, ts_PCUIF_ACT_REQ(bts_nr, trx_nr, g_chan_nr.tn)));
/* FIXME: is there a response? */
RSL.receive(tr_RSL_IPA_PDCH_ACT_ACK(g_chan_nr, ?));
}
private function f_dyn_ipa_pdch_deact(integer pcu_conn_id, integer bts_nr, integer trx_nr)
runs on ConnHdlr {
/* Send RSL CHAN REL (deactivate) */
RSL.send(ts_RSL_IPA_PDCH_DEACT(g_chan_nr));
PCU.clear;
/* expect BTS to ask PCU to deactivate the channel */
timer T_wait := 2.0;
T_wait.start;
alt {
[] as_pcuif_check_pdch_mask(pcu_conn_id, '0'B, bts_nr, trx_nr);
[] PCU.receive { repeat; }
[] T_wait.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Timeout waiting for PCUIF_INFO_IND PDCH_MASK to be '0' on TS", g_chan_nr.tn));
}
}
/* Emulate PCU asking BTS to deactivate PDCH */
PCU.send(t_SD_PCUIF(pcu_conn_id, ts_PCUIF_DEACT_REQ(bts_nr, trx_nr, g_chan_nr.tn)));
alt {
[] RSL.receive(tr_RSL_IPA_PDCH_DEACT_ACK(g_chan_nr)) {
setverdict(pass);
}
[] RSL.receive { repeat; }
}
}
/* Activate and de-activate an IPA-style dynamic TCH/F + PDCH */
private function f_TC_dyn_ipa_pdch_act_deact(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_dyn_ipa_pdch_act(pcu_conn_id, bts_nr, trx_nr);
f_sleep(3.0);
f_dyn_ipa_pdch_deact(pcu_conn_id, bts_nr, trx_nr);
setverdict(pass);
}
testcase TC_dyn_ipa_pdch_act_deact() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(3), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_ipa_pdch_act_deact), pars, true);
vc_conn.done;
}
/* try to RSL CHAN ACT a TCH/F on an IPA-style PDCH */
private function f_TC_dyn_ipa_pdch_tchf_act(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_rsl_transceive(ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode), tr_RSL_CHAN_ACT_ACK(g_chan_nr),
"RSL CHAN ACT");
f_rsl_transceive(ts_RSL_RF_CHAN_REL(g_chan_nr), tr_RSL_RF_CHAN_REL_ACK(g_chan_nr),
"RF CHAN REL", true);
setverdict(pass);
}
testcase TC_dyn_ipa_pdch_tchf_act() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(3), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_ipa_pdch_tchf_act), pars, true);
vc_conn.done;
}
/* Activate IPA style dyn PDCH as TCH/F and then illegally try to activate it as PDCH, too */
private function f_TC_dyn_ipa_pdch_tchf_act_pdch_act_nack(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_rsl_transceive(ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode), tr_RSL_CHAN_ACT_ACK(g_chan_nr),
"RSL CHAN ACT");
RSL.send(ts_RSL_IPA_PDCH_ACT(g_chan_nr));
alt {
[] RSL.receive(tr_RSL_IPA_PDCH_ACT_NACK(g_chan_nr, ?));
[] RSL.receive(tr_RSL_IPA_PDCH_ACT_ACK(g_chan_nr, ?)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected PDCH ACT ACK");
}
[] RSL.receive { repeat; }
}
f_rsl_transceive(ts_RSL_RF_CHAN_REL(g_chan_nr), tr_RSL_RF_CHAN_REL_ACK(g_chan_nr),
"RF CHAN REL", true);
setverdict(pass);
}
testcase TC_dyn_ipa_pdch_tchf_act_pdch_act_nack() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(3), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_ipa_pdch_tchf_act_pdch_act_nack), pars, true);
vc_conn.done;
}
/* try to RSL CHAN ACT a TCH/F on an IPA-style PDCH that's already in PDCH mode; expect NACK */
private function f_TC_dyn_ipa_pdch_act_tchf_act_nack(charstring id) runs on ConnHdlr {
var PCUIF_Message first_info;
var integer ts_nr := g_chan_nr.tn;
var integer trx_nr := 0;
var integer bts_nr := 0;
var integer pcu_conn_id := -1;
/* register for the TCH/F channel number */
f_rslem_register(0, g_chan_nr);
f_init_pcu(PCU, id, pcu_conn_id, first_info);
f_dyn_ipa_pdch_act(pcu_conn_id, bts_nr, trx_nr);
f_rsl_transceive(ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode), tr_RSL_CHAN_ACT_NACK(g_chan_nr),
"RSL CHAN ACT");
f_dyn_ipa_pdch_deact(pcu_conn_id, bts_nr, trx_nr);
setverdict(pass);
}
testcase TC_dyn_ipa_pdch_act_tchf_act_nack() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
pars := valueof(t_Pars(t_RslChanNr_Bm(3), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_dyn_ipa_pdch_act_tchf_act_nack), pars, true);
vc_conn.done;
}
/***********************************************************************
* LAPDm / RLL related
***********************************************************************/
private function f_tx_lapdm(template (value) LapdmFrame l,
template (value) RslLinkId link_id) runs on ConnHdlr {
var octetstring l2 := enc_LapdmFrame(valueof(l));
var template (value) SacchL1Header l1h;
/* TODO: we can use an extension of TTCN-3 for padding, i.e. PADDING('2B'O) */
if (valueof(link_id.c) == SACCH) {
/* Compose dummy L1 header */
l1h := ts_SacchL1Header(g_pars.l1_pars.ms_power_level, false, g_pars.l1_pars.ms_actual_ta);
L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(g_chan_nr, link_id, l1h, f_pad_oct(l2, 21, '2B'O)));
} else {
/* If required, pad L2 frame with constant 0x2b filling */
L1CTL.send(ts_L1CTL_DATA_REQ(g_chan_nr, link_id, f_pad_oct(l2, 23, '2B'O)));
}
}
friend type record RllTestCase {
uint3_t sapi,
RslLinkId link_id,
octetstring l3,
boolean exp
}
friend type record of RllTestCase RllTestCases;
friend template RllTestCase t_EITC(template (present) uint3_t sapi,
template (present) RslLinkId id,
octetstring l3, boolean exp) := {
sapi := sapi,
link_id := id,
l3 := l3,
exp := exp
}
/* execute the same callback function with a set of different parameters (tcs) on a
* variety of logical channels */
private function f_rll_testmatrix(RllTestCases tcs, void_fn fn) runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
/* test on each of the channels we have */
for (var integer i := 0; i < sizeof(g_AllChanTypes); i := i+1) {
pars := valueof(t_Pars(g_AllChanTypes[i], ts_RSL_ChanMode_SIGN));
/* test each of the test cases on the current channel */
for (var integer j := 0; j < sizeof(tcs); j := j+1) {
pars.spec.rll := tcs[j];
log(testcasename(), ": XXX Starting ", tcs[j] , " on ", g_AllChanTypes[i]);
vc_conn := f_start_handler(fn, pars);
vc_conn.done;
}
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* test if SABM on Um triggers EST IND (TS 48.058 3.1) */
private function f_TC_rll_est_ind(charstring id) runs on ConnHdlr {
var RllTestCase tc := g_pars.spec.rll;
timer T := 3.0;
f_l1_tune(L1CTL);
RSL.clear;
/* activate the logical channel */
f_est_dchan();
L1CTL.clear;
f_tx_lapdm(ts_LAPDm_SABM(tc.sapi, cr_MO_CMD, true, tc.l3), tc.link_id);
T.start;
alt {
[tc.l3 != ''O] RSL.receive(tr_RSL_EST_IND(g_chan_nr, tc.link_id, tc.l3)) {
if (tc.exp) {
setverdict(pass);
} else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected EST IND with L3 in ", tc));
}
}
[tc.l3 == ''O] RSL.receive(tr_RSL_EST_IND_NOL3(g_chan_nr, tc.link_id)) {
if (tc.exp) {
setverdict(pass);
} else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected EST IND without L3 in ", tc));
}
}
/* We also expect to receive the measurements */
[] as_meas_res(verify_meas := false);
[tc.exp] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for EST IND");
}
[not tc.exp] T.timeout {
setverdict(pass);
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_rll_est_ind() runs on test_CT {
var RllTestCases tcs := {
/* SAPI0 establishment (contention resolution) */
valueof(t_EITC(0, ts_RslLinkID_DCCH(0), '01020304'O, true)),
/* normal SAPI0 establishment */
valueof(t_EITC(0, ts_RslLinkID_DCCH(0), ''O, true)),
/* SAPI 3 doesn't support contention resolution */
valueof(t_EITC(3, ts_RslLinkID_DCCH(3), '01020304'O, false)),
valueof(t_EITC(3, ts_RslLinkID_SACCH(3), '01020304'O, false)),
/* normal SAPI3 establishment on main DCCH */
valueof(t_EITC(3, ts_RslLinkID_DCCH(3), ''O, true)),
/* normal SAPI3 establishment on SACCH */
valueof(t_EITC(3, ts_RslLinkID_SACCH(3), ''O, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_est_ind));
}
/* test if RLL EST REQ trigeres SABM on Um; UA on Um triggers EST CONF (TS 48.058 3.2) */
private function f_TC_rll_est_req(charstring id) runs on ConnHdlr {
var RllTestCase tc := g_pars.spec.rll;
var L1ctlDlMessage dl;
timer T := 3.0;
f_l1_tune(L1CTL);
RSL.clear;
/* activate the logical channel */
f_est_dchan();
L1CTL.clear;
/* Send a RSL EST REQ for SAPI3 on main DCCH */
RSL.send(ts_RSL_EST_REQ(g_chan_nr, tc.link_id));
T.start;
alt {
[] as_l1_exp_lapdm(tr_LAPDm_SABM(tc.sapi, cr_MT_CMD, true, ''O));
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for SABM");
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_rll_est_req_DCCH_3() runs on test_CT {
var RllTestCases tcs := {
/* normal SAPI3 establishment on main DCCH */
valueof(t_EITC(3, ts_RslLinkID_DCCH(3), ''O, true))//,
};
f_rll_testmatrix(tcs, refers(f_TC_rll_est_req));
}
testcase TC_rll_est_req_ACCH_3() runs on test_CT {
var RllTestCases tcs := {
/* normal SAPI3 establishment on SACCH */
valueof(t_EITC(3, ts_RslLinkID_SACCH(3), ''O, true))
}
f_rll_testmatrix(tcs, refers(f_TC_rll_est_req));
}
/* altstep to receive a LAPDm frame matching the given template */
friend altstep as_l1_exp_lapdm(template LapdmFrame exp) runs on ConnHdlr {
var L1ctlDlMessage dl;
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, ?)) -> value dl {
var LapdmFrame lapdm;
var octetstring l2 := dl.payload.data_ind.payload;
if (dl.dl_info.link_id.c == SACCH) {
/* remove L1 header */
l2 := substr(l2, 2, lengthof(l2)-2);
}
if (ischosen(exp.ab)) {
lapdm.ab := dec_LapdmFrameAB(l2);
} else if (ischosen(exp.b4)) {
lapdm.b4 := dec_LapdmFrameB4(l2);
} else if (ischosen(exp.bbis)) {
lapdm.bbis := dec_LapdmFrameBbis(l2);
}
log("Rx LAPDm ", lapdm);
if (match(lapdm, exp)) {
setverdict(pass);
} else {
repeat;
}
}
[] L1CTL.receive { repeat; }
}
private function f_l1_exp_lapdm(template LapdmFrame exp, float t := 3.0) runs on ConnHdlr {
timer T := t;
T.start;
alt {
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Timeout waiting for LAPDm ", exp));
}
[] as_l1_exp_lapdm(exp);
}
}
/* establish one Radio Link Layer via SABM -> UA. Use l3 for contention resolution */
private function f_est_rll_mo(uint3_t sapi, RslLinkId link_id, octetstring l3) runs on ConnHdlr {
/* send SABM from MS -> BTS */
f_tx_lapdm(ts_LAPDm_SABM(sapi, cr_MO_CMD, true, l3), link_id);
/* expect RLL EST IND on Abis */
alt {
[l3 != ''O] RSL.receive(tr_RSL_EST_IND(g_chan_nr, link_id, l3));
[l3 == ''O] RSL.receive(tr_RSL_EST_IND_NOL3(g_chan_nr, link_id));
[] RSL.receive(tr_RSL_ERROR_IND(g_chan_nr, link_id, ?)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Failing due to RSL_ERROR_IND");
}
[] RSL.receive { repeat; }
}
/* expect UA from BTS -> MS */
f_l1_exp_lapdm(tr_LAPDm_UA(sapi, cr_MT_RSP, true, l3));
}
/* test if DISC on Um triggers RLL REL IND (TS 48.058 3.3) */
private function f_TC_rll_rel_ind(charstring id) runs on ConnHdlr {
var RllTestCase tc := g_pars.spec.rll;
f_l1_tune(L1CTL);
RSL.clear;
/* activate the logical channel */
f_est_dchan();
L1CTL.clear;
/* first establish the link-layer */
f_est_rll_mo(tc.sapi, tc.link_id, tc.l3);
/* then send the DISC */
f_tx_lapdm(ts_LAPDm_DISC(tc.sapi, cr_MO_CMD, true), tc.link_id);
/* ... and expect the REL IND on the RSL side */
alt {
[] RSL.receive(tr_RSL_REL_IND(g_chan_nr, tc.link_id)) {
setverdict(pass);
}
/* We also expect to receive the measurements */
[] as_meas_res(verify_meas := false);
}
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_rll_rel_ind_DCCH_0() runs on test_CT {
var RllTestCases tcs := {
valueof(t_EITC(0, ts_RslLinkID_DCCH(0), '01020304'O, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_rel_ind));
}
testcase TC_rll_rel_ind_ACCH_0() runs on test_CT {
var RllTestCases tcs := {
valueof(t_EITC(0, ts_RslLinkID_SACCH(0), ''O, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_rel_ind));
}
testcase TC_rll_rel_ind_DCCH_3() runs on test_CT {
var RllTestCases tcs := {
valueof(t_EITC(3, ts_RslLinkID_DCCH(3), ''O, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_rel_ind));
}
testcase TC_rll_rel_ind_ACCH_3() runs on test_CT {
var RllTestCases tcs := {
valueof(t_EITC(3, ts_RslLinkID_SACCH(3), ''O, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_rel_ind));
}
/* test if RLL REL REQ triggers DISC on Um; UA/DM triggers RLL REL CONF (TS 48.058 3.4) */
private function f_TC_rll_rel_req(charstring id) runs on ConnHdlr {
var RllTestCase tc := g_pars.spec.rll;
f_l1_tune(L1CTL);
RSL.clear;
/* activate the logical channel */
f_est_dchan();
L1CTL.clear;
/* first establish the link-layer */
f_est_rll_mo(tc.sapi, tc.link_id, tc.l3);
/* then send the REL REQ via RSL */
RSL.send(ts_RSL_REL_REQ(g_chan_nr, tc.link_id, RSL_REL_MODE_NORMAL));
/* ... and expect the DISC on the Um side */
alt {
[] as_l1_exp_lapdm(tr_LAPDm_DISC(tc.sapi, cr_MT_CMD, true)) {
/* FIXME: send a UA in resposne to the DISC */
}
}
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_rll_rel_req() runs on test_CT {
var RllTestCases tcs := {
valueof(t_EITC(0, ts_RslLinkID_DCCH(0), '01020304'O, true)),
valueof(t_EITC(0, ts_RslLinkID_SACCH(0), ''O, true)),
valueof(t_EITC(3, ts_RslLinkID_DCCH(3), ''O, true)),
valueof(t_EITC(3, ts_RslLinkID_SACCH(3), ''O, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_rel_req));
}
/* TODO: test if RLL DATA REQ triggers I-frames on Um (TS 48.058 3.5) */
testcase TC_rll_data_req() runs on test_CT {
}
/* TODO: test if I-frames on Um trigger RLL DATA IND (TS 48.058 3.6) */
testcase TC_rll_data_ind() runs on test_CT {
}
/* test if RLL UNIT DATA REQ triggers UI-frame on Um (TS 48.058 3.7) */
private function f_TC_rll_ud_req(charstring id) runs on ConnHdlr {
var RllTestCase tc := g_pars.spec.rll;
f_l1_tune(L1CTL);
RSL.clear;
f_est_dchan();
L1CTL.clear;
/* Send UNITDATA REQ on RSL side */
RSL.send(ts_RSL_UNITDATA_REQ(g_chan_nr, tc.link_id, tc.l3));
/* Expect it to arrive on the other side */
if (tc.link_id.c == SACCH) {
f_l1_exp_lapdm(tr_LAPDm_B4_UI(tc.sapi, cr_MT_CMD, tc.l3));
} else {
f_l1_exp_lapdm(tr_LAPDm_UI(tc.sapi, cr_MT_CMD, tc.l3));
}
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_rll_unit_data_req_DCCH() runs on test_CT {
var octetstring l3 := f_rnd_octstring(15);
var RllTestCases tcs := {
valueof(t_EITC(0, ts_RslLinkID_DCCH(0), l3, true)),
valueof(t_EITC(3, ts_RslLinkID_DCCH(3), l3, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_ud_req));
}
testcase TC_rll_unit_data_req_ACCH() runs on test_CT {
var octetstring l3 := f_rnd_octstring(19);
var RllTestCases tcs := {
valueof(t_EITC(0, ts_RslLinkID_SACCH(0), l3, true)),
valueof(t_EITC(3, ts_RslLinkID_SACCH(3), l3, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_ud_req));
}
/* test if UI-frames on Um trigger RLL UNIT DATA IND (TS 48.058 3.8) */
private function f_TC_rll_ud_ind(charstring id) runs on ConnHdlr {
var RllTestCase tc := g_pars.spec.rll;
f_l1_tune(L1CTL);
RSL.clear;
f_est_dchan();
L1CTL.clear;
/* Send LAPDm UI frame. There is no B4 format in uplink! */
f_tx_lapdm(ts_LAPDm_UI(tc.sapi, cr_MO_CMD, tc.l3), tc.link_id);
/* Expdct RLL UNITDATA IND on RSL side */
alt {
[] RSL.receive(tr_RSL_UNITDATA_IND(g_chan_nr, tc.link_id, tc.l3)) {
setverdict(pass);
}
[] RSL.receive { repeat; }
}
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_rll_unit_data_ind_DCCH() runs on test_CT {
var octetstring l3 := f_rnd_octstring(20);
var RllTestCases tcs := {
valueof(t_EITC(0, ts_RslLinkID_DCCH(0), l3, true)),
valueof(t_EITC(3, ts_RslLinkID_DCCH(3), l3, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_ud_ind));
}
testcase TC_rll_unit_data_ind_ACCH() runs on test_CT {
var octetstring l3 := f_rnd_octstring(18);
var RllTestCases tcs := {
valueof(t_EITC(0, ts_RslLinkID_SACCH(0), l3, true)),
valueof(t_EITC(3, ts_RslLinkID_SACCH(3), l3, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_ud_ind));
}
/***********************************************************************
* Encryption Related
***********************************************************************/
/* send UNITDATA_REQ from BTS to MS and expect it to arrive */
private function f_unitdata_mt(RslLinkId link_id, octetstring l3) runs on ConnHdlr {
RSL.send(ts_RSL_UNITDATA_REQ(g_chan_nr, link_id, l3));
if (link_id.c == SACCH) {
f_l1_exp_lapdm(tr_LAPDm_B4_UI(link_id.sapi, cr_MT_CMD, l3));
} else {
f_l1_exp_lapdm(tr_LAPDm_UI(link_id.sapi, cr_MT_CMD, l3));
}
}
/* Expect (or not expect) other kinds of messages */
private altstep as_rsl_any_ind(boolean exp_any) runs on ConnHdlr {
[exp_any] RSL.receive { repeat; }
[not exp_any] RSL.receive {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected RSL message!");
}
}
/* Send UI frame from MS and expect it to arrive as RLL UNITDATA IND on Abis */
private function f_unitdata_mo(
RslLinkId link_id,
octetstring l3,
boolean exp_sacch := true, /* Should tolerate SACCH messages? */
boolean exp_any := false /* Should tolerate any other RSL messages? */
) runs on ConnHdlr {
timer T := 3.0;
f_tx_lapdm(ts_LAPDm_UI(link_id.sapi, cr_MO_CMD, l3), link_id);
T.start;
/* Expect RLL UNITDATA IND on RSL side */
alt {
[] RSL.receive(tr_RSL_UNITDATA_IND(g_chan_nr, link_id, l3)) {
setverdict(pass);
}
[exp_sacch] as_meas_res(verify_meas := false);
[] as_rsl_any_ind(exp_any);
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for UNIT_DATA_IND");
}
}
}
/* Send I-frame from MS and expect it to arrive as RLL DATA IND on Abis */
private function f_data_mo(
RslLinkId link_id,
boolean p, uint3_t nr, uint3_t ns,
octetstring l3,
boolean exp_sacch := true, /* Should tolerate SACCH messages? */
boolean exp_any := false /* Should tolerate any other RSL messages? */
) runs on ConnHdlr {
timer T := 3.0;
f_tx_lapdm(ts_LAPDm_I(link_id.sapi, cr_MO_CMD, p, nr, ns, l3), link_id);
T.start;
/* Expect RLL DATA IND on RSL side */
alt {
[] RSL.receive(tr_RSL_DATA_IND(g_chan_nr, link_id, l3)) {
setverdict(pass);
}
[exp_sacch] as_meas_res(verify_meas := false);
[] as_rsl_any_ind(exp_any);
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for DATA_IND");
}
}
}
/* Test channel activation with A5/n right from the beginning (like in assignment + hand-over) */
private function f_TC_chan_act_encr(charstring id) runs on ConnHdlr {
f_l1_tune(L1CTL);
f_est_dchan(true);
/* now we actually need to transmit some data both ways to check if the encryption works */
var L1ctlDlMessage dl;
var octetstring l3 := f_rnd_octstring(20);
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0));
/* send UNITDATA_REQ from BTS to MS and expect it to arrive */
f_unitdata_mt(link_id, l3);
/* Send UI frame from MS and expect it to arrive as RLL UNITDATA IND on Abis */
f_unitdata_mo(link_id, l3);
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_chan_act_a51() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_1, f_rnd_octstring(8)));
f_testmatrix_each_chan(pars, refers(f_TC_chan_act_encr));
}
testcase TC_chan_act_a52() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_2, f_rnd_octstring(8)));
f_testmatrix_each_chan(pars, refers(f_TC_chan_act_encr));
}
testcase TC_chan_act_a53() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_3, f_rnd_octstring(8)));
f_testmatrix_each_chan(pars, refers(f_TC_chan_act_encr));
}
testcase TC_chan_act_a54() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_4, f_rnd_octstring(16)));
f_testmatrix_each_chan(pars, refers(f_TC_chan_act_encr));
}
/* Test channel activation with A5/n right from the beginning and RSL MODE MODIFY
which should break the en/decryption on purpose by supplying a new key that is unknown to the MS*/
private function f_TC_rsl_modify_encr(charstring id) runs on ConnHdlr {
f_l1_tune(L1CTL);
f_est_dchan(true);
/* now we actually need to transmit some data both ways to check if the encryption works */
var L1ctlDlMessage dl;
var octetstring l3 := f_rnd_octstring(20);
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0));
/* send UNITDATA_REQ from BTS to MS and expect it to arrive */
f_unitdata_mt(link_id, l3);
/* Send UI frame from MS and expect it to arrive as RLL UNITDATA IND on Abis */
f_unitdata_mo(link_id, l3);
var RSL_Message rsl;
rsl := valueof(ts_RSL_MODE_MODIFY_REQ(g_chan_nr, ts_RSL_ChanMode_SIGN));
/* modify key to break proper encryption */
g_pars.encr.key := f_rnd_octstring(8);
var RSL_IE ei := valueof(t_RSL_IE(RSL_IE_ENCR_INFO, RSL_IE_Body:{encr_info := g_pars.encr}));
rsl.ies := rsl.ies & { ei };
RSL.send(rsl);
timer T0 := 1.0;
T0.start;
/* Expect RSL MODIFY ACK */
alt {
[] RSL.receive(tr_RSL_MODE_MODIFY_ACK(g_chan_nr)) {}
[] RSL.receive(tr_RSL_MODE_MODIFY_NACK(g_chan_nr, ?)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,"MODE MODIFY NACK");
}
[] T0.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for MODE MODIFY (N)ACK");
}
}
var octetstring l3msg := f_rnd_octstring(15);
timer T1 := 3.0;
/* Send UI frame from MS, do not expect it to arrive as RLL UNITDATA IND on Abis
due to broken encryption */
f_tx_lapdm(ts_LAPDm_UI(link_id.sapi, cr_MO_CMD, l3msg), link_id);
T1.start;
alt {
[] RSL.receive(tr_RSL_UNITDATA_IND(g_chan_nr, link_id, l3msg)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "BTS shouldn't be able to decrypt after key change")
}
[] T1.timeout {
setverdict(pass);
}
}
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_rsl_modify_encr() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_1, f_rnd_octstring(8)));
f_testmatrix_each_chan(pars, refers(f_TC_rsl_modify_encr));
}
/* Verify RF RESource INDication messages (periodically sent over the RSL) */
private function f_TC_rsl_rf_resource_ind(template RSL_ResourceInfo info)
runs on test_CT {
const IpaStreamId sid := IPAC_PROTO_RSL_TRX0;
var ASP_RSL_Unitdata ud;
timer T;
/* Intave is 6 SACCH periods by default */
var float Tval := int2float(6 * 480) / 1000.0 + 0.5;
T.start(Tval);
alt {
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RF_RES_IND(info), sid)) {
setverdict(pass);
}
[] RSL_CCHAN.receive(tr_ASP_RSL_UD(tr_RSL_RF_RES_IND(?), sid)) -> value ud {
setverdict(fail, "RF RESource INDication mismatch: ", ud.rsl);
}
[] RSL_CCHAN.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
"Timeout waiting for RF RESource INDication");
}
}
}
testcase TC_rsl_rf_resource_ind() runs on test_CT {
var RSL_ResourceInfo info := { };
f_init(trx_nr := 0);
f_init_l1ctl();
f_l1_tune(L1CTL);
/* g_AllChannels contains all logical channels */
for (var integer i := 0; i < lengthof(g_AllChannels); i := i + 1) {
var RslChannelNr chan_nr := g_AllChannels[i];
/* FIXME: osmo-bts does not report for Osmocom style dynamic timeslots */
if (mp_trx_pars[0].ts[chan_nr.tn].config == GSM_PCHAN_TCHH_TCHF_PDCH)
{ continue; }
info := info & { valueof(ts_RSL_ResourceInfoItem(chan_nr, mp_interf_band)) };
}
/* Align to the first interference report */
f_TC_rsl_rf_resource_ind(?);
/* Test 4 consecutive messages */
for (var integer i := 0; i < 4; i := i + 1) {
f_TC_rsl_rf_resource_ind(info);
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Test unencrypted channel activation followed by explicit ENCR CMD later */
private function f_TC_encr_cmd(charstring id) runs on ConnHdlr {
/* L3 payload doesn't matter, as it is passed transparently */
var BIT3 l3_alg_id := f_alg_id_to_l3(g_pars.encr.alg_id);
var octetstring l3 := enc_PDU_ML3_NW_MS(valueof(ts_RRM_CiphModeCmd(l3_alg_id)));
var RslLinkId link_id := valueof(ts_RslLinkID_DCCH(0));
f_l1_tune(L1CTL);
/* first establish a dedicated channel in the clear */
f_est_dchan(false);
/* Establish ABM */
f_est_rll_mo(link_id.sapi, link_id, '23420815'O);
/* then send the RSL ENCR CMD with an actual RR CIPH MOD CMD inside */
RSL.send(ts_RSL_ENCR_CMD(g_chan_nr, link_id, g_pars.encr.alg_id, g_pars.encr.key, l3));
/* expect the L3 to arrive still unencrypted on the MS side */
f_l1_exp_lapdm(tr_LAPDm_I(link_id.sapi, cr_MT_CMD, ?, ?, ?, l3));
/* configure L1 to apply ciphering */
var uint8_t alg_id := f_alg_id_to_l1ctl(g_pars.encr.alg_id);
f_L1CTL_CRYPTO_REQ(L1CTL, g_pars.chan_nr, alg_id, g_pars.encr.key);
/* send first ciphered I-frame in response and expect it on RSL */
f_data_mo(link_id, true, 1, 0, '0a0b0c0d'O, exp_sacch := true);
/* now the BTS code should have detected the first properly encrypted uplink I-frame,
* and hence enable encryption also on the downlink */
/* expect bi-directional communication work in encrypted mode */
f_unitdata_mo(link_id, f_rnd_octstring(15));
f_unitdata_mt(link_id, f_rnd_octstring(15));
/* release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_encr_cmd_a51() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_1, f_rnd_octstring(8)));
f_testmatrix_each_chan(pars, refers(f_TC_encr_cmd));
}
testcase TC_encr_cmd_a52() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_2, f_rnd_octstring(8)));
f_testmatrix_each_chan(pars, refers(f_TC_encr_cmd));
}
testcase TC_encr_cmd_a53() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_3, f_rnd_octstring(8)));
f_testmatrix_each_chan(pars, refers(f_TC_encr_cmd));
}
testcase TC_encr_cmd_a54() runs on test_CT {
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
pars.encr := valueof(ts_RSL_IE_EncrInfo(RSL_ALG_ID_A5_4, f_rnd_octstring(16)));
f_testmatrix_each_chan(pars, refers(f_TC_encr_cmd));
}
private function f_assert_lapdm(octetstring enc, template LapdmFrame exp_match, charstring name := "") {
var LapdmFrame lf;
var octetstring reenc;
/* decode the LAPDm frame */
if (ischosen(exp_match.ab)) {
lf.ab := dec_LapdmFrameAB(enc);
} else {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "unsupported frame type");
}
/* check if decoder result matches expectation */
if (not match(lf, exp_match)) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str(name, ": decoded LAPDm doesn't match"));
} else {
log(name, ": matched");
setverdict(pass);
}
/* test if re-encoded frame equals original input */
reenc := enc_LapdmFrame(lf);
if (enc != reenc) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str(name, ": re-encoded LAPDm frame doesn't match"));
} else {
setverdict(pass);
}
}
testcase TC_lapdm_selftest() runs on test_CT {
f_assert_lapdm('030301'O, tr_LAPDm_UI(0, true, ''O), "ui_s0_empty");
f_assert_lapdm('0F0301'O, tr_LAPDm_UI(3, true, ''O), "ui_s3_empty");
f_assert_lapdm('013F01'O, tr_LAPDm_SABM(0, false, true, ''O), "sabm_s0_empty");
f_assert_lapdm('013F1123420815'O, tr_LAPDm_SABM(0, false, true, '23420815'O), "sabm_s0_l3");
f_assert_lapdm('03E101'O, tr_LAPDm_RR(0, true, false, 7), "rr_s0_7");
f_assert_lapdm('03000d063505'O, tr_LAPDm_I(0, true, false, 0, 0, '063505'O), "I/0/0");
f_assert_lapdm('03e00d063505'O, tr_LAPDm_I(0, true, false, 7, 0, '063505'O), "I/7/0");
}
/***********************************************************************
* DTX Related (see GSM 05.08, section 8.3)
***********************************************************************/
private function f_TC_tch_sign_l2_fill_frame(charstring id) runs on ConnHdlr {
var L1ctlDlMessage dl;
var GsmFrameNumber first_fn;
var boolean is_first_frame := true;
var integer nfill_frames := 0;
var integer expected_fill_frames := 10000; /* initial value causes test failure if not overridden */
/* Frames numbers (mod 104) for which a fill frame is expected on TCHF if DTX is enabled. */
var Integers required_tdma_frames_dtx_tchf := { 52, 53, 54, 55, 56, 57, 58, 59 };
const integer frame_dtx_tchf_mod := 104;
/* Frame numbers (mod 104) for which a fill frame is expected at the L1SAP level,
* which operates in terms of blocks rather than frames. */
var Integers required_tdma_blocks_dtx_tchf := { 52, 56 };
const integer block_dtx_tchf_mod := 26;
timer T := 5.0;
f_l1_tune(L1CTL);
RSL.clear;
L1CTL.clear;
/* activate TCHF signalling channel */
f_est_dchan(false);
/* A template for matching dummy LAPDm func=UA frames */
var template L1ctlDlMessage tr_fill_frame := tr_L1CTL_DATA_IND(
chan_nr := g_chan_nr, link_id := tr_RslLinkID_DCCH(?),
l2_data := f_pad_oct('030301'O, 23, '2B'O));
T.start;
alt {
[] L1CTL.receive(tr_fill_frame) -> value dl {
var GsmFrameNumber fn := dl.dl_info.frame_nr;
if (is_first_frame) {
is_first_frame := false;
first_fn := dl.dl_info.frame_nr;
}
if (g_pars.chan_mode.dtx_d) {
if (fn > first_fn + frame_dtx_tchf_mod) {
T.stop;
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
/* With DTX enabled we can expect at least 3 fill frames for every 104 frames. */
expected_fill_frames := 2;
if (nfill_frames < expected_fill_frames) {
setverdict(fail, "Not enough fill frames received: ",
nfill_frames, " out of ", expected_fill_frames);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
} else {
setverdict(pass);
}
} else {
/* On DTX TCH/F channels, fill frames occur only for specific frame numbers mod 104.
* Furthermore, the L1SAP layer gives us frame numbers for the start of a block so
* we should only see the subset of frames numbers which correspond to a block boundary.
* TCH/F blocks are defined to start at 0,4,8,13,17,21 (modulo 26) */
for (var integer i := 0; i < lengthof(required_tdma_blocks_dtx_tchf); i := i + 1) {
if (fn mod frame_dtx_tchf_mod == required_tdma_blocks_dtx_tchf[i]) {
nfill_frames := nfill_frames + 1;
repeat;
}
}
setverdict(fail, "Received DTX TCH fill frame with bad frame number: ", fn,
" (mod ", frame_dtx_tchf_mod, ": ", fn mod frame_dtx_tchf_mod, ")",
" (mod ", block_dtx_tchf_mod, ": ", fn mod block_dtx_tchf_mod, ")");
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
} else {
nfill_frames := nfill_frames + 1;
if (fn > first_fn + frame_dtx_tchf_mod) {
T.stop;
select (g_chan_nr) {
/* TCH/F: we expect 24 fill frames for every 104 frames. */
case (t_RslChanNr_Bm(?)) { expected_fill_frames := 24; }
/* TCH/H: we expect 2 fill frames for every 104 frames. */
case (t_RslChanNr_Lm(?, ?)) { expected_fill_frames := 2; }
/* SDCCH: we expect 5 fill frames for every 104 frames. */
case (t_RslChanNr_SDCCH4(?, ?)) { expected_fill_frames := 4; }
case (t_RslChanNr_SDCCH8(?, ?)) { expected_fill_frames := 4; }
case else {
/* This shall not happen, just to be sure */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
if (nfill_frames >= expected_fill_frames) {
setverdict(pass);
} else {
setverdict(fail, "Not enough fill frames received: ",
nfill_frames, " out of ", expected_fill_frames);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
} else {
repeat;
}
}
}
[] L1CTL.receive { repeat; }
[] T.timeout {
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for L2 fill frames on Um");
}
}
}
private function f_tch_sign_l2_fill_frame(boolean dtxd) runs on test_CT {
var template RSL_IE_ChannelMode ch_mode;
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
pars.t_guard := 60.0;
f_init();
ch_mode := ts_RSL_ChanMode_SIGN(dtx_downlink := dtxd);
for (var integer i := 0; i < sizeof(g_AllChannels); i := i + 1) {
pars := valueof(t_Pars(g_AllChannels[i], ch_mode));
if (dtxd) {
if (i >= 4) { /* DTX is only allowed on TCH/F */
break;
}
}
vc_conn := f_start_handler(refers(f_TC_tch_sign_l2_fill_frame), pars);
vc_conn.done;
}
}
/* Verify that L2 fill frames are sent on TCH in signaling mode if
* there is nothing to transmit while DTX is disabled on downlink. */
testcase TC_tch_sign_l2_fill_frame() runs on test_CT {
f_tch_sign_l2_fill_frame(false);
}
/* Verify that particular L2 fill frames are sent on TCH in signaling mode if
* there is nothing to transmit while DTX is enabled on downlink. */
testcase TC_tch_sign_l2_fill_frame_dtxd() runs on test_CT {
f_tch_sign_l2_fill_frame(true);
}
testcase TC_chopped_ipa_ping() runs on test_CT {
IPA_Testing.f_run_TC_chopped_ipa_ping(mp_rsl_ip, mp_rsl_port, LISTEN_FOR_CLIENT);
}
testcase TC_chopped_ipa_payload() runs on test_CT {
IPA_Testing.f_run_TC_chopped_ipa_payload(mp_rsl_ip, mp_rsl_port, LISTEN_FOR_CLIENT);
}
/* Callback function to be called by as_TC_ms_pwr_ctrl_constant().
* Return value: Measurement Report to be sent (encoded octetstring). */
type function f_TC_ms_pwr_ctrl_cb(inout SacchL1Header l1h, integer num_blocks)
runs on ConnHdlr return octetstring;
private altstep as_TC_ms_pwr_ctrl(f_TC_ms_pwr_ctrl_cb cb, inout integer num_blocks)
runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
var SacchL1Header l1h;
var octetstring l2;
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(?))) -> value l1_dl {
/* Parse the L1 SACCH header (MS Power Level & Timing Advance) */
l1h := dec_SacchL1Header(substr(l1_dl.payload.data_ind.payload, 0, 2));
log("as_TC_ms_pwr_ctrl(): Rx SACCH L1 header: ", l1h);
/* Pass it to the user specified call-back function */
l2 := cb.apply(l1h, num_blocks);
/* Send a Measurement Report generated by the call-back */
L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(l1_dl.dl_info.chan_nr,
l1_dl.dl_info.link_id,
l1h, l2));
/* Shall we keep going? */
num_blocks := num_blocks - 1;
log("as_TC_ms_pwr_ctrl(): ", num_blocks, " SACCH blocks remaining");
if (num_blocks > 0) { repeat; }
}
[] L1CTL.receive { repeat; }
}
private function f_TC_ms_pwr_ctrl_cb_def(inout SacchL1Header l1h, integer num_blocks)
runs on ConnHdlr return octetstring {
/* Command the L1 to apply received parameters */
f_L1CTL_PARAM(L1CTL, l1h.actual_ta, l1h.ms_power_lvl);
/* Dummy measurement report (the results are invalid) */
return f_pad_oct('010349'O & '0615004001C0'O, 21, '00'O);
}
private function f_TC_ms_pwr_ctrl_cb_const(inout SacchL1Header l1h, integer num_blocks)
runs on ConnHdlr return octetstring {
if (l1h.ms_power_lvl != g_pars.l1_pars.ms_power_level) {
setverdict(fail, "Unexpected MS Power level change: ",
g_pars.l1_pars.ms_power_level, " -> ",
l1h.ms_power_lvl);
}
return f_TC_ms_pwr_ctrl_cb_def(l1h, num_blocks);
}
private function f_TC_ms_pwr_ctrl_cb_rssi_pwm(inout SacchL1Header l1h, integer num_blocks)
runs on ConnHdlr return octetstring {
/* UL RSSI oscillation driven by SACCH block number */
if (num_blocks rem 2 == 0) {
f_trxc_fake_rssi(-100);
} else {
f_trxc_fake_rssi(-50);
}
/* Make sure that MS power level remains constant */
return f_TC_ms_pwr_ctrl_cb_const(l1h, num_blocks);
}
/* Make sure that MS power level remains constant when 'rx-current' equals 'rx-target' */
private function f_TC_ms_pwr_ctrl_constant(charstring id)
runs on ConnHdlr {
var integer num_blocks := 8;
timer T := int2float(num_blocks);
f_l1_tune(L1CTL);
RSL.clear;
/* These IEs are needed for autonomous MS power control */
var template (value) RSL_IE_List ies := {
t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{
ms_power := ts_RSL_IE_MS_Power(g_pars.l1_pars.ms_power_level)
}),
t_RSL_IE(RSL_IE_MS_POWER_PARAM, RSL_IE_Body:{
ms_power_params := ts_RSL_IE_MS_Power_Parameters(''O)
})
};
/* Ensure that 'rx-current' equals 'rx-target' */
f_trxc_fake_rssi(mp_uplink_power_target);
/* Establish a dedicated channel */
f_est_dchan(more_ies := valueof(ies));
L1CTL.clear;
T.start;
alt {
[] as_TC_ms_pwr_ctrl(refers(f_TC_ms_pwr_ctrl_cb_const), num_blocks);
[] T.timeout {
setverdict(fail, "Not all SACCH blocks were processed in time, ",
num_blocks, " were not handled");
}
}
/* Release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_ms_pwr_ctrl_constant() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
/* Explicitly configure the Uplink power target (in dBm) */
f_vty_config(BTSVTY, "bts 0", "uplink-power-target " & int2str(mp_uplink_power_target));
for (var integer i := 0; i < sizeof(g_AllChanTypes); i := i + 1) {
pars := valueof(t_Pars(g_AllChanTypes[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": starting on ", pars.chan_nr);
vc_conn := f_start_handler(refers(f_TC_ms_pwr_ctrl_constant),
pars, trxc_comp := true);
vc_conn.done;
}
/* No need to reset Uplink power parameters - the IUT restarts anyway */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Test Exponentially Weighted Moving Average (EWMA) power filtering */
private function f_TC_ms_pwr_ctrl_pf_ewma(charstring id)
runs on ConnHdlr {
var integer num_blocks := 16;
timer T := int2float(num_blocks);
f_l1_tune(L1CTL);
RSL.clear;
/* These IEs are needed for autonomous MS power control */
var template (value) RSL_IE_List ies := {
t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{
ms_power := ts_RSL_IE_MS_Power(g_pars.l1_pars.ms_power_level)
}),
t_RSL_IE(RSL_IE_MS_POWER_PARAM, RSL_IE_Body:{
ms_power_params := ts_RSL_IE_MS_Power_Parameters(''O)
})
};
/* Ensure that 'rx-current' equals 'rx-target' */
f_trxc_fake_rssi(mp_uplink_power_target);
/* Establish a dedicated channel */
f_est_dchan(more_ies := valueof(ies));
L1CTL.clear;
T.start;
alt {
[] as_TC_ms_pwr_ctrl(refers(f_TC_ms_pwr_ctrl_cb_rssi_pwm), num_blocks);
[] T.timeout {
setverdict(fail, "Not all SACCH blocks were processed in time, ",
num_blocks, " were not handled");
}
}
/* Release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_ms_pwr_ctrl_pf_ewma() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
/* Explicitly configure EWMA filtering with 80% smoothing (alpha = 0.2) */
f_vty_config(BTSVTY, "bts 0", "uplink-power-filtering algo ewma beta 80");
/* Explicitly configure the Uplink power range (target and delte) */
f_vty_config(BTSVTY, "bts 0", "uplink-power-target " & int2str(mp_uplink_power_target)
& " hysteresis " & int2str(mp_uplink_power_hysteresis));
for (var integer i := 0; i < sizeof(g_AllChanTypes); i := i + 1) {
pars := valueof(t_Pars(g_AllChanTypes[i], ts_RSL_ChanMode_SIGN));
log(testcasename(), ": starting on ", pars.chan_nr);
vc_conn := f_start_handler(refers(f_TC_ms_pwr_ctrl_pf_ewma),
pars, trxc_comp := true);
vc_conn.done;
}
/* No need to reset Uplink power parameters - the IUT restarts anyway */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
private function f_TC_speech_no_rtp(charstring id) runs on ConnHdlr {
var template L1ctlDlMessage tr_bad_frame;
var L1ctlDlMessage l1_dl;
timer T := 8.0;
f_l1_tune(L1CTL);
RSL.clear;
f_est_dchan();
/* There may be a few errors right after the channel activation */
f_sleep(2.0); /* ... so let's give the L1 some time to stabilize */
L1CTL.clear;
/* A universal template for bad Downlink frame: {DATA,TRAFFIC}.ind */
tr_bad_frame := tr_L1CTL_TRAFFIC_IND(g_chan_nr, tr_RslLinkID_DCCH(0));
tr_bad_frame.header.msg_type := (L1CTL_DATA_IND, L1CTL_TRAFFIC_IND);
tr_bad_frame.dl_info.fire_crc := (1..255); /* != 0 */
tr_bad_frame.payload := ?;
T.start;
alt {
/* OS#4823: DATA.ind or TRAFFIC.ind with bad CRC most likely means that
* the IUT is sending *dummy bursts*, so the L1 fails to decode them. */
[] L1CTL.receive(tr_bad_frame) -> value l1_dl {
setverdict(fail, "Received {DATA,TRAFFIC}.ind with bad CRC: ", l1_dl);
}
[] as_l1_sacch();
[] L1CTL.receive { repeat; }
[] T.timeout {
/* We're done, break the loop */
setverdict(pass);
}
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_speech_no_rtp_tchf() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
/* TS1, TCH/F, V1 (FR codec) */
pars := valueof(t_Pars(ts_RslChanNr_Bm(1), ts_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM1)));
vc_conn := f_start_handler(refers(f_TC_speech_no_rtp), pars);
vc_conn.done;
}
testcase TC_speech_no_rtp_tchh() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
/* TS5, TCH/H, V1 (HR codec) */
pars := valueof(t_Pars(ts_RslChanNr_Lm(5, 0), ts_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM1)));
vc_conn := f_start_handler(refers(f_TC_speech_no_rtp), pars);
vc_conn.done;
}
/* Verify handling of Downlink and Uplink speech frames */
private function f_TC_speech_rtp(charstring id) runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
var PDU_RTP rtp_pdu;
var octetstring pl;
timer Td, Tu;
f_l1_tune(L1CTL);
f_est_dchan();
/* Activate the RTP emulation */
pl := f_rnd_octstring(6);
f_rtpem_activate(pl);
/* Give the scheduler some time to fill up the buffers */
f_sleep(2.0);
L1CTL.clear;
RSL.clear;
/* Make sure that Downlink frames are received at the UE */
Td.start(2.0);
alt {
[] L1CTL.receive(tr_L1CTL_TRAFFIC_IND(g_chan_nr, frame := pl)) -> value l1_dl {
log("TCH received: ", l1_dl.payload.traffic_ind.data);
L1CTL.send(ts_L1CTL_TRAFFIC_REQ(g_chan_nr, l1_dl.dl_info.link_id,
l1_dl.payload.traffic_ind.data));
setverdict(pass);
}
[] L1CTL.receive(tr_L1CTL_TRAFFIC_IND(g_chan_nr, frame := ?)) -> value l1_dl {
setverdict(fail, "Rx unexpected Downlink speech frame ",
"(", l1_dl.payload.traffic_ind.data, ") ",
"expected (", pl, ")");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
[] as_l1_sacch();
[] L1CTL.receive { repeat; }
[] Td.timeout {
setverdict(fail, "Timeout waiting for Downlink speech frames");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
}
/* Make sure that Uplink frames are received at the BTS */
RTPEM_DATA.clear;
Tu.start(2.0);
alt {
[] RTPEM_DATA.receive(PDU_RTP:?) -> value rtp_pdu {
if (rtp_pdu.data != pl)
{ repeat; }
}
[] RTPEM_DATA.receive { repeat; }
[] Tu.timeout {
setverdict(fail, "Timeout waiting for Uplink speech frames");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
}
f_rtpem_mode(RTPEM_CTRL, RTPEM_MODE_NONE);
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rsl_chan_deact();
f_rslem_unregister(0, g_chan_nr);
}
testcase TC_speech_rtp_tchf() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
/* TS1, TCH/F, V1 (FR codec) */
pars := valueof(t_Pars(ts_RslChanNr_Bm(1), ts_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM1)));
vc_conn := f_start_handler(refers(f_TC_speech_rtp), pars);
vc_conn.done;
/* TS1, TCH/F, V2 (EFR codec) */
pars := valueof(t_Pars(ts_RslChanNr_Bm(1), ts_RSL_ChanMode(RSL_CHRT_TCH_F, RSL_CMOD_SP_GSM2)));
vc_conn := f_start_handler(refers(f_TC_speech_rtp), pars);
vc_conn.done;
/* TODO: also test V3 (AMR codec) */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_speech_rtp_tchh() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
/* TS5, TCH/H0, V1 (HR codec) */
pars := valueof(t_Pars(ts_RslChanNr_Lm(5, 0), ts_RSL_ChanMode(RSL_CHRT_TCH_H, RSL_CMOD_SP_GSM1)));
vc_conn := f_start_handler(refers(f_TC_speech_rtp), pars);
vc_conn.done;
/* TODO: also test V3 (AMR codec) */
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
private function f_TC_early_immediate_assignment(charstring id) runs on ConnHdlr {
var GsmFrameNumber fn;
var ChannelDescription ch_desc;
var integer ra := 23;
f_l1_tune(L1CTL);
RSL.clear;
/* Send RACH request and wait for ChanReq */
fn := f_rach_req_wait_chan_rqd(ra);
/* The BSC already sends the Immediate Assignment, before the channel is active.
* (Also before the Channel Activation, even. I tried to write this test so that we first send the Chan Act and then the IMM
* ASS, but osmo-bts-trx responds so fast that the Chan Activ ACK comes back even before titan dispatches the
* IMM ASS. So move the IMM ASS even before the Chan Activ.) */
if (g_pars.fhp.enabled) {
ch_desc := valueof(ts_ChanDescH1(g_pars.chan_nr, g_pars.fhp.maio_hsn, g_pars.tsc));
} else {
ch_desc := valueof(ts_ChanDescH0(g_pars.chan_nr, mp_trx_pars[0].arfcn, g_pars.tsc));
}
var GsmRrMessage rr_msg := valueof(ts_IMM_ASS(ra, fn, 0, ch_desc, g_pars.fhp.ma_map));
RSL.send(ts_RSL_IMM_ASSIGN(enc_GsmRrMessage(rr_msg)));
/* Do not expect the Immediate Assignment to show up on MS side yet. Even give it one second before the BSC
* requests Chan Activ, to make sure the RR IMM ASS is held back. */
var L1ctlDlMessage dl;
var GsmRrMessage rr;
var template GsmRrMessage rr_imm_ass := tr_IMM_ASS(ra, fn);
rr_imm_ass.payload.imm_ass.ded_or_tbf := ?;
rr_imm_ass.payload.imm_ass.pkt_chan_desc := *;
rr_imm_ass.payload.imm_ass.chan_desc := *;
timer T := 1.0;
T.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(t_RslChanNr_PCH_AGCH(0))) -> value dl {
rr := dec_GsmRrMessage(dl.payload.data_ind.payload);
if (match(rr, rr_imm_ass)) {
setverdict(fail, "Expected IMM ASS to be delayed until Chan Act ACK, but it was passed to the MS immediately");
mtc.stop;
} else {
repeat;
}
}
[] L1CTL.receive { repeat; }
[] T.timeout;
}
RSL.send(ts_RSL_CHAN_ACT(g_chan_nr, g_pars.chan_mode, t_RSL_IE_ActType_IA));
RSL.receive(tr_RSL_CHAN_ACT_ACK(g_chan_nr));
/* Now expect the IMM ASS on Um */
f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, fn);
setverdict(pass);
/* Release the channel */
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
}
testcase TC_early_immediate_assignment() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars := valueof(t_Pars(t_RslChanNr_Bm(1), ts_RSL_ChanMode_SIGN));
f_init();
vc_conn := f_start_handler(refers(f_TC_early_immediate_assignment), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
private function f_TC_est_dchan(charstring id) runs on ConnHdlr {
var integer ra := oct2int(f_rnd_ra_cs());
var ChannelDescription ch_desc;
/* Tune the MS to BCCH */
f_l1_tune(L1CTL);
/* Send RACH request and wait for ChanReq */
var GsmFrameNumber fn := f_rach_req_wait_chan_rqd(ra);
/* Activate channel on the BTS side */
f_rsl_chan_act(g_pars.chan_mode);
/* Craft channel description (with or without frequency hopping parameters) */
if (g_pars.fhp.enabled) {
ch_desc := valueof(ts_ChanDescH1(g_pars.chan_nr, g_pars.fhp.maio_hsn, g_pars.tsc));
} else {
ch_desc := valueof(ts_ChanDescH0(g_pars.chan_nr, mp_trx_pars[0].arfcn, g_pars.tsc));
}
/* Send IMM.ASS via CCHAN */
var GsmRrMessage rr_msg := valueof(ts_IMM_ASS(ra, fn, 0, ch_desc, g_pars.fhp.ma_map));
RSL.send(ts_RSL_IMM_ASSIGN(enc_GsmRrMessage(rr_msg)));
/* Receive IMM.ASS on the MS side */
var ImmediateAssignment imm_ass := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, fn);
/* Match the Channel Description IE in received IMM.ASS */
if (not match(imm_ass.chan_desc, ch_desc)) {
setverdict(fail, "Channel Description IE does not match");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* Tune the MS to a dedicated channel indicated in the IMM.ASS */
f_L1CTL_DM_EST_REQ_IA(L1CTL, imm_ass, g_pars.fhp.ma);
/* Expect SACCH frames on Downlink */
L1CTL.clear;
f_exp_sacch(true);
/* We're done, deactivate and release */
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
f_rsl_chan_deact();
}
testcase TC_est_dchan() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init();
for (var integer i := 0; i < sizeof(g_AllChannels); i := i + 1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_est_dchan), pars);
vc_conn.done;
}
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
private type record TopTestCase {
RSL_IE_BS_Power bs_power,
RSL_IE_OSMO_TopAcchCap top_cap,
record of TopTestStep steps
};
private type record TopTestStep {
MeasurementResults meas_res optional,
GsmRxLev overpower_sacch,
GsmRxLev overpower_facch
};
private function f_rxlev_match(template (present) RslLinkId link_id,
template (present) GsmRxLev rxlev)
runs on ConnHdlr {
var L1ctlDlMessage dl := f_L1CTL_rx_data(L1CTL, g_chan_nr, link_id);
if (not match(dl.dl_info.rx_level, rxlev)) {
setverdict(fail, "RxLev(", link_id, ") := ", dl.dl_info.rx_level,
" does not match expected RxLev := ", rxlev);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
}
private function f_TC_acch_overpower(charstring id) runs on ConnHdlr {
var TopTestCase tc := g_pars.spec.top;
var GsmRxLev rxlev_dcch;
var L1ctlDlMessage dl;
/* Wait for Pau ramping to complete */
f_sleep(6.0);
f_l1_tune(L1CTL);
L1CTL.clear;
RSL.clear;
/* Measure RxLev(BCCH), calculate RxLev(DCCH) */
dl := f_L1CTL_rx_data(L1CTL, t_RslChanNr_BCCH(0));
rxlev_dcch := dl.dl_info.rx_level - (tc.bs_power.power_level * 2);
log("RxLev(BCCH) := ", dl.dl_info.rx_level);
log("Expected RxLev(DCCH) := ", rxlev_dcch);
/* Additional IEs for the CHANnel ACTIVation message */
var template (value) RSL_IE_List ies := {
/* Employ BS power control in static mode */
t_RSL_IE(RSL_IE_MS_POWER, RSL_IE_Body:{
bs_power := tc.bs_power
}),
/* Indicate the given Temporary Overpower capability */
t_RSL_IE(RSL_IE_OSMO_TOP_ACCH_CAP, RSL_IE_Body:{
top_acch_cap := tc.top_cap
})
};
/* Establish a dedicated channel */
f_est_dchan(more_ies := valueof(ies));
/* Give it some time to stabilize */
f_sleep(0.480 * 2.0);
L1CTL.clear;
RSL.clear;
for (var integer i := 0; i < lengthof(tc.steps); i := i + 1) {
var TopTestStep step := tc.steps[i];
var GsmRxLev rxlev_facch := rxlev_dcch + step.overpower_facch;
var GsmRxLev rxlev_sacch := rxlev_dcch + step.overpower_sacch;
log("Executing step[", i, "] := ", step);
/* Send RR Measurement Report (if present) */
if (ispresent(step.meas_res)) {
f_transceive_meas_rep(step.meas_res);
f_sleep(0.480 * 2.0);
L1CTL.clear;
}
/* Check RxLev on both FACCH and SACCH */
f_rxlev_match(tr_RslLinkID_DCCH(?), rxlev_facch);
f_rxlev_match(tr_RslLinkID_SACCH(?), rxlev_sacch);
setverdict(pass);
}
}
testcase TC_acch_overpower_rxqual_thresh() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
/* Verify lower and upper RxQual thresholds */
var template TopTestCase top := {
bs_power := ts_RSL_IE_BS_Power(4), /* 4 x 2dB = 8dB */
top_cap := ts_RSL_IE_OSMO_TopAcchCap(4), /* 4dB */
steps := {
/* Channel established, no overpower */
{ meas_res := omit,
overpower_sacch := 0, overpower_facch := 0 },
/* Worst possible RxQual value, overpower of 4dB */
{ meas_res := ts_MeasurementResults(rxq_f := 7),
overpower_sacch := 4, overpower_facch := 4 },
/* Worst possible RxQual value, disabling overpower */
{ meas_res := ts_MeasurementResults(rxq_f := 0),
overpower_sacch := 0, overpower_facch := 0 },
/* Lower threshold not reached, no overpower */
{ meas_res := ts_MeasurementResults(rxq_f := 1),
overpower_sacch := 0, overpower_facch := 0 },
/* Lower threshold not reached, no overpower */
{ meas_res := ts_MeasurementResults(rxq_f := 2),
overpower_sacch := 0, overpower_facch := 0 },
/* Lower threshold reached, overpower of 4dB */
{ meas_res := ts_MeasurementResults(rxq_f := 4),
overpower_sacch := 4, overpower_facch := 4 },
/* Upper threshold not reached, keeping overpower */
{ meas_res := ts_MeasurementResults(rxq_f := 3),
overpower_sacch := 4, overpower_facch := 4 },
/* Upper threshold reached, disabling overpower */
{ meas_res := ts_MeasurementResults(rxq_f := 2),
overpower_sacch := 0, overpower_facch := 0 }
}
};
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN,
spec := { top := top }, trx_nr := 1));
vc_conn := f_start_handler(refers(f_TC_acch_overpower), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_acch_overpower_rxqual_thresh_dtx() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
/* Verify handling of FULL and SUB values */
var template TopTestCase top := {
bs_power := ts_RSL_IE_BS_Power(4), /* 4 x 2dB = 8dB */
top_cap := ts_RSL_IE_OSMO_TopAcchCap(4), /* 4dB */
steps := {
/* Channel established, no overpower */
{ meas_res := omit,
overpower_sacch := 0, overpower_facch := 0 },
/* Invalid measurement results, no overpower */
{ meas_res := ts_MeasurementResults(rxq_f := 7,
rxq_s := 7,
valid := false),
overpower_sacch := 0, overpower_facch := 0 },
/* DTXu was in use, no overpower */
{ meas_res := ts_MeasurementResults(rxq_f := 7,
rxq_s := 0,
dtx_used := true),
overpower_sacch := 0, overpower_facch := 0 },
/* DTXu was in use, overpower of 4 dB */
{ meas_res := ts_MeasurementResults(rxq_f := 0,
rxq_s := 7,
dtx_used := true),
overpower_sacch := 4, overpower_facch := 4 }
}
};
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN,
spec := { top := top }, trx_nr := 1));
vc_conn := f_start_handler(refers(f_TC_acch_overpower), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_acch_overpower_always_on_facch() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
/* Overpower is always on, SACCH disabled */
var template TopTestCase top := {
bs_power := ts_RSL_IE_BS_Power(4), /* 4 x 2dB = 8dB */
top_cap := ts_RSL_IE_OSMO_TopAcchCap(overpower := 4, /* 4dB */
rxqual := 0, /* always on */
sacch_enable := false),
steps := {
/* Channel established, FACCH overpower */
{ meas_res := omit,
overpower_sacch := 0, overpower_facch := 4 },
/* MS indicates good RxQual, no difference */
{ meas_res := ts_MeasurementResults(rxq_f := 0),
overpower_sacch := 0, overpower_facch := 4 },
/* MS indicates bad RxQual, no difference */
{ meas_res := ts_MeasurementResults(rxq_f := 7),
overpower_sacch := 0, overpower_facch := 4 }
}
};
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN,
spec := { top := top }, trx_nr := 1));
vc_conn := f_start_handler(refers(f_TC_acch_overpower), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_acch_overpower_always_on_sacch() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
/* Overpower is always on, FACCH disabled */
var template TopTestCase top := {
bs_power := ts_RSL_IE_BS_Power(4), /* 4 x 2dB = 8dB */
top_cap := ts_RSL_IE_OSMO_TopAcchCap(overpower := 4, /* 4dB */
rxqual := 0, /* always on */
facch_enable := false),
steps := {
/* Channel established, SACCH overpower */
{ meas_res := omit,
overpower_sacch := 4, overpower_facch := 0 },
/* MS indicates good RxQual, no difference */
{ meas_res := ts_MeasurementResults(rxq_f := 0),
overpower_sacch := 4, overpower_facch := 0 },
/* MS indicates bad RxQual, no difference */
{ meas_res := ts_MeasurementResults(rxq_f := 7),
overpower_sacch := 4, overpower_facch := 0 }
}
};
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN,
spec := { top := top }, trx_nr := 1));
vc_conn := f_start_handler(refers(f_TC_acch_overpower), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_acch_overpower_limit() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
/* Overpower higher than current power reduction level (2dB) */
var template TopTestCase top := {
bs_power := ts_RSL_IE_BS_Power(1), /* 1 x 2dB = 2dB */
top_cap := ts_RSL_IE_OSMO_TopAcchCap(overpower := 4, /* 4dB */
rxqual := 0 /* always on */),
steps := {
/* Channel established, ACCH overpower of 2dB */
{ meas_res := omit,
overpower_sacch := 2, overpower_facch := 2 },
/* MS indicates good RxQual, no difference */
{ meas_res := ts_MeasurementResults(rxq_f := 0),
overpower_sacch := 2, overpower_facch := 2 },
/* MS indicates bad RxQual, no difference */
{ meas_res := ts_MeasurementResults(rxq_f := 7),
overpower_sacch := 2, overpower_facch := 2 }
}
};
pars := valueof(t_Pars(t_RslChanNr_Bm(0), ts_RSL_ChanMode_SIGN,
spec := { top := top }, trx_nr := 1));
vc_conn := f_start_handler(refers(f_TC_acch_overpower), pars);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* test generation of RLL ERR IND based on Um errors (TS 48.058 3.9) */
/* protocol error as per 44.006 */
/* link layer failure (repetition of I-frame N200 times without ACK */
/* repetition of SABM or DISC N200 times without ACK */
/* receptiom of SABM in multi-frame established state */
/* TODO Areas:
* channel activation
** with BS_Power / MS_Power, bypassing power control loop
** on primary vs. secondary TRX
** with timing advance from initial activation on
* mode modify
** encryption
** multirate
* check DEACTIVATE SACCH
** unsupported algorithm
* handover detection
* BS Power Control
* Physical Context
* RF resource ind
* error handling
** IE duplicated?
* PCU interface
** DATA_IND from BTS->PCU
** verification of PCU-originated DATA_REQ arrival on Um/MS side
*/
control {
execute( TC_est_dchan() );
execute( TC_chan_act_stress() );
execute( TC_chan_act_react() );
execute( TC_chan_deact_not_active() );
execute( TC_chan_act_wrong_nr() );
execute( TC_deact_sacch() );
execute( TC_sacch_filling() );
execute( TC_sacch_info_mod() );
execute( TC_sacch_multi() );
execute( TC_sacch_multi_chg() );
execute( TC_sacch_chan_act() );
execute( TC_sacch_chan_act_ho_async() );
execute( TC_sacch_chan_act_ho_sync() );
execute( TC_rach_content() );
execute( TC_rach_content_emerg() );
execute( TC_rach_count() );
execute( TC_rach_max_ta() );
execute( TC_ho_rach() );
execute( TC_rach_load_idle_thresh0() );
execute( TC_rach_load_idle_below_thresh() );
execute( TC_rach_load_count() );
execute( TC_meas_res_speech_tchf() );
execute( TC_meas_res_speech_tchf_facch() );
execute( TC_meas_res_speech_tchh() );
execute( TC_meas_res_speech_tchh_facch() );
execute( TC_meas_res_speech_tchh_toa256() );
execute( TC_meas_res_sign_tchf() );
execute( TC_meas_res_sign_tchh() );
execute( TC_meas_res_sign_sdcch4() );
execute( TC_meas_res_sign_sdcch8() );
execute( TC_meas_res_sign_tchh_toa256() );
execute( TC_meas_res_speech_tchf_sapi3() );
execute( TC_meas_res_speech_tchh_sapi3() );
execute( TC_tx_power_start_ramp_up_bcch() );
execute( TC_tx_power_start_ramp_down_bcch() );
execute( TC_tx_power_ramp_adm_state_change() );
execute( TC_rsl_bs_pwr_static_ass() );
execute( TC_rsl_bs_pwr_static_power_control() );
execute( TC_rsl_ms_pwr_ctrl() );
execute( TC_rsl_ms_pwr_dyn_active() );
execute( TC_rsl_ms_pwr_dyn_active2() );
execute( TC_rsl_ms_pwr_dyn_up() );
execute( TC_rsl_ms_pwr_dyn_down() );
execute( TC_rsl_ms_pwr_dyn_ass_updown() );
execute( TC_rsl_ms_pwr_dyn_max() );
execute( TC_rsl_chan_initial_ms_pwr() );
execute( TC_rsl_chan_initial_ta() );
execute( TC_rsl_modify_encr() );
execute( TC_rsl_rf_resource_ind() );
execute( TC_conn_fail_crit() );
execute( TC_paging_imsi_80percent() );
execute( TC_paging_tmsi_80percent() );
execute( TC_paging_imsi_200percent() );
execute( TC_paging_tmsi_200percent() );
execute( TC_rsl_protocol_error() );
execute( TC_rsl_mand_ie_error() );
execute( TC_rsl_ie_content_error() );
execute( TC_si_sched_default() );
execute( TC_si_sched_1() );
execute( TC_si_sched_2bis() );
execute( TC_si_sched_2ter() );
execute( TC_si_sched_2ter_2bis() );
execute( TC_si_sched_2quater() );
execute( TC_si_sched_13() );
execute( TC_si_sched_13_2bis_2ter_2quater() );
execute( TC_ipa_dlcx_not_active() );
execute( TC_ipa_crcx_twice_not_active() );
execute( TC_ipa_crcx_mdcx_dlcx_not_active() );
execute( TC_ipa_crcx_mdcx_mdcx_dlcx_not_active() );
execute( TC_ipa_crcx_sdcch_not_active() );
bts: Disable PCU related tests if PCU socket not configured With some real HW setups, there's no PCU (osmo-pcu) available locally, for instance when using a sysmobts, or when using a nanoBTS. There's no need to waste time and generate extra output by running this tests in this case. Furthermore, some tests seem to crash sometimes in that setup, probably due to using invalid fd (-1): MTC@801a0da9866a: Setting RSL_SYSTEM_INFO_4 (4): '31061C62F224002A4740E50400'O /osmo-ttcn3-hacks/bts/BTS_Tests: Segmentation fault occurred /usr/lib/titan/libttcn3-parallel-dynamic.so(_Z14signal_handleri+0xa3)[0x7f0c33b48073] /lib/x86_64-linux-gnu/libc.so.6(+0x33060)[0x7f0c321aa060] /osmo-ttcn3-hacks/bts/UD_PT.so(_ZN12UD__PortType15UD__PT_PROVIDER13outgoing_sendERKN9UD__Types14UD__send__dataE+0xf0)[0x7f0c349e42f8] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_17PCUIF__send__dataERK9COMPONENT+0x19e)[0x7f0c37e1731a] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_26PCUIF__send__data_templateE+0x5f)[0x7f0c37e174f7] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests20f__TC__pcu__act__reqERK7INTEGERS2_S2_RK7BOOLEAN+0x411)[0x7f0c3eec3210] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests28testcase_TC__pcu__deact__reqEbd+0x15d)[0x7f0c3eec4f27] /osmo-ttcn3-hacks/bts/BTS_Tests.so(+0xfb65d)[0x7f0c3eeef65d] /usr/lib/titan/libttcn3-parallel-dynamic.so(_ZN11Module_List15execute_controlEPKc+0x1c)[0x7f0c33af3fbc] Change-Id: I773c7ec52dd8532bf160e92ffefc8d936ca55de2
2018-11-20 12:12:22 +00:00
if (mp_pcu_socket != "") {
execute( TC_paging_imsi_200percent_with_ps() );
bts: Disable PCU related tests if PCU socket not configured With some real HW setups, there's no PCU (osmo-pcu) available locally, for instance when using a sysmobts, or when using a nanoBTS. There's no need to waste time and generate extra output by running this tests in this case. Furthermore, some tests seem to crash sometimes in that setup, probably due to using invalid fd (-1): MTC@801a0da9866a: Setting RSL_SYSTEM_INFO_4 (4): '31061C62F224002A4740E50400'O /osmo-ttcn3-hacks/bts/BTS_Tests: Segmentation fault occurred /usr/lib/titan/libttcn3-parallel-dynamic.so(_Z14signal_handleri+0xa3)[0x7f0c33b48073] /lib/x86_64-linux-gnu/libc.so.6(+0x33060)[0x7f0c321aa060] /osmo-ttcn3-hacks/bts/UD_PT.so(_ZN12UD__PortType15UD__PT_PROVIDER13outgoing_sendERKN9UD__Types14UD__send__dataE+0xf0)[0x7f0c349e42f8] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_17PCUIF__send__dataERK9COMPONENT+0x19e)[0x7f0c37e1731a] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_26PCUIF__send__data_templateE+0x5f)[0x7f0c37e174f7] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests20f__TC__pcu__act__reqERK7INTEGERS2_S2_RK7BOOLEAN+0x411)[0x7f0c3eec3210] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests28testcase_TC__pcu__deact__reqEbd+0x15d)[0x7f0c3eec4f27] /osmo-ttcn3-hacks/bts/BTS_Tests.so(+0xfb65d)[0x7f0c3eeef65d] /usr/lib/titan/libttcn3-parallel-dynamic.so(_ZN11Module_List15execute_controlEPKc+0x1c)[0x7f0c33af3fbc] Change-Id: I773c7ec52dd8532bf160e92ffefc8d936ca55de2
2018-11-20 12:12:22 +00:00
execute( TC_pcu_act_req() );
execute( TC_pcu_act_req_wrong_ts() );
execute( TC_pcu_act_req_wrong_bts() );
execute( TC_pcu_act_req_wrong_trx() );
execute( TC_pcu_deact_req() );
execute( TC_pcu_deact_req_wrong_ts() );
execute( TC_pcu_ver_si1() );
execute( TC_pcu_ver_si3() );
bts: Disable PCU related tests if PCU socket not configured With some real HW setups, there's no PCU (osmo-pcu) available locally, for instance when using a sysmobts, or when using a nanoBTS. There's no need to waste time and generate extra output by running this tests in this case. Furthermore, some tests seem to crash sometimes in that setup, probably due to using invalid fd (-1): MTC@801a0da9866a: Setting RSL_SYSTEM_INFO_4 (4): '31061C62F224002A4740E50400'O /osmo-ttcn3-hacks/bts/BTS_Tests: Segmentation fault occurred /usr/lib/titan/libttcn3-parallel-dynamic.so(_Z14signal_handleri+0xa3)[0x7f0c33b48073] /lib/x86_64-linux-gnu/libc.so.6(+0x33060)[0x7f0c321aa060] /osmo-ttcn3-hacks/bts/UD_PT.so(_ZN12UD__PortType15UD__PT_PROVIDER13outgoing_sendERKN9UD__Types14UD__send__dataE+0xf0)[0x7f0c349e42f8] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_17PCUIF__send__dataERK9COMPONENT+0x19e)[0x7f0c37e1731a] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_26PCUIF__send__data_templateE+0x5f)[0x7f0c37e174f7] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests20f__TC__pcu__act__reqERK7INTEGERS2_S2_RK7BOOLEAN+0x411)[0x7f0c3eec3210] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests28testcase_TC__pcu__deact__reqEbd+0x15d)[0x7f0c3eec4f27] /osmo-ttcn3-hacks/bts/BTS_Tests.so(+0xfb65d)[0x7f0c3eeef65d] /usr/lib/titan/libttcn3-parallel-dynamic.so(_ZN11Module_List15execute_controlEPKc+0x1c)[0x7f0c33af3fbc] Change-Id: I773c7ec52dd8532bf160e92ffefc8d936ca55de2
2018-11-20 12:12:22 +00:00
execute( TC_pcu_ver_si13() );
if (mp_l1_supports_gprs) {
execute( TC_pcu_data_req_pdtch() );
execute( TC_pcu_data_req_ptcch() );
execute( TC_pcu_data_req_wrong_bts() );
execute( TC_pcu_data_req_wrong_trx() );
execute( TC_pcu_data_req_wrong_ts() );
execute( TC_pcu_data_req_ts_inactive() );
}
execute( TC_pcu_ptcch() );
bts: Disable PCU related tests if PCU socket not configured With some real HW setups, there's no PCU (osmo-pcu) available locally, for instance when using a sysmobts, or when using a nanoBTS. There's no need to waste time and generate extra output by running this tests in this case. Furthermore, some tests seem to crash sometimes in that setup, probably due to using invalid fd (-1): MTC@801a0da9866a: Setting RSL_SYSTEM_INFO_4 (4): '31061C62F224002A4740E50400'O /osmo-ttcn3-hacks/bts/BTS_Tests: Segmentation fault occurred /usr/lib/titan/libttcn3-parallel-dynamic.so(_Z14signal_handleri+0xa3)[0x7f0c33b48073] /lib/x86_64-linux-gnu/libc.so.6(+0x33060)[0x7f0c321aa060] /osmo-ttcn3-hacks/bts/UD_PT.so(_ZN12UD__PortType15UD__PT_PROVIDER13outgoing_sendERKN9UD__Types14UD__send__dataE+0xf0)[0x7f0c349e42f8] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_17PCUIF__send__dataERK9COMPONENT+0x19e)[0x7f0c37e1731a] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_26PCUIF__send__data_templateE+0x5f)[0x7f0c37e174f7] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests20f__TC__pcu__act__reqERK7INTEGERS2_S2_RK7BOOLEAN+0x411)[0x7f0c3eec3210] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests28testcase_TC__pcu__deact__reqEbd+0x15d)[0x7f0c3eec4f27] /osmo-ttcn3-hacks/bts/BTS_Tests.so(+0xfb65d)[0x7f0c3eeef65d] /usr/lib/titan/libttcn3-parallel-dynamic.so(_ZN11Module_List15execute_controlEPKc+0x1c)[0x7f0c33af3fbc] Change-Id: I773c7ec52dd8532bf160e92ffefc8d936ca55de2
2018-11-20 12:12:22 +00:00
execute( TC_pcu_data_req_agch() );
execute( TC_pcu_data_req_pch() );
bts: Disable PCU related tests if PCU socket not configured With some real HW setups, there's no PCU (osmo-pcu) available locally, for instance when using a sysmobts, or when using a nanoBTS. There's no need to waste time and generate extra output by running this tests in this case. Furthermore, some tests seem to crash sometimes in that setup, probably due to using invalid fd (-1): MTC@801a0da9866a: Setting RSL_SYSTEM_INFO_4 (4): '31061C62F224002A4740E50400'O /osmo-ttcn3-hacks/bts/BTS_Tests: Segmentation fault occurred /usr/lib/titan/libttcn3-parallel-dynamic.so(_Z14signal_handleri+0xa3)[0x7f0c33b48073] /lib/x86_64-linux-gnu/libc.so.6(+0x33060)[0x7f0c321aa060] /osmo-ttcn3-hacks/bts/UD_PT.so(_ZN12UD__PortType15UD__PT_PROVIDER13outgoing_sendERKN9UD__Types14UD__send__dataE+0xf0)[0x7f0c349e42f8] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_17PCUIF__send__dataERK9COMPONENT+0x19e)[0x7f0c37e1731a] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_26PCUIF__send__data_templateE+0x5f)[0x7f0c37e174f7] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests20f__TC__pcu__act__reqERK7INTEGERS2_S2_RK7BOOLEAN+0x411)[0x7f0c3eec3210] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests28testcase_TC__pcu__deact__reqEbd+0x15d)[0x7f0c3eec4f27] /osmo-ttcn3-hacks/bts/BTS_Tests.so(+0xfb65d)[0x7f0c3eeef65d] /usr/lib/titan/libttcn3-parallel-dynamic.so(_ZN11Module_List15execute_controlEPKc+0x1c)[0x7f0c33af3fbc] Change-Id: I773c7ec52dd8532bf160e92ffefc8d936ca55de2
2018-11-20 12:12:22 +00:00
execute( TC_pcu_data_req_imm_ass_pch() );
execute( TC_pcu_rach_content() );
execute( TC_pcu_ext_rach_content() );
execute( TC_pcu_data_ind_lqual_cb() );
bts: Disable PCU related tests if PCU socket not configured With some real HW setups, there's no PCU (osmo-pcu) available locally, for instance when using a sysmobts, or when using a nanoBTS. There's no need to waste time and generate extra output by running this tests in this case. Furthermore, some tests seem to crash sometimes in that setup, probably due to using invalid fd (-1): MTC@801a0da9866a: Setting RSL_SYSTEM_INFO_4 (4): '31061C62F224002A4740E50400'O /osmo-ttcn3-hacks/bts/BTS_Tests: Segmentation fault occurred /usr/lib/titan/libttcn3-parallel-dynamic.so(_Z14signal_handleri+0xa3)[0x7f0c33b48073] /lib/x86_64-linux-gnu/libc.so.6(+0x33060)[0x7f0c321aa060] /osmo-ttcn3-hacks/bts/UD_PT.so(_ZN12UD__PortType15UD__PT_PROVIDER13outgoing_sendERKN9UD__Types14UD__send__dataE+0xf0)[0x7f0c349e42f8] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_17PCUIF__send__dataERK9COMPONENT+0x19e)[0x7f0c37e1731a] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_26PCUIF__send__data_templateE+0x5f)[0x7f0c37e174f7] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests20f__TC__pcu__act__reqERK7INTEGERS2_S2_RK7BOOLEAN+0x411)[0x7f0c3eec3210] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests28testcase_TC__pcu__deact__reqEbd+0x15d)[0x7f0c3eec4f27] /osmo-ttcn3-hacks/bts/BTS_Tests.so(+0xfb65d)[0x7f0c3eeef65d] /usr/lib/titan/libttcn3-parallel-dynamic.so(_ZN11Module_List15execute_controlEPKc+0x1c)[0x7f0c33af3fbc] Change-Id: I773c7ec52dd8532bf160e92ffefc8d936ca55de2
2018-11-20 12:12:22 +00:00
execute( TC_pcu_paging_from_rsl() );
execute( TC_pcu_time_ind() );
execute( TC_pcu_rts_req() );
execute( TC_pcu_oml_alert() );
execute( TC_pcu_rr_suspend() );
execute( TC_pcu_socket_connect_multi() );
execute( TC_pcu_socket_reconnect() );
execute( TC_pcu_socket_noconnect_nosi3gprs() );
execute( TC_pcu_socket_noconnect_nosi4gprs() );
execute( TC_pcu_socket_connect_si3gprs() );
execute( TC_pcu_socket_connect_si4gprs() );
execute( TC_pcu_socket_disconnect_nosi3gprs() );
execute( TC_pcu_socket_disconnect_nosi4gprs() );
execute( TC_pcu_socket_verify_info_ind() );
execute( TC_dyn_osmo_pdch_act_deact() );
execute( TC_dyn_osmo_pdch_double_act() );
execute( TC_dyn_ipa_pdch_act_deact() );
execute( TC_dyn_ipa_pdch_act_tchf_act_nack() );
execute( TC_pcu_info_ind_fh_params() );
execute( TC_pcu_socket_nsvc_ipv4() );
execute( TC_pcu_socket_nsvc_ipv6() );
execute( TC_pcu_socket_two_nsvc() );
execute( TC_pcu_interf_ind() );
bts: Disable PCU related tests if PCU socket not configured With some real HW setups, there's no PCU (osmo-pcu) available locally, for instance when using a sysmobts, or when using a nanoBTS. There's no need to waste time and generate extra output by running this tests in this case. Furthermore, some tests seem to crash sometimes in that setup, probably due to using invalid fd (-1): MTC@801a0da9866a: Setting RSL_SYSTEM_INFO_4 (4): '31061C62F224002A4740E50400'O /osmo-ttcn3-hacks/bts/BTS_Tests: Segmentation fault occurred /usr/lib/titan/libttcn3-parallel-dynamic.so(_Z14signal_handleri+0xa3)[0x7f0c33b48073] /lib/x86_64-linux-gnu/libc.so.6(+0x33060)[0x7f0c321aa060] /osmo-ttcn3-hacks/bts/UD_PT.so(_ZN12UD__PortType15UD__PT_PROVIDER13outgoing_sendERKN9UD__Types14UD__send__dataE+0xf0)[0x7f0c349e42f8] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_17PCUIF__send__dataERK9COMPONENT+0x19e)[0x7f0c37e1731a] /osmo-ttcn3-hacks/bts/PCUIF_CodecPort.so(_ZN16PCUIF__CodecPort16PCUIF__CODEC__PT4sendERKNS_26PCUIF__send__data_templateE+0x5f)[0x7f0c37e174f7] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests20f__TC__pcu__act__reqERK7INTEGERS2_S2_RK7BOOLEAN+0x411)[0x7f0c3eec3210] /osmo-ttcn3-hacks/bts/BTS_Tests.so(_ZN10BTS__Tests28testcase_TC__pcu__deact__reqEbd+0x15d)[0x7f0c3eec4f27] /osmo-ttcn3-hacks/bts/BTS_Tests.so(+0xfb65d)[0x7f0c3eeef65d] /usr/lib/titan/libttcn3-parallel-dynamic.so(_ZN11Module_List15execute_controlEPKc+0x1c)[0x7f0c33af3fbc] Change-Id: I773c7ec52dd8532bf160e92ffefc8d936ca55de2
2018-11-20 12:12:22 +00:00
} else {
log("PCU socket path not available, skipping PCU tests");
}
execute( TC_dyn_osmo_pdch_unsol_deact() );
execute( TC_dyn_osmo_pdch_tchf_act() );
execute( TC_dyn_osmo_pdch_tchh_act() );
execute( TC_dyn_osmo_pdch_sdcch8_act() );
execute( TC_dyn_ipa_pdch_tchf_act() );
execute( TC_dyn_ipa_pdch_tchf_act_pdch_act_nack() );
execute( TC_rll_est_ind() );
execute( TC_rll_est_req_DCCH_3() );
execute( TC_rll_est_req_ACCH_3() );
execute( TC_rll_rel_ind_DCCH_0() );
execute( TC_rll_rel_ind_DCCH_3() );
execute( TC_rll_rel_ind_ACCH_0() );
execute( TC_rll_rel_ind_ACCH_3() );
execute( TC_rll_rel_req() );
execute( TC_rll_unit_data_req_DCCH() );
execute( TC_rll_unit_data_req_ACCH() );
execute( TC_rll_unit_data_ind_DCCH() );
execute( TC_rll_unit_data_ind_ACCH() );
execute( TC_chan_act_a51() );
execute( TC_chan_act_a52() );
execute( TC_chan_act_a53() );
execute( TC_encr_cmd_a51() );
execute( TC_encr_cmd_a52() );
execute( TC_encr_cmd_a53() );
execute( TC_err_rep_wrong_mdisc() );
execute( TC_err_rep_wrong_msg_type() );
execute( TC_err_rep_wrong_sequence() );
execute( TC_lapdm_selftest() );
execute( TC_tch_sign_l2_fill_frame() );
execute( TC_tch_sign_l2_fill_frame_dtxd() );
execute( TC_chopped_ipa_ping() );
execute( TC_chopped_ipa_payload() );
execute( TC_ms_pwr_ctrl_constant() );
execute( TC_ms_pwr_ctrl_pf_ewma() );
execute( TC_speech_no_rtp_tchf() );
execute( TC_speech_no_rtp_tchh() );
execute( TC_speech_rtp_tchf() );
execute( TC_speech_rtp_tchh() );
execute( TC_early_immediate_assignment() );
execute( TC_acch_overpower_rxqual_thresh() );
execute( TC_acch_overpower_rxqual_thresh_dtx() );
execute( TC_acch_overpower_always_on_facch() );
execute( TC_acch_overpower_always_on_sacch() );
execute( TC_acch_overpower_limit() );
/* BEWARE: these test cases can potentially break the IUT or cause
* weird/unexpected behavior. Ensure that they are executed last. */
execute( TC_dyn_osmo_pdch_tchh_race_act() );
execute( TC_dyn_osmo_pdch_sdcch8_race_act() );
}
}