/* Cesar project {{{ * * Copyright (C) 2009 Spidcom * * <<>> * * }}} */ /** * \file ce/rx/test/src/test_rx.c * \brief Test the CE in RX. * \ingroup test * */ #include "common/std.h" #include "mac/common/store.h" #include "ce/rx/rx.h" #include "ce/rx/measure.h" #include "ce/rx/inc/measure.h" #include "ce/rx/inc/rx.h" #include "ce/rx/bitloading/fsm/fsm.h" #include "ce/rx/bitloading/inc/intervals.h" #include "ce_rx_bl_fsm_defs.h" #include "lib/test.h" #include "lib/stats.h" #include "lib/blk.h" #include "lib/mbox.h" #include #include u8 thread_stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; cyg_handle_t thread_handler; cyg_thread thread; test_t test; static int mbox_size = 0; static ce_rx_measure_mbox_t *test_measure = NULL; /** * Structure with all required tests context. */ typedef struct test_ce_rx { sar_t *s; pbproc_t *p; mac_store_t *ms; mac_config_t mc; bsu_aclf_t ba; } test_ce_rx_t; /** * Common function to initialize lower layer. * \param t test suite context * \param tcr test ce rx context */ static void test_ce_rx_ll_init (test_t t, test_ce_rx_t *tcr) { tcr->p = (pbproc_t *) t; tcr->ms = mac_store_init (); tcr->s = sar_init (tcr->ms, NULL, NULL, NULL, 0x1); mac_config_init (&tcr->mc); } /** * Common function to un-initialize lower layer. * \param tcr test ce rx context */ static void test_ce_rx_ll_uninit (test_ce_rx_t *tcr) { mac_store_uninit (tcr->ms); sar_uninit (tcr->s); } /* Stub bitloadling. */ void ce_rx_bl_fsm_handle_event (ce_rx_t *ce_rx, sta_t *sta, ce_rx_bl_fsm_event_type_t e, ce_rx_bitloading_fsm_event_param_t data) { mbox_size--; /* Copy measure for analysis in test. */ test_measure = data.measure; /* Suspend CE thread otherwise measure will be directly processed and can * not be used any more. * This is the normal behavior because SAR has higher priority than * CE. */ cyg_thread_suspend (ce_rx->thread_handler); /* Restore to sane value. */ test_measure = NULL; } /* Stub */ ce_rx_bl_fsm_event_type_t ce_rx_bl_fsm_measure_to_event (sta_t *sta, ce_rx_bitloading_fsm_event_param_t data) { dbg_assert (sta); /* Prepare event types. * We create a array of two dimensions: * First dimension: is measure for intervals or global ? * Second dimension: Data, Sound complete or not.*/ int has_interval, type; static const ce_rx_bl_fsm_event_type_t event_tab[2][3] = { { CE_RX_BL_FSM_EVENT_TYPE_data, CE_RX_BL_FSM_EVENT_TYPE_sound, CE_RX_BL_FSM_EVENT_TYPE_sound_completed }, { CE_RX_BL_FSM_EVENT_TYPE_interval_data, CE_RX_BL_FSM_EVENT_TYPE_interval_sound, CE_RX_BL_FSM_EVENT_TYPE_interval_sound_completed } }; /* Get measure. */ ce_rx_measure_mbox_t *measure = data.measure; dbg_assert (measure); /* Get interval information. */ int interval; s16 start_date_atu, end_date_atu; /* Global by default. */ has_interval = CE_RX_FSM_MEASURE_TO_EVENT_TYPE_GLOBAL; measure->fsm_id = CE_RX_MEASURE_NO_INTERVAL; ce_rx_bl_intervals_measure_to_date (&measure->rx_params, &start_date_atu, &end_date_atu); interval = ce_rx_bl_intervals_measure_to_interval (sta->rx_tonemaps, start_date_atu, end_date_atu); if (interval != CE_RX_MEASURE_NO_INTERVAL) { /* We are on an interval. */ has_interval = CE_RX_FSM_MEASURE_TO_EVENT_TYPE_INTERVAL; measure->fsm_id = interval % ce_rx_bl_intervals_fsm_count_; } /* Is this measure a Sound ? */ if (measure->rx_params.sound) { if (measure->rx_params.sound_complete) type = CE_RX_FSM_MEASURE_TO_EVENT_TYPE_SOUND_COMPLETED; else type = CE_RX_FSM_MEASURE_TO_EVENT_TYPE_SOUND; } /* This is a data. */ else type = CE_RX_FSM_MEASURE_TO_EVENT_TYPE_DATA; return event_tab[has_interval][type]; } void test_rx_ce_suite (test_t t) { test_ce_rx_t tcr; test_ce_rx_ll_init (t, &tcr); test_begin (t, "launch ce_rx thread") { ce_rx_t *ce_rx = ce_rx_init (tcr.ms, tcr.s, tcr.p, &tcr.mc, &tcr.ba); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != 0); ce_rx_uninit (ce_rx); } test_end; test_ce_rx_ll_uninit (&tcr); } void test_ce_rx_measure_suite (test_t t) { pbproc_rx_params_t rx_params; rx_params.tei = 1; rx_params.multi_net_bcast = false; rx_params.preamble_ntb = 0; rx_params.beacon_period_start_ntb = 42; rx_params.fl_tck = 1664; test_ce_rx_t tcr; test_ce_rx_ll_init (t, &tcr); ce_rx_t *ce_rx = ce_rx_init (tcr.ms, tcr.s, tcr.p, &tcr.mc, &tcr.ba); test_begin (t, "no RX params, no channel data & no PB stats") { bool catch = false; mbox_size = 1; dbg_fatal_try_begin { ce_rx_measure_sar_cb (ce_rx, NULL, 0, NULL, 0, 0, 0); } dbg_fatal_try_catch_void () { catch = true; } dbg_fatal_try_end; test_fail_if (!catch); test_fail_if (mbox_size != 1); /* Resume CE thread. */ cyg_thread_resume (ce_rx->thread_handler); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != 0); } test_end; test_begin (t, "some RX params, but no channel data & no PB stats") { bool catch = false; mbox_size = 1; dbg_fatal_try_begin { ce_rx_measure_sar_cb (ce_rx, &rx_params, 0, NULL, 0, 0, 0); } dbg_fatal_try_catch_void () { catch = true; } dbg_fatal_try_end; test_fail_if (!catch); test_fail_if (mbox_size != 1); /* Resume CE thread. */ cyg_thread_resume (ce_rx->thread_handler); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != 0); } test_end; test_begin (t, "some RX params & some channel data & no PB stats") { pb_t *pb = (pb_t *) blk_alloc_desc (); mbox_size = 1; ce_rx_measure_sar_cb (ce_rx, &rx_params, 0, pb, 1, 0, 0); test_fail_if (test_measure == NULL); test_fail_if (test_measure->chan_data_count != 1); test_fail_if (test_measure->chan_data != (phy_chandata_t *) pb); test_fail_if (test_measure->total_pb_count != 0); test_fail_if (test_measure->false_pb_count != 0); test_fail_if (test_measure->ber_sum != 0); test_fail_if (mbox_size != 0); /* Resume CE thread. */ cyg_thread_resume (ce_rx->thread_handler); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != 0); } test_end; test_begin (t, "some RX params & some channel data & PB stats") { pb_t *pb = (pb_t *) blk_alloc_desc (); mbox_size = 1; ce_rx_measure_sar_cb (ce_rx, &rx_params, 42, pb, 1, 24, 4242); test_fail_if (mbox_size != 0); test_fail_if (test_measure == NULL); test_fail_if (test_measure->chan_data_count != 1); test_fail_if (test_measure->chan_data != (phy_chandata_t *) pb); test_fail_if (test_measure->total_pb_count != 42); test_fail_if (test_measure->false_pb_count != 24); test_fail_if (test_measure->ber_sum != 4242); /* Resume CE thread. */ cyg_thread_resume (ce_rx->thread_handler); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != 0); } test_end; test_begin (t, "handle too much measure") { /* Stop CE thread to store many measure. */ cyg_thread_suspend (ce_rx->thread_handler); uint i; for (i = 0; i < CE_RX_MEASURE_MBOX_MAX_SIZE; i++) { pb_t *pb = (pb_t *) blk_alloc_desc (); ce_rx_measure_sar_cb (ce_rx, &rx_params, 42, pb, 1, 24, 4242); } test_fail_if (mbox_peek (&ce_rx->measure_mbox) != CE_RX_MEASURE_MBOX_MAX_SIZE); /* Try to add a measurement without channel data. */ ce_rx_measure_sar_cb (ce_rx, &rx_params, 42, NULL, 0, 24, 4242); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != CE_RX_MEASURE_MBOX_MAX_SIZE); /* Try to add a channel data measurement. */ pb_t *pb = (pb_t *) blk_alloc_desc (); ce_rx_measure_sar_cb (ce_rx, &rx_params, 42, pb, 1, 24, 4242); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != CE_RX_MEASURE_MBOX_MAX_SIZE + 1); /* Clean. */ for (i = 0; i <= CE_RX_MEASURE_MBOX_MAX_SIZE + 1; i++) cyg_thread_resume (ce_rx->thread_handler); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != 0); } test_end; test_begin (t, "measure from unassociated station") { pb_t *pb = (pb_t *) blk_alloc_desc (); mbox_size = 0; rx_params.tei = MAC_TEI_UNASSOCIATED; rx_params.multi_net_bcast = false; ce_rx_measure_sar_cb (ce_rx, &rx_params, 0, pb, 1, 0, 0); test_fail_if (mbox_size != 0); /* Resume CE thread. */ cyg_thread_resume (ce_rx->thread_handler); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != 0); /* Clean. */ blk_release_desc ((blk_t *) pb); } test_end; test_begin (t, "measure from multi-unicast") { pb_t *pb = (pb_t *) blk_alloc_desc (); mbox_size = 0; rx_params.tei = MAC_TEI_STA_MIN; rx_params.multi_net_bcast = true; ce_rx_measure_sar_cb (ce_rx, &rx_params, 0, pb, 1, 0, 0); test_fail_if (mbox_size != 0); /* Resume CE thread. */ cyg_thread_resume (ce_rx->thread_handler); test_fail_if (mbox_peek (&ce_rx->measure_mbox) != 0); /* Clean. */ blk_release_desc ((blk_t *) pb); } test_end; /* Clean. */ dbg_assert (mac_store_sta_remove (tcr.ms, 1) == true); ce_rx_uninit (ce_rx); test_ce_rx_ll_uninit (&tcr); } void test_ce_rx_get_snr (test_t t) { const uint tei = 1; test_ce_rx_t tcr; u16 ber = 0x42; test_ce_rx_ll_init (t, &tcr); ce_rx_t *ce_rx = ce_rx_init (tcr.ms, tcr.s, tcr.p, &tcr.mc, &tcr.ba); mac_store_sta_add (tcr.ms, tei); sta_t *sta = mac_store_sta_get (tcr.ms, tei); dbg_assert (sta); sta->ce_rx_bt.noise_nrj = INVALID_PTR; test_case_begin (t, "get SNR"); test_begin (t, "get SNR, no station") { test_fail_if (ce_rx_get_nsr (ce_rx, tei + 1, 0, 0, &ber) != NULL); test_fail_if (ber != 0x42); } test_end; test_begin (t, "get SNR, bit loading not ready") { sta->ce_rx_bt.fsm = 0; test_fail_if (ce_rx_get_nsr (ce_rx, tei, 0, 0, &ber) != NULL); test_fail_if (ber != 0x42); } test_end; test_begin (t, "get SNR, bit loading initial done") { sta->ce_rx_bt.fsm = CE_RX_BL_FSM_STATE_TRACKING; test_fail_if (ce_rx_get_nsr (ce_rx, tei, 0, 0, &ber) != NULL); sta->ce_rx_bt.noise_nrj_blk_count = 1; test_fail_if (ce_rx_get_nsr (ce_rx, tei, 0, 0, &ber) != INVALID_PTR); test_fail_if (ber != 0); sta->ce_rx_bt.noise_nrj_blk_count = 0; } test_end; test_begin (t, "get SNR, intervals version") { sta->rx_tonemaps->intervals->version = 0x42; test_fail_if (ce_rx_get_nsr (ce_rx, tei, 0, 0, &ber) != NULL); test_fail_if (ce_rx_get_nsr (ce_rx, tei, 0, 0x42, &ber) != NULL); sta->ce_rx_bt.noise_nrj_blk_count = 1; test_fail_if (ce_rx_get_nsr (ce_rx, tei, 0, 0x42, &ber) != INVALID_PTR); sta->ce_rx_bt.noise_nrj_blk_count = 0; } test_end; test_begin (t, "get SNR, interval enable") { ce_rx_behavior = CE_RX_BEHAVIOR_INTERVALS_ENABLE; ce_rx_bitloading_intervals_t intervals; ce_rx_bitloading_t interval; intervals.intervals[0] = &interval; interval.noise_nrj = (void*) 0xDEADCAFE; interval.noise_nrj_blk_count = 1; interval.fsm = CE_RX_BL_FSM_STATE_TRACKING; intervals.tmi[0] = sta->rx_tonemaps->default_tmi; sta->intervals = &intervals; sta->ce_rx_bt.noise_nrj_blk_count = 1; /* The TMI of the interval match the default one, the NSR of the * default should be used. */ test_fail_if (ce_rx_get_nsr (ce_rx, tei, 0, 0x42, &ber) != INVALID_PTR); sta->intervals->tmi[0] = 5; /* The TMI of the interval does not match the default one, * the NSR of the interval tonemap should be used. */ test_fail_if (ce_rx_get_nsr (ce_rx, tei, 0, 0x42, &ber) != (void*) 0xDEADCAFE); /* Clean all the state of the modified interval for this test. */ sta->ce_rx_bt.noise_nrj_blk_count = 0; sta->intervals = NULL; } test_end; /* Clean. */ blk_release (sta); dbg_assert (mac_store_sta_remove (tcr.ms, tei) == true); ce_rx_uninit (ce_rx); test_ce_rx_ll_uninit (&tcr); } void test_ce_rx_stats (test_t t) { test_case_begin (t, "stats"); test_ce_rx_t tcr; test_ce_rx_ll_init (t, &tcr); test_begin (t, "init") { ce_rx_t *ce_rx = ce_rx_init (tcr.ms, tcr.s, tcr.p, &tcr.mc, &tcr.ba); test_fail_if (ce_rx->stats.resend_tm != 0); ce_rx->stats.resend_tm = 0x42; ce_rx_stats_init (ce_rx); test_fail_if (ce_rx->stats.resend_tm != 0); ce_rx_uninit (ce_rx); } test_end; test_ce_rx_ll_uninit (&tcr); } void test_rx_ce_thread (cyg_addrword_t data) { lib_stats_init (); test_rx_ce_suite (test); test_ce_rx_measure_suite (test); test_ce_rx_get_snr (test); test_ce_rx_stats (test); lib_stats_uninit (); test_begin (test, "memory") { test_fail_unless (blk_check_memory ()); } test_end; test_result (test); HAL_PLATFORM_EXIT (test_nb_failed (test) == 0 ? 0 : 1); } int main (int argc, char **argv) { test_init (test, argc, argv); /* Create the ECos thread. */ cyg_thread_create (CE_RX_THREAD_PRIORITY + 5, &test_rx_ce_thread, (cyg_addrword_t) NULL, "Test CE RX", thread_stack, sizeof (thread_stack), &thread_handler, &thread); cyg_thread_resume (thread_handler); return 1; }