hdlr_raw_prbs: More complete implementation, lifted from osmo-e1d/e1-prbs-gen

This commit is contained in:
Harald Welte 2022-04-15 20:52:05 +02:00
parent 88e00cbc78
commit e648c025ac
10 changed files with 440 additions and 15 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
*.o *.o
*.a
capi-test capi-test

View File

@ -7,12 +7,15 @@ CFLAGS += $(shell pkg-config --cflags talloc libosmocore capi20)
all: capi-test all: capi-test
clean: clean:
@rm -f *.o capi-test @rm -f *.o prbs/*.o *.a capi-test
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) -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) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)

View File

@ -32,7 +32,7 @@ struct bchan_handler_ops {
/* initialize whatever handler-private state (call setup) */ /* initialize whatever handler-private state (call setup) */
int (*init)(struct call_state *cst); int (*init)(struct call_state *cst);
/* receive data (ISDN -> handler) */ /* 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) */ /* destroy whatever handler-private state (call teardown) */
void (*fini)(struct call_state *cst); void (*fini)(struct call_state *cst);
}; };

2
capi.c
View File

@ -253,7 +253,7 @@ struct capi_inst *capi_init(void *ctx)
if (!ci) if (!ci)
return NULL; return NULL;
rc = capi20_register(1, 8, 128, &ci->applid); rc = capi20_register(1, 8, 1280, &ci->applid);
if (rc != 0) { if (rc != 0) {
fprintf(stderr, "Error in capi_register: %s\n", capi_info2str(rc)); fprintf(stderr, "Error in capi_register: %s\n", capi_info2str(rc));
talloc_free(ci); talloc_free(ci);

View File

@ -1,3 +1,4 @@
/* Raw/Transparent B-Channel loop-back */
#include "bchan.h" #include "bchan.h"
@ -7,7 +8,7 @@ static int raw_loop_init(struct call_state *cst)
return 0; 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 */ /* echo whatever we received */
bchan_call_tx(cst, data, len); bchan_call_tx(cst, data, len);

View File

@ -1,35 +1,44 @@
/* Raw/Transparent B-channel with PRBS (pseudo-random bit sequence),
* useful for BERT (Bit Error Rate Test) */
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <osmocom/core/prbs.h>
#include "bchan.h" #include "bchan.h"
#include "errno.h" #include "errno.h"
#include "prbs/prbs.h"
#define PRBS_ORDER 11
#define PRBS_BITLEN (1 << PRBS_ORDER)
struct raw_prbs_priv { struct raw_prbs_priv {
struct osmo_prbs_state prbs; struct timeslot_state ts;
}; };
static int raw_prbs_init(struct call_state *cst) static int raw_prbs_init(struct call_state *cst)
{ {
struct raw_prbs_priv *rpp; struct raw_prbs_priv *rpp;
rpp = cst->priv = talloc_zero(cst, struct raw_prbs_priv); rpp = cst->priv = talloc_zero(cst, struct raw_prbs_priv);
if (!rpp) if (!rpp)
return -ENOMEM; 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; 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; struct raw_prbs_priv *rpp = cst->priv;
uint8_t tx_ubit[len*8]; uint8_t tx_buf[len];
uint8_t tx_pbit[len];
/* TODO: process what we received */ prbs_process_rx(&rpp->ts.rx, data, len);
/* generate respective number of PRBS bits and transmit them */ /* generate respective number of PRBS bits and transmit them */
osmo_prbs_get_ubits(tx_ubit, len, &rpp->prbs); prbs_process_tx(&rpp->ts, tx_buf, len);
osmo_ubit2pbit(tx_pbit, tx_ubit, len*8); bchan_call_tx(cst, tx_buf, len);
bchan_call_tx(cst, tx_pbit, len);
} }
static void raw_prbs_fini(struct call_state *cst) static void raw_prbs_fini(struct call_state *cst)

183
prbs/prbs.c Normal file
View File

@ -0,0 +1,183 @@
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* 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 <stdint.h>
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/prbs.h>
#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)));
}
}

66
prbs/prbs.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/prbs.h>
#include <osmocom/core/select.h>
#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);

119
prbs/rx.c Normal file
View File

@ -0,0 +1,119 @@
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* 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 <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <osmocom/core/select.h>
#include <osmocom/core/utils.h>
#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;
}
}

43
prbs/tx.c Normal file
View File

@ -0,0 +1,43 @@
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <osmocom/core/select.h>
#include <osmocom/core/utils.h>
#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);
}
}