Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/src/bert.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 15:50:58 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.6pre17/src/bert.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,512 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * bert.c - Bit error rate tests. + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2004 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1, + * as published by the Free Software Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: bert.c,v 1.33 2009/04/14 16:04:53 steveu Exp $ + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <time.h> + +#include "spandsp/telephony.h" +#include "spandsp/logging.h" +#include "spandsp/async.h" +#include "spandsp/bert.h" + +#include "spandsp/private/logging.h" +#include "spandsp/private/bert.h" + +#define MEASUREMENT_STEP 100 + +static const char *qbf = "VoyeZ Le BricK GeanT QuE J'ExaminE PreS Du WharF 123 456 7890 + - * : = $ % ( )" + "ThE QuicK BrowN FoX JumpS OveR ThE LazY DoG 123 456 7890 + - * : = $ % ( )"; + +SPAN_DECLARE(const char *) bert_event_to_str(int event) +{ + switch (event) + { + case BERT_REPORT_SYNCED: + return "synced"; + case BERT_REPORT_UNSYNCED: + return "unsync'ed"; + case BERT_REPORT_REGULAR: + return "regular"; + case BERT_REPORT_GT_10_2: + return "error rate > 1 in 10^2"; + case BERT_REPORT_LT_10_2: + return "error rate < 1 in 10^2"; + case BERT_REPORT_LT_10_3: + return "error rate < 1 in 10^3"; + case BERT_REPORT_LT_10_4: + return "error rate < 1 in 10^4"; + case BERT_REPORT_LT_10_5: + return "error rate < 1 in 10^5"; + case BERT_REPORT_LT_10_6: + return "error rate < 1 in 10^6"; + case BERT_REPORT_LT_10_7: + return "error rate < 1 in 10^7"; + } + return "???"; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) bert_get_bit(bert_state_t *s) +{ + int bit; + + if (s->limit && s->tx.bits >= s->limit) + return SIG_STATUS_END_OF_DATA; + bit = 0; + switch (s->pattern_class) + { + case 0: + bit = s->tx.reg & 1; + s->tx.reg = (s->tx.reg >> 1) | ((s->tx.reg & 1) << s->shift2); + break; + case 1: + bit = s->tx.reg & 1; + s->tx.reg = (s->tx.reg >> 1) | (((s->tx.reg ^ (s->tx.reg >> s->shift)) & 1) << s->shift2); + if (s->max_zeros) + { + /* This generator suppresses runs >s->max_zeros */ + if (bit) + { + if (++s->tx.zeros > s->max_zeros) + { + s->tx.zeros = 0; + bit ^= 1; + } + } + else + { + s->tx.zeros = 0; + } + } + bit ^= s->invert; + break; + case 2: + if (s->tx.step_bit == 0) + { + s->tx.step_bit = 7; + s->tx.reg = qbf[s->tx.step++]; + if (s->tx.reg == 0) + { + s->tx.reg = 'V'; + s->tx.step = 1; + } + } + bit = s->tx.reg & 1; + s->tx.reg >>= 1; + s->tx.step_bit--; + break; + } + s->tx.bits++; + return bit; +} +/*- End of function --------------------------------------------------------*/ + +static void assess_error_rate(bert_state_t *s) +{ + int i; + int j; + int sum; + int test; + + /* We assess the error rate in decadic steps. For each decade we assess the error over 10 times + the number of bits, to smooth the result. This means we assess the 1 in 100 rate based on 1000 bits + (i.e. we look for >=10 errors in 1000 bits). We make an assessment every 100 bits, using a sliding + window over the last 1000 bits. We assess the 1 in 1000 rate over 10000 bits in a similar way, and + so on for the lower error rates. */ + test = TRUE; + for (i = 2; i <= 7; i++) + { + if (++s->decade_ptr[i] < 10) + break; + /* This decade has reached 10 snapshots, so we need to touch the next decade */ + s->decade_ptr[i] = 0; + /* Sum the last 10 snapshots from this decade, to see if we overflow into the next decade */ + for (sum = 0, j = 0; j < 10; j++) + sum += s->decade_bad[i][j]; + if (test && sum > 10) + { + /* We overflow into the next decade */ + test = FALSE; + if (s->error_rate != i && s->reporter) + s->reporter(s->user_data, BERT_REPORT_GT_10_2 + i - 2, &s->results); + s->error_rate = i; + } + s->decade_bad[i][0] = 0; + if (i < 7) + s->decade_bad[i + 1][s->decade_ptr[i + 1]] = sum; + } + if (i > 7) + { + if (s->decade_ptr[i] >= 10) + s->decade_ptr[i] = 0; + if (test) + { + if (s->error_rate != i && s->reporter) + s->reporter(s->user_data, BERT_REPORT_GT_10_2 + i - 2, &s->results); + s->error_rate = i; + } + } + else + { + s->decade_bad[i][s->decade_ptr[i]] = 0; + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) bert_put_bit(bert_state_t *s, int bit) +{ + if (bit < 0) + { + /* Special conditions */ + printf("Status is %s (%d)\n", signal_status_to_str(bit), bit); + return; + } + bit = (bit & 1) ^ s->invert; + s->rx.bits++; + switch (s->pattern_class) + { + case 0: + if (s->rx.resync) + { + s->rx.reg = (s->rx.reg >> 1) | (bit << s->shift2); + s->rx.ref_reg = (s->rx.ref_reg >> 1) | ((s->rx.ref_reg & 1) << s->shift2); + if (s->rx.reg == s->rx.ref_reg) + { + if (++s->rx.resync > s->resync_time) + { + s->rx.resync = 0; + if (s->reporter) + s->reporter(s->user_data, BERT_REPORT_SYNCED, &s->results); + } + } + else + { + s->rx.resync = 2; + s->rx.ref_reg = s->rx.master_reg; + } + } + else + { + s->results.total_bits++; + if ((bit ^ s->rx.ref_reg) & 1) + s->results.bad_bits++; + s->rx.ref_reg = (s->rx.ref_reg >> 1) | ((s->rx.ref_reg & 1) << s->shift2); + } + break; + case 1: + if (s->rx.resync) + { + /* If we get a reasonable period for which we correctly predict the + next bit, we must be in sync. */ + /* Don't worry about max. zeros tests when resyncing. + It might just extend the resync time a little. Trying + to include the test might affect robustness. */ + if (bit == (int) ((s->rx.reg >> s->shift) & 1)) + { + if (++s->rx.resync > s->resync_time) + { + s->rx.resync = 0; + if (s->reporter) + s->reporter(s->user_data, BERT_REPORT_SYNCED, &s->results); + } + } + else + { + s->rx.resync = 2; + s->rx.reg ^= s->mask; + } + } + else + { + s->results.total_bits++; + if (s->max_zeros) + { + /* This generator suppresses runs >s->max_zeros */ + if ((s->rx.reg & s->mask)) + { + if (++s->rx.zeros > s->max_zeros) + { + s->rx.zeros = 0; + bit ^= 1; + } + } + else + { + s->rx.zeros = 0; + } + } + if (bit != (int) ((s->rx.reg >> s->shift) & 1)) + { + s->results.bad_bits++; + s->rx.resync_bad_bits++; + s->decade_bad[2][s->decade_ptr[2]]++; + } + if (--s->rx.measurement_step <= 0) + { + /* Every hundred bits we need to do the error rate measurement */ + s->rx.measurement_step = MEASUREMENT_STEP; + assess_error_rate(s); + } + if (--s->rx.resync_cnt <= 0) + { + /* Check if there were enough bad bits during this period to + justify a resync. */ + if (s->rx.resync_bad_bits >= (s->rx.resync_len*s->rx.resync_percent)/100) + { + s->rx.resync = 1; + s->results.resyncs++; + if (s->reporter) + s->reporter(s->user_data, BERT_REPORT_UNSYNCED, &s->results); + } + s->rx.resync_cnt = s->rx.resync_len; + s->rx.resync_bad_bits = 0; + } + } + s->rx.reg = (s->rx.reg >> 1) | (((s->rx.reg ^ (s->rx.reg >> s->shift)) & 1) << s->shift2); + break; + case 2: + s->rx.reg = (s->rx.reg >> 1) | (bit << 6); + /* TODO: There is no mechanism for synching yet. This only works if things start in sync. */ + if (++s->rx.step_bit == 7) + { + s->rx.step_bit = 0; + if ((int) s->rx.reg != qbf[s->rx.step]) + { + /* We need to work out the number of actual bad bits here. We need to look at the + error rate, and see it a resync is needed. etc. */ + s->results.bad_bits++; + } + if (qbf[++s->rx.step] == '\0') + s->rx.step = 0; + } + s->results.total_bits++; + break; + } + if (s->report_frequency > 0) + { + if (--s->rx.report_countdown <= 0) + { + if (s->reporter) + s->reporter(s->user_data, BERT_REPORT_REGULAR, &s->results); + s->rx.report_countdown = s->report_frequency; + } + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) bert_result(bert_state_t *s, bert_results_t *results) +{ + results->total_bits = s->results.total_bits; + results->bad_bits = s->results.bad_bits; + results->resyncs = s->results.resyncs; + return sizeof(*results); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) bert_set_report(bert_state_t *s, int freq, bert_report_func_t reporter, void *user_data) +{ + s->report_frequency = freq; + s->reporter = reporter; + s->user_data = user_data; + + s->rx.report_countdown = s->report_frequency; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(bert_state_t *) bert_init(bert_state_t *s, int limit, int pattern, int resync_len, int resync_percent) +{ + int i; + int j; + + if (s == NULL) + { + if ((s = (bert_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + } + memset(s, 0, sizeof(*s)); + + s->pattern = pattern; + s->limit = limit; + s->reporter = NULL; + s->user_data = NULL; + s->report_frequency = 0; + + s->resync_time = 72; + s->invert = 0; + switch (s->pattern) + { + case BERT_PATTERN_ZEROS: + s->tx.reg = 0; + s->shift2 = 31; + s->pattern_class = 0; + break; + case BERT_PATTERN_ONES: + s->tx.reg = ~((uint32_t) 0); + s->shift2 = 31; + s->pattern_class = 0; + break; + case BERT_PATTERN_7_TO_1: + s->tx.reg = 0xFEFEFEFE; + s->shift2 = 31; + s->pattern_class = 0; + break; + case BERT_PATTERN_3_TO_1: + s->tx.reg = 0xEEEEEEEE; + s->shift2 = 31; + s->pattern_class = 0; + break; + case BERT_PATTERN_1_TO_1: + s->tx.reg = 0xAAAAAAAA; + s->shift2 = 31; + s->pattern_class = 0; + break; + case BERT_PATTERN_1_TO_3: + s->tx.reg = 0x11111111; + s->shift2 = 31; + s->pattern_class = 0; + break; + case BERT_PATTERN_1_TO_7: + s->tx.reg = 0x01010101; + s->shift2 = 31; + s->pattern_class = 0; + break; + case BERT_PATTERN_QBF: + s->tx.reg = 0; + s->pattern_class = 2; + break; + case BERT_PATTERN_ITU_O151_23: + s->pattern_class = 1; + s->tx.reg = 0x7FFFFF; + s->mask = 0x20; + s->shift = 5; + s->shift2 = 22; + s->invert = 1; + s->resync_time = 56; + s->max_zeros = 0; + break; + case BERT_PATTERN_ITU_O151_20: + s->pattern_class = 1; + s->tx.reg = 0xFFFFF; + s->mask = 0x8; + s->shift = 3; + s->shift2 = 19; + s->invert = 1; + s->resync_time = 50; + s->max_zeros = 14; + break; + case BERT_PATTERN_ITU_O151_15: + s->pattern_class = 1; + s->tx.reg = 0x7FFF; + s->mask = 0x2; + s->shift = 1; + s->shift2 = 14; + s->invert = 1; + s->resync_time = 40; + s->max_zeros = 0; + break; + case BERT_PATTERN_ITU_O152_11: + s->pattern_class = 1; + s->tx.reg = 0x7FF; + s->mask = 0x4; + s->shift = 2; + s->shift2 = 10; + s->invert = 0; + s->resync_time = 32; + s->max_zeros = 0; + break; + case BERT_PATTERN_ITU_O153_9: + s->pattern_class = 1; + s->tx.reg = 0x1FF; + s->mask = 0x10; + s->shift = 4; + s->shift2 = 8; + s->invert = 0; + s->resync_time = 28; + s->max_zeros = 0; + break; + } + s->tx.bits = 0; + s->tx.step = 0; + s->tx.step_bit = 0; + s->tx.zeros = 0; + + s->rx.reg = s->tx.reg; + s->rx.ref_reg = s->rx.reg; + s->rx.master_reg = s->rx.ref_reg; + s->rx.bits = 0; + s->rx.step = 0; + s->rx.step_bit = 0; + + s->rx.resync = 1; + s->rx.resync_cnt = resync_len; + s->rx.resync_bad_bits = 0; + s->rx.resync_len = resync_len; + s->rx.resync_percent = resync_percent; + + s->results.total_bits = 0; + s->results.bad_bits = 0; + s->results.resyncs = 0; + + s->rx.report_countdown = 0; + + for (i = 0; i < 8; i++) + { + for (j = 0; j < 10; j++) + s->decade_bad[i][j] = 0; + s->decade_ptr[i] = 0; + } + s->error_rate = 8; + s->rx.measurement_step = MEASUREMENT_STEP; + + span_log_init(&s->logging, SPAN_LOG_NONE, NULL); + span_log_set_protocol(&s->logging, "BERT"); + + return s; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) bert_release(bert_state_t *s) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) bert_free(bert_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/