osmo-ttcn3-hacks/bts/BTS_Tests.ttcn

5256 lines
170 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_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 Osmocom_CTRL_Adapter all;
import from Osmocom_CTRL_Functions all;
import from RSL_Types all;
import from IPA_Types all;
import from IPA_Emulation all;
import from IPA_Testing all;
import from RSL_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 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;
friend module BTS_Tests_SMSCB;
friend module BTS_Tests_virtphy;
/* 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;
integer mp_trx0_arfcn := 871;
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;
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_ms_actual_ta_exp := 0;
integer mp_timing_offset_256syms_exp := 512;
/* 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;
}
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;
/* TRXC port (for classic tests) */
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 {
port L1CTL_PT L1CTL;
port TRXC_CODEC_PT BTS_TRXC;
var integer g_bts_trxc_conn_id;
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;
}
function f_init_rsl(charstring id) runs on test_CT {
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));
}
type record ConnHdlrPars {
RslChannelNr chan_nr,
RSL_IE_ChannelMode chan_mode,
float t_guard,
ConnL1Pars l1_pars,
TestSpecUnion spec optional,
RSL_IE_EncryptionInfo encr optional
}
/* Test-specific parameters */
type union TestSpecUnion {
RllTestCase rll
}
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
};
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
}
template (value) LocationAreaIdentification ts_LAI_default := {
mcc_mnc := '262F42'H,
lac := 42
}
/* Default SYSTEM INFORMATION 3 */
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 := '2B2B2B2B'O
}
}
}
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
}
}
}
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 := ''O
}
}
}
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_RSL_UD(ts_RSL_BCCH_INFO(rsl_si_type, si_enc)));
}
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");
}
/* PCU socket may at any time receive a new INFO.ind */
private 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");
}
}
}
/* global init function */
function f_init(charstring id := "BTS-Test") runs on test_CT {
timer T := mp_ipa_up_timeout;
g_AllChannels := {
/* TS 1..4: TCH/F */
valueof(ts_RslChanNr_Bm(1)), valueof(ts_RslChanNr_Bm(2)),
valueof(ts_RslChanNr_Bm(3)), valueof(ts_RslChanNr_Bm(4)),
/* TS 5: TCH/H */
valueof(ts_RslChanNr_Lm(5,0)), valueof(ts_RslChanNr_Lm(5,1)),
/* TS 0: SDCCH/4 */
valueof(ts_RslChanNr_SDCCH4(0,0)), valueof(ts_RslChanNr_SDCCH4(0,1)),
valueof(ts_RslChanNr_SDCCH4(0,2)), valueof(ts_RslChanNr_SDCCH4(0,3)),
/* TS 6: SDCCH/8 */
valueof(ts_RslChanNr_SDCCH8(6,0)), valueof(ts_RslChanNr_SDCCH8(6,1)),
valueof(ts_RslChanNr_SDCCH8(6,2)), valueof(ts_RslChanNr_SDCCH8(6,3)),
valueof(ts_RslChanNr_SDCCH8(6,4)), valueof(ts_RslChanNr_SDCCH8(6,5)),
valueof(ts_RslChanNr_SDCCH8(6,6)), valueof(ts_RslChanNr_SDCCH8(6,7))
};
/* FIXME: FACCH/H is unreliable with calypso firmware, see OS#3653 */
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))
};
}
f_init_rsl(id);
T.start;
alt {
[] RSL_CCHAN.receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP});
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for ASP_IPA_EVENT_UP");
}
}
f_sleep(0.5); /* workaround for OS#3000 */
f_init_vty(id);
f_ipa_ctrl_start(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);
map(self:PCU, system:PCU);
f_init_pcu(PCU, id, g_pcu_conn_id, g_pcu_last_info);
if (mp_bts_trxc_port != -1) {
var TrxcMessage ret;
/* start with a default moderate timing offset equalling TA=2 */
f_main_trxc_connect();
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, valueof(ts_TRXC_FAKE_TIMING(2*256)));
}
/* 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) */
function f_init_l1ctl() runs on test_CT {
map(self:L1CTL, system:L1CTL);
f_connect_reset(L1CTL);
}
type function void_fn(charstring id) runs on ConnHdlr;
/* create a new test component */
function f_start_handler(void_fn fn, ConnHdlrPars pars, boolean pcu_comp := false)
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);
if (pcu_comp) {
/* the ConnHdlr component wants to talk directly to the PCU, so disconnect
* it from the test_CT and connect it to the component. This obviously only
* works for one component, i.e. no concurrency */
unmap(self:PCU, system:PCU);
map(vc_conn:PCU, system:PCU);
}
vc_conn.start(f_handler_init(fn, id, pars));
return vc_conn;
}
template ASP_RSL_Unitdata ts_RSL_UD(template RSL_Message rsl, IpaStreamId sid := IPAC_PROTO_RSL_TRX0) := {
streamId := sid,
rsl := rsl
}
template ASP_RSL_Unitdata tr_RSL_UD(template RSL_Message rsl,
template IpaStreamId sid := IPAC_PROTO_RSL_TRX0) := {
streamId := sid,
rsl := rsl
}
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) {
f_L1CTL_FBSB(L1CTL, { false, mp_trx0_arfcn }, CCCH_MODE_COMBINED, mp_rxlev_exp);
}
private function f_trxc_connect() runs on ConnHdlr {
map(self:BTS_TRXC, system:BTS_TRXC);
var Result res;
res := TRXC_CodecPort_CtrlFunct.f_IPL4_connect(BTS_TRXC, 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 trx-control interface of trxcon, check your configuration");
}
g_bts_trxc_conn_id := res.connId;
}
private function f_trxc_fake_rssi(uint8_t rssi) runs on ConnHdlr {
var TrxcMessage ret;
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, valueof(ts_TRXC_FAKE_RSSI(rssi)));
}
private function f_trx_fake_toffs256(int16_t toffs256) runs on ConnHdlr {
var TrxcMessage ret;
ret := f_TRXC_transceive(BTS_TRXC, g_bts_trxc_conn_id, valueof(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;
map(self:L1CTL, system:L1CTL);
f_connect_reset(L1CTL);
if (mp_bts_trxc_port != -1) {
f_trxc_connect();
}
g_Tguard.start(pars.t_guard);
activate(as_Tguard());
f_rslem_register(0, pars.chan_nr);
/* call the user-supplied test case function */
fn.apply(id);
}
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();
[not ignore_other] RSL.receive {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected RSL message received");
}
[ignore_other] RSL.receive { repeat; }
}
return rx;
}
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);
}
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");
}
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);
}
private template ConnHdlrPars t_Pars(template RslChannelNr chan_nr,
template RSL_IE_ChannelMode chan_mode,
float t_guard := 20.0) := {
chan_nr := valueof(chan_nr),
chan_mode := valueof(chan_mode),
t_guard := t_guard,
l1_pars := {
dtx_enabled := false,
toa256_enabled := false,
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
},
spec := omit,
encr := omit
}
/***********************************************************************
* Channel Activation / Deactivation
***********************************************************************/
/* Stress test: Do 500 channel activations/deactivations in rapid succession */
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(testcasename());
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 */
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 */
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 */
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 */
private function f_testmatrix_each_chan(ConnHdlrPars pars, void_fn fn) runs on test_CT {
var ConnHdlr vc_conn;
f_init(testcasename());
/* 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);
f_l1_tune(L1CTL);
RSL.clear;
/* Step 1: Activate ASYNC HO channel without MS power IE */
/* Activate channel on BTS side */
f_rsl_chan_act(g_pars.chan_mode, 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_DM_EST_REQ(L1CTL, {false, mp_trx0_arfcn }, g_pars.chan_nr, 7);
/* Verify that no DL SACCH is being received */
f_sacch_missing(?);
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 */
var RSL_IE_List addl_ies := {
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_DM_EST_REQ(L1CTL, {false, mp_trx0_arfcn }, g_pars.chan_nr, 7);
/* Verify that DL SACCH is being received */
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);
var RSL_IE_List addl_ies;
f_l1_tune(L1CTL);
RSL.clear;
/* Step 1: Activate SYNC HO channel without MS power IE */
/* Activate channel on BTS side */
f_rsl_chan_act(g_pars.chan_mode, 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_DM_EST_REQ(L1CTL, {false, mp_trx0_arfcn }, g_pars.chan_nr, 7);
/* Verify that no DL SACCH is being received */
f_sacch_missing(?);
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 := {
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_DM_EST_REQ(L1CTL, {false, mp_trx0_arfcn }, g_pars.chan_nr, 7);
/* Verify that no DL SACCH is being received */
f_sacch_missing(?);
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 := {
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_DM_EST_REQ(L1CTL, {false, mp_trx0_arfcn }, g_pars.chan_nr, 7);
/* Verify that no DL SACCH is being received */
f_sacch_missing(?);
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 := {
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_DM_EST_REQ(L1CTL, {false, mp_trx0_arfcn }, g_pars.chan_nr, 7);
/* Verify that DL SACCH is being received */
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
***********************************************************************/
/* like L1SAP_IS_PACKET_RACH */
private function ra_is_ps(OCT1 ra) return boolean {
if ((ra and4b 'F0'O == '70'O) and (ra and4b '0F'O != '0F'O)) {
return true;
}
return false;
}
/* generate a random RACH for circuit-switched */
private function f_rnd_ra_cs() return OCT1 {
var OCT1 ra;
do {
ra := f_rnd_octstring(1);
} while (ra_is_ps(ra));
return ra;
}
/* generate a random RACH for packet-switched */
private function f_rnd_ra_ps() return OCT1 {
var OCT1 ra;
do {
ra := f_rnd_octstring(1);
} while (not ra_is_ps(ra));
return ra;
}
/* generate a random 11-bit RA (packet-switched only) */
private function f_rnd_ra11_ps() return BIT11 {
var integer ra11 := f_rnd_int(bit2int('11111111111'B));
return int2bit(ra11, 11);
}
/* Send 1000 RACH requests and check their RA+FN on the RSL side */
testcase TC_rach_content() runs on test_CT {
f_init(testcasename());
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 := 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_RSL_UD(tr_RSL_CHAN_RQD(ra, fn, ?))) {
T.stop;
}
[] RSL_CCHAN.receive(tr_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__);
}
/* 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_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();
timer T := 5.0;
T.start;
alt {
[] RSL_CCHAN.receive(tr_RSL_UD(tr_RSL_RACH_LOAD_IND(tr_rach_slots_per_interval, 0, 0))) {
setverdict(pass);
repeat;
}
[] RSL_CCHAN.receive(tr_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_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);
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_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_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_main_trxc_connect() runs on test_CT {
map(self:BTS_TRXC, system:BTS_TRXC);
var Result res;
res := TRXC_CodecPort_CtrlFunct.f_IPL4_connect(BTS_TRXC, 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 trx-control interface of trxcon, check your configuration");
}
g_bts_trxc_conn_id := res.connId;
}
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, valueof(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_RSL_UD(tr_RSL_CHAN_RQD(ra, fn))) {
setverdict(pass);
}
[not expect_pass] RSL_CCHAN.receive(tr_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(testcasename());
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__);
}
/***********************************************************************
* Measurement Processing / Reporting
***********************************************************************/
template LapdmAddressField ts_LapdmAddr(LapdmSapi sapi, boolean c_r) := {
spare := '0'B,
lpd := 0,
sapi := sapi,
c_r := c_r,
ea := true
}
template LapdmFrameAB ts_LAPDm_AB(LapdmSapi sapi, boolean c_r, boolean p, octetstring pl) := {
addr := ts_LapdmAddr(sapi, c_r),
ctrl := ts_LapdmCtrlUI(p),
len := 0, /* overwritten */
m := false,
el := 1,
payload := pl
}
/* handle incoming downlink SACCH and respond with uplink SACCH (meas res) */
altstep as_l1_sacch() runs on ConnHdlr {
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);
var GsmRrL3Message meas_rep := valueof(ts_MEAS_REP(true, mp_rxlev_exp, mp_rxlev_exp, 0, 0, omit));
var LapdmFrameAB lb := valueof(ts_LAPDm_AB(0, false, false, enc_GsmRrL3Message(meas_rep)));
log("LAPDm: ", lb);
var L1ctlDataReq data_req := {
l1header := valueof(ts_SacchL1Header(g_pars.l1_pars.ms_power_level, false, g_pars.l1_pars.ms_actual_ta)),
l2_payload := f_pad_oct(enc_LapdmFrameAB(lb), 21, '2B'O)
}
log("Sending Measurement Report: ", data_req);
L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(g_chan_nr, ts_RslLinkID_SACCH(0), data_req));
repeat;
}
}
altstep as_l1_dcch() 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;
}
}
type record MeasElem {
uint6_t rxlev,
uint3_t rxqual
}
type record MeasElemFS {
MeasElem full,
MeasElem sub
}
type record ConnL1Pars {
boolean dtx_enabled,
boolean toa256_enabled,
MeasElemFS meas_ul,
int16_t timing_offset_256syms,
uint5_t bs_power_level,
uint5_t ms_power_level,
uint8_t ms_actual_ta
}
/* 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);
}
/* verify we regularly receive measurement reports with incrementing numbers */
altstep as_meas_res(boolean verify_meas := true) runs on ConnHdlr {
var RSL_Message rsl;
[not verify_meas] RSL.receive(tr_RSL_MEAS_RES(?)) { repeat; }
[] 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);
repeat;
}
[] 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;
}
/* Establish dedicated channel: L1CTL + RSL side */
private function f_est_dchan(boolean encr_enable := false, RSL_IE_List more_ies := {}) runs on ConnHdlr {
var GsmFrameNumber fn;
var ImmediateAssignment imm_ass;
var integer ra := 23;
/* Send RACH request and wait for ChanReq */
fn := f_rach_req_wait_chan_rqd(ra);
/* Activate channel on BTS side */
f_rsl_chan_act(g_pars.chan_mode, encr_enable, more_ies);
/* Send IMM.ASS via CCHAN */
var ChannelDescription ch_desc := {
chan_nr := g_pars.chan_nr,
tsc := 7,
h := false,
arfcn := mp_trx0_arfcn,
maio_hsn := omit
};
var MobileAllocation ma := {
len := 0,
ma := ''B
};
var GsmRrMessage rr_msg := valueof(ts_IMM_ASS(ra, fn, 0, ch_desc, ma));
RSL.send(ts_RSL_IMM_ASSIGN(enc_GsmRrMessage(rr_msg)));
/* receive IMM.ASS on MS side */
var ImmediateAssignment ia_um;
ia_um := f_L1CTL_WAIT_IMM_ASS(L1CTL, ra, fn);
/* enable dedicated mode */
f_L1CTL_DM_EST_REQ_IA(L1CTL, ia_um);
/* 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);
}
g_first_meas_res := true;
}
/* establish DChan, verify existance + contents of measurement reports */
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_trx_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();
[] 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);
}
testcase TC_meas_res_sign_tchf() runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
f_init(testcasename());
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_meas_res_periodic), pars);
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(testcasename());
for (var integer ss := 0; ss <= 1; ss := ss+1) {
pars := valueof(t_Pars(t_RslChanNr_Lm(5, ss), ts_RSL_ChanMode_SIGN));
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), pars);
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(testcasename());
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);
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(testcasename());
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);
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(testcasename());
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));
pars.l1_pars.toa256_enabled := true;
vc_conn := f_start_handler(refers(f_TC_meas_res_periodic), 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. */
function f_tc_rsl_ms_pwr_ctrl(charstring id) runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
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 {
/* Pick all SACCH blocks for checking */
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(?))) -> value l1_dl {
/* The first byte of the L1 header contains the power level.
* The reserved bits and the fpc bit is set to 0, so we may
* compare directly. */
if (not (l1_dl.payload.data_ind.payload[0] == int2oct(power_level, 1))) {
setverdict(fail, "Power level in L1 header does not match the signaled (RSL) power level.");
}
/* 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(testcasename());
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! */
function f_tc_rsl_chan_initial_ta(charstring id) runs on ConnHdlr {
var L1ctlDlMessage l1_dl;
var uint5_t ta_to_test := 16;
f_l1_tune(L1CTL);
RSL.clear;
/* tell fake_trx to use a given timing offset for all bursts */
f_trx_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 {
/* Pick all SACCH blocks for checking */
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, tr_RslLinkID_SACCH(?))) -> value l1_dl {
/* The second byte of the L1 header contains the TA. */
if (not (l1_dl.payload.data_ind.payload[1] == int2oct(ta_to_test, 1))) {
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(testcasename());
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);
vc_conn.done;
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
/* 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(testcasename());
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
***********************************************************************/
function tmsi_is_dummy(TMSIP_TMSI_V tmsi) return boolean {
if (tmsi == 'FFFFFFFF'O) {
return true;
} else {
return false;
}
}
type record allowedFn { integer frame_nr }
template allowedFn bs_ag_blks_res_0 := { frame_nr := (6, 12, 16, 22, 26, 32, 36, 42, 46) }
template allowedFn bs_ag_blks_res_1 := { frame_nr := (12, 16, 22, 26, 32, 36, 42, 46) }
template allowedFn bs_ag_blks_res_2 := { frame_nr := (16, 22, 26, 32, 36, 42, 46) }
template allowedFn bs_ag_blks_res_3 := { frame_nr := (22, 26, 32, 36, 42, 46) }
template allowedFn bs_ag_blks_res_4 := { frame_nr := (26, 32, 36, 42, 46) }
template allowedFn bs_ag_blks_res_5 := { frame_nr := (32, 36, 42, 46) }
template allowedFn bs_ag_blks_res_6 := { frame_nr := (36, 42, 46) }
template allowedFn bs_ag_blks_res_7 := { frame_nr := (42, 46) }
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;
}
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)) {
num_paging_rcv_msgs := num_paging_rcv_msgs + 1;
num_paging_rcv_ids := num_paging_rcv_ids + 1;
if (isvalue(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 (isvalue(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;
}
}
type record PagingTestCfg {
boolean combined_ccch,
integer bs_ag_blks_res,
float load_factor,
boolean exp_load_ind,
boolean exp_overload,
boolean use_tmsi
}
type record PagingTestState {
integer num_paging_sent,
integer num_paging_rcv_msgs,
integer num_paging_rcv_ids,
integer num_overload
}
/* receive + ignore RSL RF RES IND */
altstep as_rsl_res_ind() runs on test_CT {
[] RSL_CCHAN.receive(tr_RSL_UD(tr_RSL_RF_RES_IND)) {
repeat;
}
}
/* Helper function for paging related testing */
private function f_TC_paging(PagingTestCfg cfg) runs on test_CT return PagingTestState {
f_init(testcasename());
f_init_l1ctl();
f_l1_tune(L1CTL);
var PagingTestState st := {
num_paging_sent := 0,
num_paging_rcv_msgs := 0,
num_paging_rcv_ids := 0,
num_overload := 0
};
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_RSL_UD(tr_RSL_PAGING_LOAD_IND(0))) {
st.num_overload := st.num_overload + 1;
repeat;
}
[not cfg.exp_overload] RSL_CCHAN.receive(tr_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_RSL_UD(tr_RSL_PAGING_LOAD_IND)) {
log("Rx LOAD_IND");
/* FIXME: analyze/verify interval + contents */
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; }
[] 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) {
/* build mobile Identity */
var MobileL3_CommonIE_Types.MobileIdentityLV mi;
if (cfg.use_tmsi) {
mi := valueof(ts_MI_TMSI_LV(f_rnd_octstring(4)));
} else {
mi := valueof(ts_MI_IMSI_LV(f_gen_imsi(st.num_paging_sent)));
}
var octetstring mi_enc_lv := enc_MobileIdentityLV(mi);
var octetstring mi_enc := substr(mi_enc_lv, 1, lengthof(mi_enc_lv)-1);
/* Send RSL PAGING COMMAND */
RSL_CCHAN.send(ts_RSL_UD(ts_RSL_PAGING_CMD(mi_enc, st.num_paging_sent mod 4)));
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 { }
[] as_rsl_res_ind();
}
}
/* wait for max 18s for paging queue to drain (size: 200, ~ 13 per s -> 15s) */
timer T_wait := 18.0;
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_RSL_UD(tr_RSL_PAGING_LOAD_IND(65535))) { }
[] RSL_CCHAN.receive(tr_RSL_UD(tr_RSL_PAGING_LOAD_IND)) { repeat; }
[] T_wait.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Waiting for empty paging queue");
}
[] as_rsl_res_ind();
}
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,
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,
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,
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,
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__);
}
/***********************************************************************
* Immediate Assignment / AGCH
***********************************************************************/
const MobileAllocation c_MA_null := {
len := 0,
ma := ''B
}
template (value) ChannelDescription ts_ChanDesc(template (value) RslChannelNr chan_nr, uint3_t tsc := 7,
uint12_t arfcn := 871) := {
chan_nr := chan_nr,
tsc := tsc,
h := false,
arfcn := arfcn,
maio_hsn := omit
}
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(testcasename());
f_init_l1ctl();
f_l1_tune(L1CTL);
for (var integer i := 0; i < num_total; i := i+1) {
var ChannelDescription ch_desc := valueof(ts_ChanDesc(valueof(t_RslChanNr_SDCCH4(0, 0))));
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_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_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 {
/* somehow dec_SystemInformation will try to decode even non-RR as SI */
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 */
type record SystemInformationFn {
GsmFrameNumber frame_number,
SystemInformation si
}
/* an arbitrary-length vector of decoded SI + gsmtap header */
type record of SystemInformationFn SystemInformationVector;
/* an array of SI-vectors indexed by TC value */
type SystemInformationVector SystemInformationVectorPerTc[8];
/* determine if a given SI vector contains given SI type at least once */
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 */
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 */
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 */
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");
}
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 */
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 */
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 */
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 */
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 */
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 */
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 {
/* somehow dec_SystemInformation will try to decode even non-RR as SI */
if (not (l1_dl.payload.data_ind.payload[1] == '06'O)) {
log("Ignoring non-RR SI ", l1_dl);
repeat;
}
var SystemInformationFn sig := {
frame_number := l1_dl.dl_info.frame_nr,
si := dec_SystemInformation(l1_dl.payload.data_ind.payload)
}
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! */
function f_TC_si_sched() 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);
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, '050607a8a0364aa698d72ff424feee0506d5e7fff02043'O);
f_TC_si_sched();
Misc_Helpers.f_shutdown(__BFILE__, __LINE__);
}
testcase TC_si_sched_13() runs on test_CT {
f_init();
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 {
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);
si_cfg.si2quater_present := true;
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_2quater, '050607a8a0364aa698d72ff424feee0506d5e7fff02043'O);
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_bcch_info() runs on test_CT {
f_init(testcasename());
/* 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_RSL_UD(tr_RSL_ERROR_REPORT(cause))) {
setverdict(pass);
}
[] RSL_CCHAN.receive(tr_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(testcasename());
var RSL_Message rsl := valueof(ts_RSL_BCCH_INFO(RSL_SYSTEM_INFO_1, ''O));
rsl.ies := omit;
RSL_CCHAN.send(ts_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(testcasename());
var RSL_Message rsl := valueof(ts_RSL_BCCH_INFO(RSL_SYSTEM_INFO_1, ''O));
rsl.ies := { rsl.ies[0] };
RSL_CCHAN.send(ts_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(testcasename());
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_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(testcasename());
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 */
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);
}
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(testcasename());
/* 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_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_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) */
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(testcasename());
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 */
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(testcasename());
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 */
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(testcasename());
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 */
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 uint32_t remote_ip := f_rnd_int(c_UINT32_MAX);
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(testcasename());
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 */
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 uint32_t remote_ip := f_rnd_int(c_UINT32_MAX);
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_int(c_UINT32_MAX);
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(testcasename());
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) */
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(testcasename());
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)));
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_pcu_test() runs on test_CT {
f_init();
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_pcu_test();
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_pcu_test();
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_pcu_test();
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_pcu_test();
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_pcu_test();
/* 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_pcu_test();
f_TC_pcu_deact_req(0, 0, 1);
}
/* Test the PCU->BTS Version and BTS->PCU SI13 handshake */
testcase TC_pcu_ver_si13() runs on test_CT {
const octetstring si13 := '00010203040506070909'O;
var PCUIF_send_data sd;
timer T:= 3.0;
f_init_pcu_test();
/* Set SI13 via RSL */
f_rsl_bcch_fill_raw(RSL_SYSTEM_INFO_13, si13);
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(si13)) == si13) {
setverdict(pass);
} else {
repeat;
}
}
[] PCU.receive { repeat; }
[] T.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for SI13");
}
}
}
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_pcu_test();
}
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);
}
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);
}
/* 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_pcu_test();
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_pcu_test();
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_pcu_test();
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_pcu_test();
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, 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_pcu_test();
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,
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_pad_oct(octetstring str, integer len, OCT1 pad) return octetstring {
var integer strlen := lengthof(str);
for (var integer i := 0; i < len-strlen; i := i+1) {
str := str & pad;
}
return str;
}
/* Send PAGING via RSL, expect it to shw up on PCU socket */
testcase TC_pcu_paging_from_rsl() runs on test_CT {
f_init_pcu_test();
for (var integer i := 0; i < 100; i := i+1) {
var MobileL3_CommonIE_Types.MobileIdentityLV mi;
timer T := 3.0;
if (i < 50) {
mi := valueof(ts_MI_TMSI_LV(f_rnd_octstring(4)));
} else {
mi := valueof(ts_MI_IMSI_LV(f_gen_imsi(i)));
}
var octetstring mi_enc_lv := enc_MobileIdentityLV(mi);
var octetstring mi_enc := substr(mi_enc_lv, 1, lengthof(mi_enc_lv)-1);
var octetstring t_mi_lv := f_pad_oct(mi_enc_lv, 9, '00'O);
/* Send RSL PAGING COMMAND */
RSL_CCHAN.send(ts_RSL_UD(ts_RSL_PAGING_CMD(mi_enc, i mod 4)));
T.start;
alt {
[] PCU.receive(t_SD_PCUIF(g_pcu_conn_id, tr_PCUIF_PAG_REQ(0, t_mi_lv))) {
}
[] 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_pcu_test();
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, 10);
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_pcu_test();
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, 10);
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_pcu_test();
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(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);
}
template (value) PDU_ML3_MS_NW ts_RRM_GprsSuspReq(template (value) OCT4 tlli,
template (value) RoutingAreaIdentificationV rai,
template (value) OCT1 cause) := {
discriminator := '0000'B, /* overwritten */
tiOrSkip := {
skipIndicator := '0000'B
},
msgs := {
rrm := {
gPRS_suspensionRequest := {
messageType := '00110100'B,
tLLI := tlli,
routingAreaIdentification := rai,
suspensionCause := cause,
service_Support := omit
}
}
}
}
/* 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");
}
}
}
testcase TC_pcu_rr_suspend() runs on test_CT {
var ConnHdlrPars pars;
var ConnHdlr vc_conn;
f_init();
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;
}
/***********************************************************************
* Osmocom Style Dynamic Timeslot Support
***********************************************************************/
private function f_dyn_osmo_pdch_act(integer pcu_conn_id, integer bts_nr, integer trx_nr)
runs on ConnHdlr {
var PCUIF_send_data sd;
/* 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 */
alt {
[] PCU.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_INFO_IND(bts_nr, ?))) -> value sd {
if (substr(sd.data.u.info_ind.trx[trx_nr].pdch_mask, g_chan_nr.tn, 1) != '1'B) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "PCUIF_INFO_IND PDCH_MASK not '1' after PDCH ACT");
}
}
[] PCU.receive { repeat; }
}
/* 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 {
var PCUIF_send_data sd;
/* 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 */
alt {
[] PCU.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_INFO_IND(bts_nr, ?))) -> value sd {
if (substr(sd.data.u.info_ind.trx[trx_nr].pdch_mask, g_chan_nr.tn, 1) != '0'B) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "PCUIF_INFO_IND PDCH_MASK not '0' after PDCH DEACT");
}
}
[] PCU.receive { repeat; }
}
/* 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 */
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(testcasename());
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 */
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(testcasename());
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 */
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(testcasename());
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 */
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(testcasename());
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 */
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(testcasename());
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;
}
/***********************************************************************
* 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 {
var PCUIF_send_data sd;
/* 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 {
[] PCU.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_INFO_IND(bts_nr, ?))) -> value sd {
if (substr(sd.data.u.info_ind.trx[trx_nr].pdch_mask, g_chan_nr.tn, 1) != '1'B) {
log("PCUIF_INFO_IND PDCH_MASK not yet '1' after PDCH ACT on TS", g_chan_nr.tn,
" mask:", sd.data.u.info_ind.trx[trx_nr].pdch_mask);
repeat;
}
}
[] 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 {
var PCUIF_send_data sd;
/* 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 {
[] PCU.receive(t_SD_PCUIF(pcu_conn_id, tr_PCUIF_INFO_IND(bts_nr, ?))) -> value sd {
if (substr(sd.data.u.info_ind.trx[trx_nr].pdch_mask, g_chan_nr.tn, 1) != '0'B) {
log("PCUIF_INFO_IND PDCH_MASK not yet '0' after PDCH DEACT on TS", g_chan_nr.tn,
" mask:", sd.data.u.info_ind.trx[trx_nr].pdch_mask);
repeat;
}
}
[] 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 */
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 */
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(testcasename());
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 */
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(testcasename());
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 */
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(testcasename());
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));
if (valueof(link_id.c) == SACCH) {
/* prepend dummy L1 header */
var L1ctlDataReq l1hl2 := {
l1header := valueof(ts_SacchL1Header(g_pars.l1_pars.ms_power_level, false, g_pars.l1_pars.ms_actual_ta)),
l2_payload := f_pad_oct(l2, 21, '2B'O)
}
L1CTL.send(ts_L1CTL_DATA_REQ_SACCH(g_chan_nr, link_id, l1hl2));
} else {
/* If required, pad L2 frame with constant 0x2b filling */
l2 := f_pad_oct(l2, 23, '2B'O);
log("encoding ", l, " to ", l2);
L1CTL.send(ts_L1CTL_DATA_REQ(g_chan_nr, link_id, l2));
}
}
type record RllTestCase {
uint3_t sapi,
RslLinkId link_id,
octetstring l3,
boolean exp
}
type record of RllTestCase RllTestCases;
template RllTestCase t_EITC(uint3_t sapi, 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(testcasename());
/* 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, valueof(ts_RslLinkID_DCCH(0)), '01020304'O, true)),
/* normal SAPI0 establishment */
valueof(t_EITC(0, valueof(ts_RslLinkID_DCCH(0)), ''O, true)),
/* SAPI 3 doesn't support contention resolution */
valueof(t_EITC(3, valueof(ts_RslLinkID_DCCH(3)), '01020304'O, false)),
valueof(t_EITC(3, valueof(ts_RslLinkID_SACCH(3)), '01020304'O, false)),
/* normal SAPI3 establishment on main DCCH */
valueof(t_EITC(3, valueof(ts_RslLinkID_DCCH(3)), ''O, true)),
/* normal SAPI3 establishment on SACCH */
valueof(t_EITC(3, valueof(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 {
[] 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);
}
lapdm.ab := dec_LapdmFrameAB(l2);
if (match(lapdm, tr_LAPDm_SABM(tc.sapi, cr_MT_CMD, true, ''O))) {
setverdict(pass);
} else {
repeat;
}
}
[] L1CTL.receive { repeat; }
[] 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, valueof(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, valueof(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 */
private 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, valueof(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, valueof(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, valueof(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, valueof(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, valueof(ts_RslLinkID_DCCH(0)), '01020304'O, true)),
valueof(t_EITC(0, valueof(ts_RslLinkID_SACCH(0)), ''O, true)),
valueof(t_EITC(3, valueof(ts_RslLinkID_DCCH(3)), ''O, true)),
valueof(t_EITC(3, valueof(ts_RslLinkID_SACCH(3)), ''O, true))
};
f_rll_testmatrix(tcs, refers(f_TC_rll_rel_req));
}
/* test if RLL DATA REQ triggers I-frames on Um (TS 48.058 3.5) */
testcase TC_rll_data_req() runs on test_CT {
}
/* 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, valueof(ts_RslLinkID_DCCH(0)), l3, true)),
valueof(t_EITC(3, valueof(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, valueof(ts_RslLinkID_SACCH(0)), l3, true)),
valueof(t_EITC(3, valueof(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, valueof(ts_RslLinkID_DCCH(0)), l3, true)),
valueof(t_EITC(3, valueof(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, valueof(ts_RslLinkID_SACCH(0)), l3, true)),
valueof(t_EITC(3, valueof(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 */
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) */
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));
}
/* Test unencrypted channel activation followed by explicit ENCR CMD later */
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));
}
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)
***********************************************************************/
/* XXX These functions must be kept in sync with g_AllChannels defined on test_CT. */
function f_g_chan_is_tchf() runs on ConnHdlr return boolean {
return (g_chan_nr == valueof(ts_RslChanNr_Bm(1)) or
g_chan_nr == valueof(ts_RslChanNr_Bm(2)) or
g_chan_nr == valueof(ts_RslChanNr_Bm(3)) or
g_chan_nr == valueof(ts_RslChanNr_Bm(4)));
}
function f_g_chan_is_tchh() runs on ConnHdlr return boolean {
return (g_chan_nr == valueof(ts_RslChanNr_Lm(5,0)) or
g_chan_nr == valueof(ts_RslChanNr_Lm(5,1)));
}
function f_g_chan_is_sdcch4() runs on ConnHdlr return boolean {
return (g_chan_nr == valueof(ts_RslChanNr_SDCCH4(0,0)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH4(0,1)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH4(0,2)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH4(0,3)));
}
function f_g_chan_is_sdcch8() runs on ConnHdlr return boolean {
return (g_chan_nr == valueof(ts_RslChanNr_SDCCH8(6,0)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH8(6,1)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH8(6,2)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH8(6,3)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH8(6,4)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH8(6,5)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH8(6,6)) or
g_chan_nr == valueof(ts_RslChanNr_SDCCH8(6,7)));
}
function f_test_l2_fill_frames(boolean dtxd) runs on ConnHdlr {
var L1ctlDlMessage dl;
var octetstring l2_fill_frame := '0303012B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B2B'O;
var octetstring l2_fill_frame_sacch := substr(l2_fill_frame, 0, lengthof(l2_fill_frame) - 2);
var GsmFrameNumber first_fn;
var boolean is_first_frame := true;
var integer nfill_frames_sacch := 0;
var integer nfill_frames_nonsacch := 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);
T.start;
alt {
[] L1CTL.receive(tr_L1CTL_DATA_IND(g_chan_nr, ?)) -> value dl {
var GsmFrameNumber fn := dl.dl_info.frame_nr;
var octetstring l2 := dl.payload.data_ind.payload;
if (is_first_frame) {
is_first_frame := false;
first_fn := dl.dl_info.frame_nr;
}
if (dl.dl_info.link_id.c == SACCH) {
l2 := substr(l2, 2, lengthof(l2) - 2); /* remove L1 header */
if (not match(l2_fill_frame_sacch, l2)) {
repeat;
}
} else if (not match(l2_fill_frame, l2)) {
repeat;
}
if (dtxd) {
if (not f_g_chan_is_tchf()) {
T.stop;
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received fill frame on non-TCH/F channel; DTX is only allowed on TCH/F!");
}
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.
* 2 SACCH, 1 TCH/F */
expected_fill_frames := 3;
if (nfill_frames_sacch + nfill_frames_nonsacch < expected_fill_frames) {
log("received only ", nfill_frames_sacch, "+", nfill_frames_nonsacch,
" (SACCH+other) out of ", expected_fill_frames, " expected fill frames");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Not enough fill frames received");
} else {
setverdict(pass);
}
} else {
if (dl.dl_info.link_id.c == SACCH) {
nfill_frames_sacch := nfill_frames_sacch + 1;
repeat;
}
/* 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_nonsacch := nfill_frames_nonsacch + 1;
repeat;
}
}
log("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);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unexpected L2 fill frame received on Um");
}
} else {
if (dl.dl_info.link_id.c == SACCH) {
nfill_frames_sacch := nfill_frames_sacch + 1;
} else {
nfill_frames_nonsacch := nfill_frames_nonsacch + 1;
}
if (fn > first_fn + frame_dtx_tchf_mod) {
T.stop;
if (f_g_chan_is_tchf()) {
/* Without DTX we can expect 25 fill frames for every 104 frames.
* (24 FACCH + 1 SACCH filling) */
expected_fill_frames := 25;
} else if (f_g_chan_is_tchh()) {
/* We can expect 2 fill frames for every 104 frames. */
expected_fill_frames := 2;
} else if (f_g_chan_is_sdcch4() or f_g_chan_is_sdcch8()) {
/* We can expect 5 fill frames for every 104 frames. */
expected_fill_frames := 5;
} else {
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Unknown channel type");
}
f_rsl_chan_deact();
f_L1CTL_DM_REL_REQ(L1CTL, g_chan_nr);
if (nfill_frames_sacch + nfill_frames_nonsacch >= expected_fill_frames) {
setverdict(pass);
} else {
log("received only ", nfill_frames_sacch, "+", nfill_frames_nonsacch,
" (SACCH+other) out of ", expected_fill_frames, " expected fill frames");
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Not enough fill frames received");
}
} 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");
}
}
}
function f_TC_tch_sign_l2_fill_frame(charstring id) runs on ConnHdlr {
f_test_l2_fill_frames(false);
}
function f_TC_tch_sign_l2_fill_frame_dtxd(charstring id) runs on ConnHdlr {
f_test_l2_fill_frames(true);
}
function f_tch_sign_l2_fill_frame(boolean dtxd) runs on test_CT {
var ConnHdlr vc_conn;
var ConnHdlrPars pars;
pars.t_guard := 60.0;
f_init(testcasename());
for (var integer i := 0; i < sizeof(g_AllChannels); i := i + 1) {
pars := valueof(t_Pars(g_AllChannels[i], ts_RSL_ChanMode_SIGN(dtxd)));
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_dtxd), pars);
} else {
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);
}
/* 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_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_count() );
execute( TC_rach_max_ta() );
execute( TC_rach_load_idle_thresh0() );
execute( TC_rach_load_idle_below_thresh() );
execute( TC_rach_load_count() );
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_rsl_ms_pwr_ctrl() );
execute( TC_rsl_chan_initial_ta() );
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_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_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() );
}
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() );
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() );
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_act_deact() );
execute( TC_dyn_osmo_pdch_unsol_deact() );
execute( TC_dyn_osmo_pdch_double_act() );
execute( TC_dyn_osmo_pdch_tchf_act() );
execute( TC_dyn_osmo_pdch_tchh_act() );
execute( TC_dyn_ipa_pdch_act_deact() );
execute( TC_dyn_ipa_pdch_tchf_act() );
execute( TC_dyn_ipa_pdch_tchf_act_pdch_act_nack() );
execute( TC_dyn_ipa_pdch_act_tchf_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() );
}
}