diff --git a/.gitignore b/.gitignore index 1c79ea9..ed2f90d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.o +*.a capi-test diff --git a/Makefile b/Makefile index 5c1071c..a78795a 100644 --- a/Makefile +++ b/Makefile @@ -7,12 +7,15 @@ CFLAGS += $(shell pkg-config --cflags talloc libosmocore capi20) all: capi-test clean: - @rm -f *.o capi-test + @rm -f *.o prbs/*.o *.a capi-test %.o: %.c $(CC) $(CFLAGS) -o $@ -c $^ -capi-test: capi.o capiconn.o bchan.o hdlr_raw_loop.o hdlr_raw_prbs.o +libprbs.a: prbs/prbs.o prbs/rx.o prbs/tx.o + $(AR) r $@ $^ + +capi-test: capi.o capiconn.o bchan.o hdlr_raw_loop.o hdlr_raw_prbs.o libprbs.a $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) diff --git a/bchan.h b/bchan.h index 046999b..8222d91 100644 --- a/bchan.h +++ b/bchan.h @@ -32,7 +32,7 @@ struct bchan_handler_ops { /* initialize whatever handler-private state (call setup) */ int (*init)(struct call_state *cst); /* receive data (ISDN -> handler) */ - void (*rx_data)(struct call_state *cst, const uint8_t *data, uint8_t len); + void (*rx_data)(struct call_state *cst, const uint8_t *data, size_t len); /* destroy whatever handler-private state (call teardown) */ void (*fini)(struct call_state *cst); }; diff --git a/capi.c b/capi.c index b3b8434..db9abec 100644 --- a/capi.c +++ b/capi.c @@ -253,7 +253,7 @@ struct capi_inst *capi_init(void *ctx) if (!ci) return NULL; - rc = capi20_register(1, 8, 128, &ci->applid); + rc = capi20_register(1, 8, 1280, &ci->applid); if (rc != 0) { fprintf(stderr, "Error in capi_register: %s\n", capi_info2str(rc)); talloc_free(ci); diff --git a/hdlr_raw_loop.c b/hdlr_raw_loop.c index c8439fe..d7cd4bc 100644 --- a/hdlr_raw_loop.c +++ b/hdlr_raw_loop.c @@ -1,3 +1,4 @@ +/* Raw/Transparent B-Channel loop-back */ #include "bchan.h" @@ -7,7 +8,7 @@ static int raw_loop_init(struct call_state *cst) return 0; } -static void raw_loop_rx(struct call_state *cst, const uint8_t *data, uint8_t len) +static void raw_loop_rx(struct call_state *cst, const uint8_t *data, size_t len) { /* echo whatever we received */ bchan_call_tx(cst, data, len); diff --git a/hdlr_raw_prbs.c b/hdlr_raw_prbs.c index 0eb6383..585fe9a 100644 --- a/hdlr_raw_prbs.c +++ b/hdlr_raw_prbs.c @@ -1,35 +1,44 @@ +/* Raw/Transparent B-channel with PRBS (pseudo-random bit sequence), + * useful for BERT (Bit Error Rate Test) */ #include -#include #include "bchan.h" #include "errno.h" +#include "prbs/prbs.h" + +#define PRBS_ORDER 11 +#define PRBS_BITLEN (1 << PRBS_ORDER) + struct raw_prbs_priv { - struct osmo_prbs_state prbs; + struct timeslot_state ts; }; static int raw_prbs_init(struct call_state *cst) { struct raw_prbs_priv *rpp; + rpp = cst->priv = talloc_zero(cst, struct raw_prbs_priv); if (!rpp) return -ENOMEM; - osmo_prbs_state_init(&rpp->prbs, &osmo_prbs15); + + ts_init_prbs_tx(&rpp->ts, 0); + ts_init_prbs_rx(&rpp->ts, 0); + return 0; } -static void raw_prbs_rx(struct call_state *cst, const uint8_t *data, uint8_t len) +static void raw_prbs_rx(struct call_state *cst, const uint8_t *data, size_t len) { struct raw_prbs_priv *rpp = cst->priv; - uint8_t tx_ubit[len*8]; - uint8_t tx_pbit[len]; - /* TODO: process what we received */ + uint8_t tx_buf[len]; + + prbs_process_rx(&rpp->ts.rx, data, len); /* generate respective number of PRBS bits and transmit them */ - osmo_prbs_get_ubits(tx_ubit, len, &rpp->prbs); - osmo_ubit2pbit(tx_pbit, tx_ubit, len*8); - bchan_call_tx(cst, tx_pbit, len); + prbs_process_tx(&rpp->ts, tx_buf, len); + bchan_call_tx(cst, tx_buf, len); } static void raw_prbs_fini(struct call_state *cst) diff --git a/prbs/prbs.c b/prbs/prbs.c new file mode 100644 index 0000000..664fe2d --- /dev/null +++ b/prbs/prbs.c @@ -0,0 +1,183 @@ +/* (C) 2019 by Harald Welte + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include "prbs.h" + +/* according to https://users.ece.cmu.edu/~koopman/lfsr/index.html all below + * coefficients should render maximal length LFSRs of 11bit (2048) length */ +static const uint32_t prbs11_coeff[] = { + 0x402, + 0x40B, + 0x415, + 0x416, + 0x423, + 0x431, + 0x432, + 0x438, + 0x43D, + 0x446, + 0x44A, + 0x44F, + 0x454, + 0x458, + 0x467, + 0x468, + 0x470, + 0x473, + 0x475, + 0x47A, + 0x486, + 0x489, + 0x492, + 0x494, + 0x49D, + 0x49E, + 0x4A2, + 0x4A4, + 0x4A8, + 0x4AD, + 0x4B9, + 0x4BA, + 0x4BF, + 0x4C1, + 0x4C7, + 0x4D5, + 0x4D6, + 0x4DC, + 0x4E3, + 0x4EC, + 0x4F2, + 0x4FB, + 0x500, + 0x503, + 0x509, + 0x50A, + 0x514, + 0x524, + 0x530, + 0x536, + 0x53C, + 0x53F, + 0x542, + 0x548, + 0x54E, + 0x553, + 0x555, + 0x559, + 0x55A, + 0x56A, + 0x56F, + 0x574, + 0x577, + 0x578, + 0x57D, + 0x581, + 0x584, + 0x588, + 0x599, + 0x59F, + 0x5A0, + 0x5A5, + 0x5AC, + 0x5AF, + 0x5B2, + 0x5B7, + 0x5BE, + 0x5C3, + 0x5C5, + 0x5C9, + 0x5CA, + 0x5D7, + 0x5DB, + 0x5DE, + 0x5E4, + 0x5ED, + 0x5EE, + 0x5F3, + 0x5F6, + 0x605, + 0x606, + 0x60C, + 0x60F, + 0x62B, + 0x630, + 0x635, + 0x639, + 0x642, + 0x644, + 0x64B +}; + +/* build the PRBS description for a given timeslot number */ +void prbs_for_ts_nr(struct osmo_prbs *prbs, uint8_t ts_nr) +{ + + OSMO_ASSERT(ts_nr < ARRAY_SIZE(prbs11_coeff)); + prbs->name = "custom"; + prbs->len = 11; + prbs->coeff = prbs11_coeff[ts_nr]; +} + +/* compute one full sequence of the given PRBS */ +void prbs_precomp(struct prbs_precomp *out, const struct osmo_prbs *prbs) +{ + struct osmo_prbs_state prbs_s; + int i; + + osmo_prbs_state_init(&prbs_s, prbs); + for (i = 0; i < sizeof(out->bytes); i++) { + ubit_t ubit[8]; + osmo_prbs_get_ubits(ubit, sizeof(ubit), &prbs_s); + osmo_ubit2pbit(&out->bytes[i], ubit, sizeof(ubit)); + } +} + +void ts_init_prbs_tx(struct timeslot_state *ts, unsigned int prbs_offs_tx) +{ + unsigned int prbs_nr = prbs_offs_tx + ts->ofd.priv_nr; + /* initialize the transmit-side PRNG for this slot */ + printf("Selecting PRBS11 #%02u for Tx of TS%02u\n", prbs_nr, ts->ofd.priv_nr); + prbs_for_ts_nr(&ts->tx.prbs, prbs_nr); + prbs_precomp(&ts->tx.prbs_pc, &ts->tx.prbs); +} + +void ts_init_prbs_rx(struct timeslot_state *ts, unsigned int prbs_offs_rx) +{ + unsigned int prbs_nr = prbs_offs_rx + ts->ofd.priv_nr; + /* initialize the receive-side PRNG for this slot */ + ubit_t ubit[PRBS_LEN*2]; + printf("Selecting PRBS11 #%02u for Rx of TS%02u\n", prbs_nr, ts->ofd.priv_nr); + prbs_for_ts_nr(&ts->rx.prbs, prbs_nr); + prbs_precomp(&ts->rx.prbs_pc[0], &ts->rx.prbs); + osmo_pbit2ubit(ubit, ts->rx.prbs_pc[0].bytes, PRBS_LEN); + /* copy buffer twice back-to-back */ + memcpy(ubit+PRBS_LEN, ubit, PRBS_LEN); + + /* pre-compute bit-shifted versions */ + for (int i = 1; i < ARRAY_SIZE(ts->rx.prbs_pc); i++) { + osmo_ubit2pbit_ext(ts->rx.prbs_pc[i].bytes, 0, ubit, i, PRBS_LEN, 0); + //printf("%d: %s\n", i, osmo_hexdump_nospc(ts->prbs_pc[i].bytes, sizeof(ts->prbs_pc[i].bytes))); + } +} diff --git a/prbs/prbs.h b/prbs/prbs.h new file mode 100644 index 0000000..ca3e54b --- /dev/null +++ b/prbs/prbs.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +#define MAX_NR_TS 32 +#define PRBS_LEN 2048 + +/* prbs.c */ + +struct timeslot_state; +struct prbs_precomp { + uint8_t bytes[PRBS_LEN/8]; +}; + +void prbs_for_ts_nr(struct osmo_prbs *prbs, uint8_t ts_nr); + +void prbs_precomp(struct prbs_precomp *out, const struct osmo_prbs *prbs); +void ts_init_prbs_tx(struct timeslot_state *ts, unsigned int prbs_offs_tx); +void ts_init_prbs_rx(struct timeslot_state *ts, unsigned int prbs_offs_rx); + +/* utils.c */ +uint8_t bits_set_in_byte(uint8_t byte); +void cfg_dahdi_buffer(int fd); +void set_realtime(int rt_prio); + + +struct timeslot_state_tx { + struct osmo_prbs prbs; /* PRBS definition */ + struct prbs_precomp prbs_pc; /* pre-computed PRBS bytes */ + unsigned int prbs_pc_idx; /* next to-be-transmitted byte offset in prbs_pc */ + bool active; /* started tx ? */ +}; + +struct timeslot_state_rx { + struct osmo_prbs prbs; /* PRBS definition */ + struct prbs_precomp prbs_pc[8]; /* bit-shifted pre-computed PRBS sequences */ + struct { + bool has_sync; /* do we have a PRBS sync? */ + struct timespec ts_sync; /* time at which sync was established */ + unsigned int prbs_pc_num; /* index to prbs_pc[] array */ + unsigned int prbs_pc_offset; /* offset of next byte into prbs_pc[pc_num].bytes[] */ + + unsigned int num_bit_err; /* bit errors since last sync */ + unsigned int num_sync_loss; /* number of sync losses since start */ + } sync_state; +}; + + +struct timeslot_state { + struct osmo_fd ofd; + struct timeslot_state_tx tx; + struct timeslot_state_rx rx; +}; + +struct test_state { + struct timeslot_state ts[MAX_NR_TS]; + unsigned int next_unused_ts; +}; + +/* rx.c */ +void prbs_process_rx(struct timeslot_state_rx *tsr, const uint8_t *data, unsigned int len); + +/* tx.c */ +void prbs_process_tx(struct timeslot_state *ts, uint8_t *buf, int len); diff --git a/prbs/rx.c b/prbs/rx.c new file mode 100644 index 0000000..0bebcb8 --- /dev/null +++ b/prbs/rx.c @@ -0,0 +1,119 @@ +/* (C) 2019 by Harald Welte + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "prbs.h" + +/* we could generate a lookup table at start ... */ +uint8_t bits_set_in_byte(uint8_t byte) +{ + uint8_t ret = 0; + int i; + + for (i = 0; i < 8; i++) { + if (byte & (1 << i)) + ret += 1; + } + return ret; +} + + +static uint8_t next_prbs_pc_byte(struct timeslot_state_rx *tsr) +{ + const struct prbs_precomp *pc = &tsr->prbs_pc[tsr->sync_state.prbs_pc_num]; + uint8_t ret = pc->bytes[tsr->sync_state.prbs_pc_offset]; + tsr->sync_state.prbs_pc_offset = (tsr->sync_state.prbs_pc_offset + 1) % sizeof(pc->bytes); + return ret; +} + +/* compare if received buffer matches PRBS; count number of different bits */ +static unsigned int compare_buf(struct timeslot_state_rx *tsr, const uint8_t *data, unsigned int len) +{ + unsigned int i, num_wrong_bits = 0; + + for (i = 0; i < len; i++) { + uint8_t bt = next_prbs_pc_byte(tsr); + if (data[i] != bt) { + uint8_t x = data[i] ^ bt; + num_wrong_bits += bits_set_in_byte(x); + } + } + return num_wrong_bits; +} + +/* process incoming received data; try to correlate with prbs sequence */ +void prbs_process_rx(struct timeslot_state_rx *tsr, const uint8_t *data, unsigned int len) +{ + if (!tsr->sync_state.has_sync) { + unsigned int pc_num; + /* we haven't synced yet and must attempt to sync to the pattern. We will try + * to match each pattern */ + for (pc_num = 0; pc_num < ARRAY_SIZE(tsr->prbs_pc); pc_num++) { + const struct prbs_precomp *pc = &tsr->prbs_pc[pc_num]; + uint8_t *found; + long int offset; + + OSMO_ASSERT(len > sizeof(pc->bytes)); + found = memmem(data, len, pc->bytes, sizeof(pc->bytes)); + if (!found) + continue; + + offset = (found - data); + printf("FOUND SYNC (pc_num=%u, offset=%li)\n", pc_num, offset); + clock_gettime(CLOCK_MONOTONIC, &tsr->sync_state.ts_sync); + tsr->sync_state.has_sync = true; + tsr->sync_state.prbs_pc_num = pc_num; + tsr->sync_state.prbs_pc_offset = (sizeof(pc->bytes) - offset) % sizeof(pc->bytes); + tsr->sync_state.num_bit_err = 0; + /* FIXME: compare the remainder of the buffer */ + return; + } + } + if (tsr->sync_state.has_sync) { + unsigned int num_wrong_bits; + /* we already have sync */ + num_wrong_bits = compare_buf(tsr, data, len); + if (num_wrong_bits >= len*8/4) { /* more than 25% of wrong bits */ + struct timespec ts_now; + clock_gettime(CLOCK_MONOTONIC, &ts_now); + printf("LOST SYNC after %u of %u wrong bits in one buffer; " + "until now, total bit errors %u in %lu seconds\n", + num_wrong_bits, len*8, tsr->sync_state.num_bit_err, + ts_now.tv_sec - tsr->sync_state.ts_sync.tv_sec); + tsr->sync_state.has_sync = false; + tsr->sync_state.num_sync_loss++; + } + tsr->sync_state.num_bit_err += num_wrong_bits; + } +} diff --git a/prbs/tx.c b/prbs/tx.c new file mode 100644 index 0000000..d13847a --- /dev/null +++ b/prbs/tx.c @@ -0,0 +1,43 @@ +/* (C) 2019 by Harald Welte + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "prbs.h" + +void prbs_process_tx(struct timeslot_state *ts, uint8_t *buf, int len) +{ + int i, rc; + + for (i = 0; i < len; i++) { + buf[i] = ts->tx.prbs_pc.bytes[ts->tx.prbs_pc_idx]; + ts->tx.prbs_pc_idx = (ts->tx.prbs_pc_idx + 1) % sizeof(ts->tx.prbs_pc); + } +}