From 511f9c3e4a0a7ebb6294d6a2e4ac29dbcdad818c Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Sat, 13 Oct 2012 12:38:54 +0200 Subject: [PATCH] si: Partially implement the range encoding for the SI. I saw the old copy of the "Appendix J" code too late and I have discovered some quirks and I am more familar with my implementation. Most noticable 'w' only needs to be as big as the input arfcn but requires the 'w' to be initialized. The power_of_2 implementation differs as well (mine matches the output of wirehsark). The f0 could be chosen in a better way but right now picking the lower bound is the easiest. It is not clear if to use modulo if the range is chosen in the middle. This can be improved in the future. Right now I have no bit fiddling for range128, 256 and 1024 as I was running out of time. --- openbsc/configure.ac | 1 + openbsc/include/openbsc/Makefile.am | 3 +- openbsc/include/openbsc/arfcn_range_encode.h | 26 ++ openbsc/src/arfcn_list_range.c | 194 ------------ openbsc/src/libbsc/Makefile.am | 3 +- openbsc/src/libbsc/arfcn_range_encode.c | 305 +++++++++++++++++++ openbsc/src/libbsc/system_information.c | 136 +++++++-- openbsc/tests/Makefile.am | 2 +- openbsc/tests/si/Makefile.am | 12 + openbsc/tests/si/si_test.c | 156 ++++++++++ openbsc/tests/si/si_test.ok | 20 ++ openbsc/tests/testsuite.at | 6 + 12 files changed, 642 insertions(+), 222 deletions(-) create mode 100644 openbsc/include/openbsc/arfcn_range_encode.h delete mode 100644 openbsc/src/arfcn_list_range.c create mode 100644 openbsc/src/libbsc/arfcn_range_encode.c create mode 100644 openbsc/tests/si/Makefile.am create mode 100644 openbsc/tests/si/si_test.c create mode 100644 openbsc/tests/si/si_test.ok diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 92d7a077b..86dab55f6 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -138,6 +138,7 @@ AC_OUTPUT( tests/bsc-nat/Makefile tests/mgcp/Makefile tests/gprs/Makefile + tests/si/Makefile doc/Makefile doc/examples/Makefile Makefile) diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 58fa691b0..93b30bb0c 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -12,7 +12,8 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ auth.h osmo_msc.h bsc_msc.h bsc_nat.h \ osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \ osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \ - bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h + bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h \ + arfcn_range_encode.h openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/arfcn_range_encode.h b/openbsc/include/openbsc/arfcn_range_encode.h new file mode 100644 index 000000000..7a6fff000 --- /dev/null +++ b/openbsc/include/openbsc/arfcn_range_encode.h @@ -0,0 +1,26 @@ +#ifndef ARFCN_RANGE_ENCODE_H +#define ARFCN_RANGE_ENCODE_H + +#include + +enum { + ARFCN_RANGE_INVALID = -1, + ARFCN_RANGE_128 = 127, + ARFCN_RANGE_256 = 255, + ARFCN_RANGE_512 = 511, + ARFCN_RANGE_1024 = 1023, +}; + +#define RANGE_ENC_MAX_ARFCNS 29 + +int range_enc_determine_range(const int *arfcns, int size, int *f0_out); +int range_enc_arfcns(const int rng, const int *arfcns, int sze, int *out, int idx); +int range_enc_find_index(const int rng, const int *arfcns, int size); +int range_enc_filter_arfcns(const int rng, int *arfcns, const int sze, const int f0, int *f0_included); + +int range_enc_range128(uint8_t *chan_list, int f0, int *w); +int range_enc_range256(uint8_t *chan_list, int f0, int *w); +int range_enc_range512(uint8_t *chan_list, int f0, int *w); +int range_enc_range1024(uint8_t *chan_list, int f0, int f0_incl, int *w); + +#endif diff --git a/openbsc/src/arfcn_list_range.c b/openbsc/src/arfcn_list_range.c deleted file mode 100644 index b7f6e0ffd..000000000 --- a/openbsc/src/arfcn_list_range.c +++ /dev/null @@ -1,194 +0,0 @@ -/* C-Implementation of the Algorithm described in Appendix J of GSM TS 44.018, - * (C) 2009 by Dirk Hakkesteegt - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -/* Annex J.3 indicates that at least in one BA list, we can never have more - * than 29 frequencies within the 16byte limit */ -#define MAX_ARRFCNS 29 - -/***************************************************************************** -* NAME : smod -* DESCRIPTION : n smod m indicates the offset remainder of the euclidian -* division of n by m -* INPUT : n, m -* OUTPUT : n smod m -* RETURNS : -* Errorcodes : -******************************************************************************/ -static int smod(int n, int m) -{ - int result = n % m; - if (result < 0) - result += m; - - if (result == 0) - result = m; - - return result; -} - -/***************************************************************************** -* NAME : mod -* DESCRIPTION : n mod m indicates the remainder of the euclidian division of -* n by m -* INPUT : n, m -* OUTPUT : n mod m -* RETURNS : -* Errorcodes : -******************************************************************************/ -static int mod(int n, int m) -{ - int result = n % m; - if (result < 0) - result += m; - - return result; -} - -/***************************************************************************** -* NAME : greatest_power_of_2_le_to -* DESCRIPTION : Calculates the greatest power of 2 that is lesser or equal -* to the input value; -* INPUT : -* OUTPUT : -* RETURNS : -* Errorcodes : -******************************************************************************/ -static int greatest_power_of_2_le_to(int input) -{ - int check_val = 1; - while (check_val <= input) - check_val *= 2; - - return check_val / 2; -} - -/***************************************************************************** -* NAME : ENCODE_SUBTREE -* DESCRIPTION : Recursive encoding routine based on 3GPP TS44.018 Annex J.4 -* INPUT : index: current position in the W list -* set: the array to be encoded -* range: the current range -* set_size: number of elements in set -* OUTPUT : W: the array of results -* RETURNS : -* Errorcodes : -******************************************************************************/ -static void encode_subtree(int index, int *set, int range, int set_size, int *W) -{ - int index_in_set = 0; - int N, J, I, x; - int subset[18]; - int subset_index, origin_value; - - /* Check if this is a leaf */ - if (set_size == 0) { - W[index] = 0; - return; - } else { - if (set_size == 1) { - W[index] = 1 + set[1]; - return; - } - } - - for (I = 1; I <= set_size; I++) { - N = 0; - for (J = 1; J <= set_size; J++) { - x = set[J] - set[I]; - x = mod(x, range); - if (x <= (range-1)/2) - N++; - } - if (N-1 == (set_size-1) / 2) { - index_in_set = I; - break; - } - } - - W[index] = set[index_in_set] + 1; - - /* Left subset */ - subset[0] = 0; - origin_value = mod((set[index_in_set] + (range-1) / 2 + 1), range); - subset_index = 1; - for (I = 1; I <= set_size; I++) { - if (mod((set[I]-origin_value), range) < range/2) { - subset[subset_index] = mod((set[I] - origin_value), range); - subset_index++; - subset[subset_index] = 0; - } - } - encode_subtree(index + greatest_power_of_2_le_to(index), - subset, range / 2, subset_index-1, W); - - /* Right subset */ - subset[0] = 0; - origin_value = mod((set[index_in_set] + 1), range); - subset_index=1; - for (I = 1; I<= set_size; I++) { - if (mod((set[I]-origin_value), range) < range/2) { - subset[subset_index] = mod((set[I] - origin_value), range); - subset_index++; - subset[subset_index] = 0; - } - } - encode_subtree(index + 2*greatest_power_of_2_le_to(index), - subset, (range-1)/2, subset_index-1, W); -} - -/***************************************************************************** -* NAME : CalcARFCN -* DESCRIPTION : Calculate the ARFCN list -* INPUT : F: the list of input frequencies. MUST BE SORTED! -* count: the number of elements in the F list -* range: the encoding range (default: range 512) -* OUTPUT : W: the list of W values -* RETURNS : -* Errorcodes : -******************************************************************************/ -static void CalcARFCN(const unsigned int *F, int *W, unsigned int count, unsigned int range) -{ - int i; - int Fd[MAX_ARFCNS+1]; - - W[0] = F[0]; - for (i = 1; i < count; i++) { - Fd[i] = F[i] - F[0] - 1; - } - encode_subtree(1, Fd, range-1, count-1, W); -} - -int bitvec2arfcn_list_range(uint8_t *range, struct bitvec *bv, uint16_t range) -{ - unsigned int i, idx = 0; - int F[MAX_ARFCNS+1]; - int W[MAX_ARFCNS+1]; - - /* build an array of integers from the bitmask */ - for (i = 0; i < bv->data_len*8; i++) { - if (bitvec_get_bit_pos(bv, i)) - F[idx++] = i; - } - /* Perform the actual algorithm to calculate the 'W' values */ - CalcARFCN(F, W, idx, range); - - /* FIXME: Encode the 'W' values into the actual format as used in 04.08 */ - - return -EIO; -} diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am index 7cb1e6e00..21cbccd31 100644 --- a/openbsc/src/libbsc/Makefile.am +++ b/openbsc/src/libbsc/Makefile.am @@ -22,5 +22,6 @@ libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ e1_config.c \ bsc_api.c bsc_msc.c bsc_vty.c \ gsm_04_08_utils.c \ - bsc_init.c bts_init.c bsc_rf_ctrl.c + bsc_init.c bts_init.c bsc_rf_ctrl.c \ + arfcn_range_encode.c diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c new file mode 100644 index 000000000..02a75a53c --- /dev/null +++ b/openbsc/src/libbsc/arfcn_range_encode.c @@ -0,0 +1,305 @@ +/* gsm 04.08 system information (si) encoding and decoding + * 3gpp ts 04.08 version 7.21.0 release 1998 / etsi ts 100 940 v7.21.0 */ + +/* + * (C) 2012 Holger Hans Peter Freyther + * (C) 2012 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include + +#include + +int greatest_power_of_2_lesser_or_equal_to(int index) +{ + int power_of_2 = 1; + + do { + power_of_2 *= 2; + } while (power_of_2 <= index); + + /* now go back one step */ + return power_of_2 / 2; +} + +static inline int mod(int data, int range) +{ + int res = data % range; + while (res < 0) + res += range; + return res; +} + +/** + * Determine at which index to split the ARFCNs to create an + * equally size partition for the given range. Return -1 if + * no such partition exists. + */ +int range_enc_find_index(const int range, const int *freqs, const int size) +{ + int i, j, n; + + const int RANGE_DELTA = (range - 1) / 2; + + for (i = 0; i < size; ++i) { + n = 0; + for (j = 0; j < size; ++j) { + if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA) + n += 1; + } + + if (n - 1 == (size - 1) / 2) + return i; + } + + return -1; +} + +/** + * Range encode the ARFCN list. + * \param range The range to use. + * \param arfcns The list of ARFCNs + * \param size The size of the list of ARFCNs + * \param out Place to store the W(i) output. + */ +int range_enc_arfcns(const int range, + const int *arfcns, int size, int *out, + const int index) +{ + int split_at; + int i; + + /* + * The below is a GNU extension and we can remove it when + * we move to a quicksort like in-situ swap with the pivot. + */ + int arfcns_left[size / 2]; + int arfcns_right[size / 2]; + int l_size; + int r_size; + int l_origin; + int r_origin; + + + /* Test the two recursion anchors and stop processing */ + if (size == 0) + return 0; + + if (size == 1) { + out[index] = 1 + arfcns[0]; + return 0; + } + + /* Now do the processing */ + split_at = range_enc_find_index(range, arfcns, size); + + /* we now know where to split */ + out[index] = 1 + arfcns[split_at]; + + /* calculate the work that needs to be done for the leafs */ + l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range); + r_origin = mod(arfcns[split_at] + 1, range); + for (i = 0, l_size = 0, r_size = 0; i < size; ++i) { + if (mod(arfcns[i] - l_origin, range) < range / 2) + arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range); + if (mod(arfcns[i] - r_origin, range) < range / 2) + arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range); + } + + /* + * Now recurse and we need to make this iterative... but as the + * tree is balanced the stack will not be too deep. + */ + range_enc_arfcns(range / 2, arfcns_left, l_size, + out, index + greatest_power_of_2_lesser_or_equal_to(index + 1)); + range_enc_arfcns((range -1 ) / 2, arfcns_right, r_size, + out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1))); + return 0; +} + +/* + * The easiest is to use f0 == arfcns[0]. This means that under certain + * circumstances we can encode less ARFCNs than possible with an optimal f0. + * + * TODO: Solve the optimisation problem and pick f0 so that the max distance + * is the smallest. Taking into account the modulo operation. I think picking + * size/2 will be the optimal arfcn. + */ +/** + * This implements the range determination as described in GSM 04.08 J4. The + * result will be a base frequency f0 and the range to use. + * + * \param[in] arfcns The input frequencies, they must be sorted, lowest number first + * \param[in] size The length of the array + * \param[out] f0 The selected F0 base frequency. It might not be inside the list + */ +int range_enc_determine_range(const int *arfcns, const int size, int *f0) +{ + int max = 0; + + /* + * Go for the easiest. And pick arfcns[0] == f0. + */ + max = arfcns[size - 1] - arfcns[0]; + *f0 = arfcns[0]; + + if (max < 128 && size <= 29) + return ARFCN_RANGE_128; + if (max < 256 && size <= 22) + return ARFCN_RANGE_256; + if (max < 512 && size <= 18) + return ARFCN_RANGE_512; + if (max < 1024 && size <= 17) + return ARFCN_RANGE_1024; + + return ARFCN_RANGE_INVALID; +} + +/* + * The below is easier is to write in four methods than + * to use the max_bits. The encoding is so screwed.. as + * the bits need to be put in place in the wrong order.. + */ +#define HIGH_BITS(w, index, bits, offset) \ + (w[index - 1] >> (bits - offset)) +#define LOW_BITS(w, index, bits, offset) \ + (w[index - 1]) + +static void write_orig_arfcn(uint8_t *chan_list, int f0) +{ + chan_list[0] |= (f0 >> 9) & 1; + chan_list[1] = (f0 >> 1); + chan_list[2] = (f0 & 1) << 7; +} + +int range_enc_range128(uint8_t *chan_list, int f0, int *w) +{ + chan_list[0] = 0x8C; + write_orig_arfcn(chan_list, f0); + + LOGP(DRR, LOGL_ERROR, "Range128 encoding is not implemented.\n"); + return -1; +} + +int range_enc_range256(uint8_t *chan_list, int f0, int *w) +{ + chan_list[0] = 0x8A; + write_orig_arfcn(chan_list, f0); + + LOGP(DRR, LOGL_ERROR, "Range256 encoding is not implemented.\n"); + return -1; +} + +int range_enc_range512(uint8_t *chan_list, int f0, int *w) +{ + struct gsm48_range_512 *range512; + write_orig_arfcn(chan_list, f0); + + range512 = (struct gsm48_range_512 *) &chan_list[0]; + range512->form_id = chan_list[0] = 0x44; + + /* W(1) */ + range512->w1_hi = HIGH_BITS(w, 1, 9, 7); + range512->w1_lo = LOW_BITS (w, 1, 9, 2); + /* W(2) */ + range512->w2_hi = HIGH_BITS(w, 2, 8, 6); + range512->w2_lo = LOW_BITS (w, 2, 8, 2); + /* W(3) */ + range512->w3_hi = HIGH_BITS(w, 3, 8, 6); + range512->w3_lo = LOW_BITS (w, 3, 8, 2); + /* W(4) */ + range512->w4_hi = HIGH_BITS(w, 4, 7, 6); + range512->w4_lo = LOW_BITS (w, 4, 7, 1); + /* W(5) */ + range512->w5 = HIGH_BITS(w, 5, 7, 7); + /* W(6) */ + range512->w6 = HIGH_BITS(w, 6, 7, 7); + /* W(7) */ + range512->w7_hi = HIGH_BITS(w, 7, 7, 1); + range512->w7_lo = LOW_BITS (w, 7, 7, 6); + /* W(8) */ + range512->w8_hi = HIGH_BITS(w, 8, 6, 2); + range512->w8_lo = LOW_BITS (w, 8, 6, 4); + /* W(9) */ + range512->w9_hi = HIGH_BITS(w, 9, 6, 4); + range512->w9_lo = LOW_BITS(w, 9, 6, 2); + /* W(10) */ + range512->w10 = HIGH_BITS(w, 10, 6, 6); + /* W(11) */ + range512->w11 = HIGH_BITS(w, 11, 6, 6); + /* W(12) */ + range512->w12_hi = HIGH_BITS(w, 12, 6, 2); + range512->w12_lo = LOW_BITS (w, 12, 6, 4); + /* W(13) */ + range512->w13_hi = HIGH_BITS(w, 13, 6, 4); + range512->w13_lo = LOW_BITS(w, 13, 6, 2); + /* W(14) */ + range512->w14 = HIGH_BITS(w, 14, 6, 6); + /* W(15) */ + range512->w15 = HIGH_BITS(w, 15, 6, 6); + /* W(16) */ + range512->w16_hi = HIGH_BITS(w, 16, 5, 2); + range512->w16_lo = HIGH_BITS(w, 16, 5, 3); + /* W(17) */ + range512->w17 = HIGH_BITS(w, 17, 5, 5); + + return 0; +} + +int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w) +{ + chan_list[0] = 0x80 | (f0_included << 2); + + LOGP(DRR, LOGL_ERROR, "Range1024 encoding is not implemented.\n"); + return -1; +} + +int range_enc_filter_arfcns(const int range, int *arfcns, + const int size, const int f0, int *f0_included) +{ + int i, j = 0; + *f0_included = 0; + + if (range == ARFCN_RANGE_1024) { + for (i = 0; i < size; ++i) { + if (arfcns[i] == f0) { + *f0_included = 1; + continue; + } + + /* copy and subtract */ + arfcns[j++] = mod(arfcns[i] - 1, 1024); + } + } else { + for (i = 0; i < size; ++i) { + /* + * Appendix J.4 says the following: + * All frequencies except F(0), minus F(0) + 1. + * I assume we need to exclude it here. + */ + if (arfcns[i] == f0) + continue; + + arfcns[j++] = mod(arfcns[i] - (f0 + 1), 1024); + } + } + + return j; +} diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c index 08c4fd18a..702924135 100644 --- a/openbsc/src/libbsc/system_information.c +++ b/openbsc/src/libbsc/system_information.c @@ -2,6 +2,7 @@ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008-2010 by Harald Welte + * (C) 2012 Holger Hans Peter Freyther * * All Rights Reserved * @@ -34,6 +35,7 @@ #include #include #include +#include /* Frequency Lists as per TS 04.08 10.5.2.13 */ @@ -88,11 +90,105 @@ static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn) return 0; } +/* generate a variable bitmap */ +static int enc_freq_lst_var_bitmap(uint8_t *chan_list, + struct bitvec *bv, const struct gsm_bts *bts, + int bis, int ter, int min, int pgsm) +{ + int i; + + /* set it to 'Variable bitmap format' */ + chan_list[0] = 0x8e; + + chan_list[0] |= (min >> 9) & 1; + chan_list[1] = (min >> 1); + chan_list[2] = (min & 1) << 7; + + for (i = 0; i < bv->data_len*8; i++) { + /* see notes in bitvec2freq_list */ + if (bitvec_get_bit_pos(bv, i) + && ((!bis && !ter && gsm_arfcn2band(i) == bts->band) + || (bis && pgsm && gsm_arfcn2band(i) == bts->band && (i < 1 || i > 124)) + || (ter && gsm_arfcn2band(i) != bts->band))) { + int rc = freq_list_bmrel_set_arfcn(chan_list, i); + if (rc < 0) + return rc; + } + } + + return 0; +} + +/* generate a frequency list with the range 512 format */ +static int enc_freq_lst_range(uint8_t *chan_list, + struct bitvec *bv, const struct gsm_bts *bts, + int bis, int ter, int pgsm) +{ + int arfcns[RANGE_ENC_MAX_ARFCNS]; + int w[RANGE_ENC_MAX_ARFCNS]; + int f0_included = 0; + int arfcns_used = 0; + int i, rc, range, f0; + + /* + * Select ARFCNs according to the rules in bitvec2freq_list + */ + for (i = 0; i < bv->data_len * 8; ++i) { + /* More ARFCNs than the maximum */ + if (arfcns_used > ARRAY_SIZE(arfcns)) + return -1; + /* Check if we can select it? */ + if (bitvec_get_bit_pos(bv, i) + && ((!bis && !ter && gsm_arfcn2band(i) == bts->band) + || (bis && pgsm && gsm_arfcn2band(i) == bts->band && (i < 1 || i > 124)) + || (ter && gsm_arfcn2band(i) != bts->band))) { + arfcns[arfcns_used++] = i; + } + } + + /* + * Check if the given list of ARFCNs can be encoded. + */ + range = range_enc_determine_range(arfcns, arfcns_used, &f0); + if (range == ARFCN_RANGE_INVALID) + return -2; + + /* + * Manipulate the ARFCN list according to the rules in J4 depending + * on the selected range. + */ + arfcns_used = range_enc_filter_arfcns(range, arfcns, arfcns_used, + f0, &f0_included); + + memset(w, 0, sizeof(w)); + rc = range_enc_arfcns(range, arfcns, arfcns_used, w, 0); + if (rc != 0) + return -3; + + /* Select the range and the amount of bits needed */ + switch (range) { + case ARFCN_RANGE_128: + return range_enc_range128(chan_list, f0, w); + break; + case ARFCN_RANGE_256: + return range_enc_range256(chan_list, f0, w); + break; + case ARFCN_RANGE_512: + return range_enc_range512(chan_list, f0, w); + break; + case ARFCN_RANGE_1024: + return range_enc_range1024(chan_list, f0, f0_included, w); + break; + default: + return -4; + }; +} + /* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, const struct gsm_bts *bts, int bis, int ter) { - int i, rc, min = -1, max = -1, pgsm = 0; + int i, rc, min = -1, max = -1, pgsm = 0, arfcns = 0; memset(chan_list, 0, 16); @@ -114,9 +210,6 @@ static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, return 0; } - /* We currently only support the 'Variable bitmap format' */ - chan_list[0] = 0x8e; - for (i = 0; i < bv->data_len*8; i++) { /* in case of SI2 or SI5 allow all neighbours in same band * in case of SI*bis, allow neighbours in same band ouside pgsm @@ -126,6 +219,9 @@ static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, && ((!bis && !ter && gsm_arfcn2band(i) == bts->band) || (bis && pgsm && gsm_arfcn2band(i) == bts->band && (i < 1 || i > 124)) || (ter && gsm_arfcn2band(i) != bts->band))) { + /* count the arfcns we want to carry */ + arfcns += 1; + /* 955..1023 < 0..885 */ if (min < 0) min = i; @@ -152,29 +248,19 @@ static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, return 0; } - if (((max - min) & 1023) > 111) { - LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, " - "distance > 111\n", min, max); - return -EINVAL; - } + /* Now find the best encoding */ + if (((max - min) & 1023) <= 111) + return enc_freq_lst_var_bitmap(chan_list, bv, bts, bis, + ter, min, pgsm); - chan_list[0] |= (min >> 9) & 1; - chan_list[1] = (min >> 1); - chan_list[2] = (min & 1) << 7; + /* Attempt to do the range encoding */ + rc = enc_freq_lst_range(chan_list, bv, bts, bis, ter, pgsm); + if (rc == 0) + return 0; - for (i = 0; i < bv->data_len*8; i++) { - /* see notes above */ - if (bitvec_get_bit_pos(bv, i) - && ((!bis && !ter && gsm_arfcn2band(i) == bts->band) - || (bis && pgsm && gsm_arfcn2band(i) == bts->band && (i < 1 || i > 124)) - || (ter && gsm_arfcn2band(i) != bts->band))) { - rc = freq_list_bmrel_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - - return 0; + LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, arfcns=%d " + "can not generate ARFCN list", min, max, arfcns); + return -EINVAL; } /* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index 84dbd922c..cede8e824 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = gsm0408 db channel mgcp gprs +SUBDIRS = gsm0408 db channel mgcp gprs si if BUILD_NAT SUBDIRS += bsc-nat diff --git a/openbsc/tests/si/Makefile.am b/openbsc/tests/si/Makefile.am new file mode 100644 index 000000000..63cf6f861 --- /dev/null +++ b/openbsc/tests/si/Makefile.am @@ -0,0 +1,12 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) + +EXTRA_DIST = si_test.ok + +noinst_PROGRAMS = si_test + +si_test_SOURCES = si_test.c + +si_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) diff --git a/openbsc/tests/si/si_test.c b/openbsc/tests/si/si_test.c new file mode 100644 index 000000000..16f6ea39d --- /dev/null +++ b/openbsc/tests/si/si_test.c @@ -0,0 +1,156 @@ + + +#include +#include +#include + +#include + +#include + +#define DBG(...) + +#define VERIFY(res, cmp, wanted) \ + if (!(res cmp wanted)) { \ + printf("ASSERT failed: %s:%d Wanted: %d %s %d\n", \ + __FILE__, __LINE__, res, # cmp, wanted); \ + } + + +static int freqs1[] = { + 12, 70, 121, 190, 250, 320, 401, 475, 520, 574, 634, 700, 764, 830, 905, 980 +}; + +static int freqs2[] = { + 402, 460, 1, 67, 131, 197, 272, 347, +}; + +static int freqs3[] = { + 68, 128, 198, 279, 353, 398, 452, + +}; + +static int w_out[] = { + 122, 2, 69, 204, 75, 66, 60, 70, 83, 3, 24, 67, 54, 64, 70, 9, +}; + +static int range128[] = { + 1, 1 + 127, +}; + +static int range256[] = { + 1, 1 + 128, +}; + +static int range512[] = { + 1, 1+ 511, +}; + + +static void test_arfcn_filter() +{ + int arfcns[50], i, res, f0_included; + for (i = 0; i < ARRAY_SIZE(arfcns); ++i) + arfcns[i] = (i + 1) * 2; + + /* check that the arfcn is taken out. f0_included is only set for Range1024 */ + f0_included = 24; + res = range_enc_filter_arfcns(ARFCN_RANGE_512, arfcns, ARRAY_SIZE(arfcns), + arfcns[0], &f0_included); + VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); + VERIFY(f0_included, ==, 0); + for (i = 0; i < res; ++i) + VERIFY(arfcns[i], ==, ((i+2) * 2) - (2+1)); + + /* check with range1024 */ + for (i = 0; i < ARRAY_SIZE(arfcns); ++i) + arfcns[i] = (i + 1) * 2; + res = range_enc_filter_arfcns(ARFCN_RANGE_1024, arfcns, ARRAY_SIZE(arfcns), + arfcns[0], &f0_included); + VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); + VERIFY(f0_included, ==, 1); + for (i = 0; i < res; ++i) + VERIFY(arfcns[i], ==, ((i + 2) * 2) - 1); + + /* check with range1024, not included */ + for (i = 0; i < ARRAY_SIZE(arfcns); ++i) + arfcns[i] = (i + 1) * 2; + res = range_enc_filter_arfcns(ARFCN_RANGE_1024, arfcns, ARRAY_SIZE(arfcns), + 11, &f0_included); + VERIFY(res, ==, ARRAY_SIZE(arfcns)); + VERIFY(f0_included, ==, 0); + for (i = 0; i < res; ++i) + VERIFY(arfcns[i], ==, ((i + 1) * 2) - 1); +} + +static void test_print_encoding() +{ + int rc; + int w[17]; + uint8_t chan_list[16]; + memset(chan_list, 0x23, sizeof(chan_list)); + + for (rc = 0; rc < ARRAY_SIZE(w); ++rc) + switch (rc % 3) { + case 0: + w[rc] = 0xAAAA; + break; + case 1: + w[rc] = 0x5555; + break; + case 2: + w[rc] = 0x9696; + break; + } + + rc = range_enc_range512(chan_list, 0x96, w); + VERIFY(rc, ==, 0); + + printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list))); +} + +int main(int argc, char **argv) +{ + int ws[(sizeof(freqs1)/sizeof(freqs1[0]))]; + int i, f0 = 0xFFFFFF; + + memset(&ws[0], 0x23, sizeof(ws)); + + i = range_enc_find_index(1023, freqs1, ARRAY_SIZE(freqs1)); + printf("Element is: %d => freqs[i] = %d\n", i, freqs1[i]); + VERIFY(i, ==, 2); + + i = range_enc_find_index(511, freqs2, ARRAY_SIZE(freqs2)); + printf("Element is: %d => freqs[i] = %d\n", i, freqs2[i]); + VERIFY(i, ==, 2); + + i = range_enc_find_index(511, freqs3, ARRAY_SIZE(freqs3)); + printf("Element is: %d => freqs[i] = %d\n", i, freqs3[i]); + VERIFY(i, ==, 0); + + i = range_enc_arfcns(1023, freqs1, ARRAY_SIZE(freqs1), ws, 0); + VERIFY(i, ==, 0); + + for (i = 0; i < sizeof(freqs1)/sizeof(freqs1[0]); ++i) { + printf("w[%d]=%d\n", i, ws[i]); + VERIFY(ws[i], ==, w_out[i]); + } + + i = range_enc_determine_range(range128, ARRAY_SIZE(range128), &f0); + VERIFY(i, ==, ARFCN_RANGE_128); + VERIFY(f0, ==, 1); + + i = range_enc_determine_range(range256, ARRAY_SIZE(range256), &f0); + VERIFY(i, ==, ARFCN_RANGE_256); + VERIFY(f0, ==, 1); + + i = range_enc_determine_range(range512, ARRAY_SIZE(range512), &f0); + VERIFY(i, ==, ARFCN_RANGE_512); + VERIFY(f0, ==, 1); + + + test_arfcn_filter(); + test_print_encoding(); + + return 0; +} diff --git a/openbsc/tests/si/si_test.ok b/openbsc/tests/si/si_test.ok new file mode 100644 index 000000000..6c4b028b0 --- /dev/null +++ b/openbsc/tests/si/si_test.ok @@ -0,0 +1,20 @@ +Element is: 2 => freqs[i] = 121 +Element is: 2 => freqs[i] = 1 +Element is: 0 => freqs[i] = 68 +w[0]=122 +w[1]=2 +w[2]=69 +w[3]=204 +w[4]=75 +w[5]=66 +w[6]=60 +w[7]=70 +w[8]=83 +w[9]=3 +w[10]=24 +w[11]=67 +w[12]=54 +w[13]=64 +w[14]=70 +w[15]=9 +Range512: 88 4b 2a 95 65 95 55 2c a9 55 aa 55 6a 95 59 55 diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 22f0b74fd..8e1277312 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -37,3 +37,9 @@ AT_CHECK([test "$enable_nat_test" != no || exit 77]) cat $abs_srcdir/bsc-nat/bsc_nat_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bsc-nat/bsc_nat_test], [], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([si]) +AT_KEYWORDS([si]) +cat $abs_srcdir/si/si_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/si/si_test], [], [expout], [ignore]) +AT_CLEANUP