diff --git a/.gitignore b/.gitignore
index 1a2cdbd..42412a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+._*
.deps
.libs
*.o
diff --git a/A51.cpp b/A51.cpp
deleted file mode 100644
index 1e9098d..0000000
--- a/A51.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * A pedagogical implementation of A5/1.
- *
- * Copyright (C) 1998-1999: Marc Briceno, Ian Goldberg, and David Wagner
- *
- * The source code below is optimized for instructional value and clarity.
- * Performance will be terrible, but that's not the point.
- * The algorithm is written in the C programming language to avoid ambiguities
- * inherent to the English language. Complain to the 9th Circuit of Appeals
- * if you have a problem with that.
- *
- * This software may be export-controlled by US law.
- *
- * This software is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.
- * Copyright remains the authors' and as such any Copyright notices in
- * the code are not to be removed.
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The license and distribution terms for any publicly available version or
- * derivative of this code cannot be changed. i.e. this code cannot simply be
- * copied and put under another distribution license
- * [including the GNU Public License.]
- *
- * Background: The Global System for Mobile communications is the most widely
- * deployed cellular telephony system in the world. GSM makes use of
- * four core cryptographic algorithms, neither of which has been published by
- * the GSM MOU. This failure to subject the algorithms to public review is all
- * the more puzzling given that over 100 million GSM
- * subscribers are expected to rely on the claimed security of the system.
- *
- * The four core GSM algorithms are:
- * A3 authentication algorithm
- * A5/1 "strong" over-the-air voice-privacy algorithm
- * A5/2 "weak" over-the-air voice-privacy algorithm
- * A8 voice-privacy key generation algorithm
- *
- * In April of 1998, our group showed that COMP128, the algorithm used by the
- * overwhelming majority of GSM providers for both A3 and A8
- * functionality was fatally flawed and allowed for cloning of GSM mobile
- * phones.
- * Furthermore, we demonstrated that all A8 implementations we could locate,
- * including the few that did not use COMP128 for key generation, had been
- * deliberately weakened by reducing the keyspace from 64 bits to 54 bits.
- * The remaining 10 bits are simply set to zero!
- *
- * See http://www.scard.org/gsm for additional information.
- *
- * The question so far unanswered is if A5/1, the "stronger" of the two
- * widely deployed voice-privacy algorithm is at least as strong as the
- * key. Meaning: "Does A5/1 have a work factor of at least 54 bits"?
- * Absent a publicly available A5/1 reference implementation, this question
- * could not be answered. We hope that our reference implementation below,
- * which has been verified against official A5/1 test vectors, will provide
- * the cryptographic community with the base on which to construct the
- * answer to this important question.
- *
- * Initial indications about the strength of A5/1 are not encouraging.
- * A variant of A5, while not A5/1 itself, has been estimated to have a
- * work factor of well below 54 bits. See http://jya.com/crack-a5.htm for
- * background information and references.
- *
- * With COMP128 broken and A5/1 published below, we will now turn our attention
- * to A5/2. The latter has been acknowledged by the GSM community to have
- * been specifically designed by intelligence agencies for lack of security.
- *
- */
-
-
-#include
-#include
-#include "A51.h"
-
-/* Masks for the three shift registers */
-#define R1MASK 0x07FFFF /* 19 bits, numbered 0..18 */
-#define R2MASK 0x3FFFFF /* 22 bits, numbered 0..21 */
-#define R3MASK 0x7FFFFF /* 23 bits, numbered 0..22 */
-
-/* Middle bit of each of the three shift registers, for clock control */
-#define R1MID 0x000100 /* bit 8 */
-#define R2MID 0x000400 /* bit 10 */
-#define R3MID 0x000400 /* bit 10 */
-
-/* The three shift registers. They're in global variables to make the code
- * easier to understand.
- * A better implementation would not use global variables. */
-word R1, R2, R3;
-
-/* Look at the middle bits of R1,R2,R3, take a vote, and
- * return the majority value of those 3 bits. */
-bit majority() {
- int sum;
- sum = ((R1>>8)&1) + ((R2>>10)&1) + ((R3>>10)&1);
- if (sum >= 2)
- return 1;
- else
- return 0;
-}
-
-/* Clock two or three of R1,R2,R3, with clock control
- * according to their middle bits.
- * Specifically, we clock Ri whenever Ri's middle bit
- * agrees with the majority value of the three middle bits.*/
-void clock() {
- bit maj = majority();
- if (((R1&R1MID)!=0) == maj)
- R1 = ((R1<<1) & R1MASK) | (1 & (R1>>18 ^ R1>>17 ^ R1>>16 ^ R1>>13));
- if (((R2&R2MID)!=0) == maj)
- R2 = ((R2<<1) & R2MASK) | (1 & (R2>>21 ^ R2>>20));
- if (((R3&R3MID)!=0) == maj)
- R3 = ((R3<<1) & R3MASK) | (1 & (R3>>22 ^ R3>>21 ^ R3>>20 ^ R3>>7));
-}
-
-/* Clock all three of R1,R2,R3, ignoring their middle bits.
- * This is only used for key setup. */
-void clockallthree() {
- R1 = ((R1<<1) & R1MASK) | (1 & (R1>>18 ^ R1>>17 ^ R1>>16 ^ R1>>13));
- R2 = ((R2<<1) & R2MASK) | (1 & (R2>>21 ^ R2>>20));
- R3 = ((R3<<1) & R3MASK) | (1 & (R3>>22 ^ R3>>21 ^ R3>>20 ^ R3>>7));
-}
-
-/* Generate an output bit from the current state.
- * You grab a bit from each register via the output generation taps;
- * then you XOR the resulting three bits. */
-bit getbit() {
- return ((R1>>18)^(R2>>21)^(R3>>22))&1;
-}
-
-/* Do the A5/1 key setup. This routine accepts a 64-bit key and
- * a 22-bit frame number. */
-void keysetup(byte key[8], word frame) {
- int i;
- bit keybit, framebit;
-
- /* Zero out the shift registers. */
- R1 = R2 = R3 = 0;
-
- /* Load the key into the shift registers,
- * LSB of first byte of key array first,
- * clocking each register once for every
- * key bit loaded. (The usual clock
- * control rule is temporarily disabled.) */
- for (i=0; i<64; i++) {
- clockallthree(); /* always clock */
- keybit = (key[i/8] >> (i&7)) & 1; /* The i-th bit of the
-key */
- R1 ^= keybit; R2 ^= keybit; R3 ^= keybit;
- }
-
- /* Load the frame number into the shift
- * registers, LSB first,
- * clocking each register once for every
- * key bit loaded. (The usual clock
- * control rule is still disabled.) */
- for (i=0; i<22; i++) {
- clockallthree(); /* always clock */
- framebit = (frame >> i) & 1; /* The i-th bit of the frame #
-*/
- R1 ^= framebit; R2 ^= framebit; R3 ^= framebit;
- }
-
- /* Run the shift registers for 100 clocks
- * to mix the keying material and frame number
- * together with output generation disabled,
- * so that there is sufficient avalanche.
- * We re-enable the majority-based clock control
- * rule from now on. */
- for (i=0; i<100; i++) {
- clock();
- }
-
- /* Now the key is properly set up. */
-}
-
-/* Generate output. We generate 228 bits of
- * keystream output. The first 114 bits is for
- * the A->B frame; the next 114 bits is for the
- * B->A frame. You allocate a 15-byte buffer
- * for each direction, and this function fills
- * it in. */
-void run(byte AtoBkeystream[], byte BtoAkeystream[]) {
- int i;
-
- /* Zero out the output buffers. */
- for (i=0; i<=113/8; i++)
- AtoBkeystream[i] = BtoAkeystream[i] = 0;
-
- /* Generate 114 bits of keystream for the
- * A->B direction. Store it, MSB first. */
- for (i=0; i<114; i++) {
- clock();
- AtoBkeystream[i/8] |= getbit() << (7-(i&7));
- }
-
- /* Generate 114 bits of keystream for the
- * B->A direction. Store it, MSB first. */
- for (i=0; i<114; i++) {
- clock();
- BtoAkeystream[i/8] |= getbit() << (7-(i&7));
- }
-}
-
-void A51_GSM( byte *key, int klen, int count, byte *block1, byte *block2 )
-{
- assert(klen == 64);
- keysetup(key, count); // TODO - frame and count are not the same
- run(block1, block2);
-}
diff --git a/A51.h b/A51.h
deleted file mode 100644
index d1337de..0000000
--- a/A51.h
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-#include
-#include
-
-typedef unsigned char byte;
-typedef unsigned long word;
-typedef word bit;
-
-void A51_GSM( byte *key, int klen, int count, byte *block1, byte *block2 );
-
diff --git a/A51Test.cpp b/A51Test.cpp
deleted file mode 100644
index 57ce467..0000000
--- a/A51Test.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * A pedagogical implementation of A5/1.
- *
- * Copyright (C) 1998-1999: Marc Briceno, Ian Goldberg, and David Wagner
- *
- * The source code below is optimized for instructional value and clarity.
- * Performance will be terrible, but that's not the point.
- * The algorithm is written in the C programming language to avoid ambiguities
- * inherent to the English language. Complain to the 9th Circuit of Appeals
- * if you have a problem with that.
- *
- * This software may be export-controlled by US law.
- *
- * This software is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.
- * Copyright remains the authors' and as such any Copyright notices in
- * the code are not to be removed.
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The license and distribution terms for any publicly available version or
- * derivative of this code cannot be changed. i.e. this code cannot simply be
- * copied and put under another distribution license
- * [including the GNU Public License.]
- *
- * Background: The Global System for Mobile communications is the most widely
- * deployed cellular telephony system in the world. GSM makes use of
- * four core cryptographic algorithms, neither of which has been published by
- * the GSM MOU. This failure to subject the algorithms to public review is all
- * the more puzzling given that over 100 million GSM
- * subscribers are expected to rely on the claimed security of the system.
- *
- * The four core GSM algorithms are:
- * A3 authentication algorithm
- * A5/1 "strong" over-the-air voice-privacy algorithm
- * A5/2 "weak" over-the-air voice-privacy algorithm
- * A8 voice-privacy key generation algorithm
- *
- * In April of 1998, our group showed that COMP128, the algorithm used by the
- * overwhelming majority of GSM providers for both A3 and A8
- * functionality was fatally flawed and allowed for cloning of GSM mobile
- * phones.
- * Furthermore, we demonstrated that all A8 implementations we could locate,
- * including the few that did not use COMP128 for key generation, had been
- * deliberately weakened by reducing the keyspace from 64 bits to 54 bits.
- * The remaining 10 bits are simply set to zero!
- *
- * See http://www.scard.org/gsm for additional information.
- *
- * The question so far unanswered is if A5/1, the "stronger" of the two
- * widely deployed voice-privacy algorithm is at least as strong as the
- * key. Meaning: "Does A5/1 have a work factor of at least 54 bits"?
- * Absent a publicly available A5/1 reference implementation, this question
- * could not be answered. We hope that our reference implementation below,
- * which has been verified against official A5/1 test vectors, will provide
- * the cryptographic community with the base on which to construct the
- * answer to this important question.
- *
- * Initial indications about the strength of A5/1 are not encouraging.
- * A variant of A5, while not A5/1 itself, has been estimated to have a
- * work factor of well below 54 bits. See http://jya.com/crack-a5.htm for
- * background information and references.
- *
- * With COMP128 broken and A5/1 published below, we will now turn our attention
- * to A5/2. The latter has been acknowledged by the GSM community to have
- * been specifically designed by intelligence agencies for lack of security.
- *
- */
-
-
-#include
-#include
-#include
-#include "./A51.h"
-
-
-/* Test the code by comparing it against
- * a known-good test vector. */
-void test() {
- byte key[8] = {0x12, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
- word frame = 0x134;
- byte goodAtoB[15] = { 0x53, 0x4E, 0xAA, 0x58, 0x2F, 0xE8, 0x15,
- 0x1A, 0xB6, 0xE1, 0x85, 0x5A, 0x72, 0x8C, 0x00 };
- byte goodBtoA[15] = { 0x24, 0xFD, 0x35, 0xA3, 0x5D, 0x5F, 0xB6,
- 0x52, 0x6D, 0x32, 0xF9, 0x06, 0xDF, 0x1A, 0xC0 };
- byte AtoB[15], BtoA[15];
- int i, failed=0;
-
- A51_GSM(key, 64, frame, AtoB, BtoA);
-
- /* Compare against the test vector. */
- for (i=0; i<15; i++)
- if (AtoB[i] != goodAtoB[i])
- failed = 1;
- for (i=0; i<15; i++)
- if (BtoA[i] != goodBtoA[i])
- failed = 1;
-
- /* Print some debugging output. */
- printf("key: 0x");
- for (i=0; i<8; i++)
- printf("%02X", key[i]);
- printf("\n");
- printf("frame number: 0x%06X\n", (unsigned int)frame);
- printf("known good output:\n");
- printf(" A->B: 0x");
- for (i=0; i<15; i++)
- printf("%02X", goodAtoB[i]);
- printf(" B->A: 0x");
- for (i=0; i<15; i++)
- printf("%02X", goodBtoA[i]);
- printf("\n");
- printf("observed output:\n");
- printf(" A->B: 0x");
- for (i=0; i<15; i++)
- printf("%02X", AtoB[i]);
- printf(" B->A: 0x");
- for (i=0; i<15; i++)
- printf("%02X", BtoA[i]);
- printf("\n");
-
- if (!failed) {
- printf("Self-check succeeded: everything looks ok.\n");
- } else {
- /* Problems! The test vectors didn't compare*/
- printf("\nI don't know why this broke; contact the authors.\n");
- exit(1);
- }
-
- printf("time test\n");
- int n = 10000;
- float t = clock();
- for (i = 0; i < n; i++) {
- A51_GSM(key, 64, frame, AtoB, BtoA);
- }
- t = (clock() - t) / (CLOCKS_PER_SEC * (float)n);
- printf("A51_GSM takes %g seconds per iteration\n", t);
-}
-
-int main(void) {
- test();
- return 0;
-}
diff --git a/BitVector.cpp b/BitVector.cpp
index 7487834..a36ab7b 100644
--- a/BitVector.cpp
+++ b/BitVector.cpp
@@ -1,5 +1,5 @@
/*
-* Copyright 2008, 2009 Free Software Foundation, Inc.
+* Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
@@ -30,38 +30,26 @@
#include
#include
#include
+#include
using namespace std;
-/**
- Apply a Galois polymonial to a binary seqeunce.
- @param val The input sequence.
- @param poly The polynomial.
- @param order The order of the polynomial.
- @return Single-bit result.
-*/
-unsigned applyPoly(uint64_t val, uint64_t poly, unsigned order)
-{
- uint64_t prod = val & poly;
- unsigned sum = prod;
- for (unsigned i=1; i>i;
- return sum & 0x01;
-}
-
-
-
-
-
BitVector::BitVector(const char *valString)
- :Vector(strlen(valString))
{
- uint32_t accum = 0;
- for (size_t i=0; iiState) << 1; // input state for 0
- const uint32_t iState1 = iState0 | 0x01; // input state for 1
- const uint32_t oStateShifted = (sp->oState) << mIRate; // shifted output
- const float cost = sp->cost;
- sp++;
- // 0 input extension
- mCandidates[i].cost = cost;
- mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask];
- mCandidates[i].iState = iState0;
- // 1 input extension
- mCandidates[i+1].cost = cost;
- mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask];
- mCandidates[i+1].iState = iState1;
- }
-}
-
-
-void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost)
-{
- const float *cTab[2] = {matchCost,mismatchCost};
- for (unsigned i=0; i>1)&0x01][0];
- }
-}
-
-
-void ViterbiR2O4::pruneCandidates()
-{
- const vCand* c1 = mCandidates; // 0-prefix
- const vCand* c2 = mCandidates + mIStates; // 1-prefix
- for (unsigned i=0; i=minCost) continue;
- minCost = thisCost;
- minIndex=i;
- }
- return mSurvivors[minIndex];
-}
-
-
-const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs)
-{
- branchCandidates();
- getSoftCostMetrics(inSample,probs,iprobs);
- pruneCandidates();
- return minCost();
-}
-
-
uint64_t Parity::syndrome(const BitVector& receivedCodeword)
{
return receivedCodeword.syndrome(*this);
@@ -446,85 +294,6 @@ BitVector SoftVector::sliced() const
-void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
-{
- const size_t sz = size();
- const unsigned deferral = decoder.deferral();
- const size_t ctsz = sz + deferral*decoder.iRate();
- assert(sz <= decoder.iRate()*target.size());
-
- // Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
- {
- BitVector bits = sliced();
- uint32_t accum = 0;
- for (size_t i=0; i0.5F) pVal = 1.0F-pVal;
- float ipVal = 1.0F-pVal;
- // This is a cheap approximation to an ideal cost function.
- if (pVal<0.01F) pVal = 0.01;
- if (ipVal<0.01F) ipVal = 0.01;
- matchCostTable[i] = 0.25F/ipVal;
- mismatchCostTable[i] = 0.25F/pVal;
- }
-
- // pad end of table with unknowns
- for (size_t i=sz; i=deferral) *op++ = (minCost.iState >> deferral)&0x01;
- oCount++;
- }
- }
-}
-
-
-
// (pat) Added 6-22-2012
float SoftVector::getEnergy(float *plow) const
{
@@ -541,6 +310,36 @@ float SoftVector::getEnergy(float *plow) const
return avg;
}
+// (pat) Added 1-2014. Compute SNR of a soft vector. Very similar to above.
+// Since we dont really know what the expected signal values are, we will assume that the signal is 0 or 1
+// and return the SNR on that basis.
+// SNR is power(signal) / power(noise) where power can be calculated as (RMS(signal) / RMS(noise))**2 of the values.
+// Since RMS is square-rooted, ie RMS = sqrt(1/n * (x1**2 + x2**2 ...)), we just add up the squares.
+// To compute RMS of the signal we will remove any constant offset, so the signal values are either 0.5 or -0.5,
+// so the RMS of the signal is just 0.5**2 * len; all we need to compute is the noise component.
+float SoftVector::getSNR() const
+{
+ float sumSquaresNoise = 0;
+ const SoftVector &vec = *this;
+ int len = vec.size();
+ if (len == 0) { return 0.0; }
+ for (int i = 0; i < len; i++) {
+ float bit = vec[i];
+ if (bit < 0.5) {
+ // Assume signal is 0.
+ sumSquaresNoise += (bit - 0.0) * (bit - 0.0);
+ } else {
+ // Assume signal is 1.
+ sumSquaresNoise += (bit - 1.0) * (bit - 1.0);
+ }
+ }
+ float sumSquaresSignal = 0.5 * 0.5 * len;
+ // I really want log10 of this to convert to dB, but log is expensive, and Harvind seems to like absolute SNR.
+ // Clamp max to 999; it shouldnt get up there but be sure. This also avoids divide by zero.
+ if (sumSquaresNoise * 1000 < sumSquaresSignal) return 999;
+ return sumSquaresSignal / sumSquaresNoise;
+}
+
ostream& operator<<(ostream& os, const SoftVector& sv)
{
@@ -622,4 +421,60 @@ bool BitVector::unhex(const char* src)
return true;
}
+bool BitVector::operator==(const BitVector &other) const
+{
+ unsigned l = size();
+ return l == other.size() && 0==memcmp(begin(),other.begin(),l);
+}
+
+void BitVector::copyPunctured(BitVector &dst, const unsigned *puncture, const size_t plth)
+{
+ assert(size() - plth == dst.size());
+ char *srcp = mStart;
+ char *dstp = dst.mStart;
+ const unsigned *pend = puncture + plth;
+ while (srcp < mEnd) {
+ if (puncture < pend) {
+ int n = (*puncture++) - (srcp - mStart);
+ assert(n >= 0);
+ for (int i = 0; i < n; i++) {
+ assert(srcp < mEnd && dstp < dst.mEnd);
+ *dstp++ = *srcp++;
+ }
+ srcp++;
+ } else {
+ while (srcp < mEnd) {
+ assert(dstp < dst.mEnd);
+ *dstp++ = *srcp++;
+ }
+ }
+ }
+ assert(dstp == dst.mEnd && puncture == pend);
+}
+
+void SoftVector::copyUnPunctured(SoftVector &dst, const unsigned *puncture, const size_t plth)
+{
+ assert(size() + plth == dst.size());
+ float *srcp = mStart;
+ float *dstp = dst.mStart;
+ const unsigned *pend = puncture + plth;
+ while (dstp < dst.mEnd) {
+ if (puncture < pend) {
+ int n = (*puncture++) - (dstp - dst.mStart);
+ assert(n >= 0);
+ for (int i = 0; i < n; i++) {
+ assert(srcp < mEnd && dstp < dst.mEnd);
+ *dstp++ = *srcp++;
+ }
+ *dstp++ = 0.5;
+ } else {
+ while (srcp < mEnd) {
+ assert(dstp < dst.mEnd);
+ *dstp++ = *srcp++;
+ }
+ }
+ }
+ assert(dstp == dst.mEnd && puncture == pend);
+}
+
// vim: ts=4 sw=4
diff --git a/BitVector.h b/BitVector.h
index 63c4e91..f1faf92 100644
--- a/BitVector.h
+++ b/BitVector.h
@@ -1,5 +1,5 @@
/*
-* Copyright 2008, 2009 Free Software Foundation, Inc.
+* Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -23,11 +23,12 @@
*/
-#ifndef FECVECTORS_H
-#define FECVECTORS_H
+#ifndef BITVECTORS_H
+#define BITVECTORS_H
#include "Vector.h"
#include
+#include
class BitVector;
@@ -35,6 +36,7 @@ class SoftVector;
+
/** Shift-register (LFSR) generator. */
class Generator {
@@ -112,171 +114,83 @@ class Parity : public Generator {
};
-
-
-/**
- Class to represent convolutional coders/decoders of rate 1/2, memory length 4.
- This is the "workhorse" coder for most GSM channels.
-*/
-class ViterbiR2O4 {
-
- private:
- /**name Lots of precomputed elements so the compiler can optimize like hell. */
- //@{
- /**@name Core values. */
- //@{
- static const unsigned mIRate = 2; ///< reciprocal of rate
- static const unsigned mOrder = 4; ///< memory length of generators
- //@}
- /**@name Derived values. */
- //@{
- static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors
- static const uint32_t mSMask = mIStates-1; ///< survivor mask
- static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask
- static const uint32_t mOMask = (0x01<
+{
public:
-
- /**
- A candidate sequence in a Viterbi decoder.
- The 32-bit state register can support a deferral of 6 with a 4th-order coder.
- */
- typedef struct candStruct {
- uint32_t iState; ///< encoder input associated with this candidate
- uint32_t oState; ///< encoder output associated with this candidate
- float cost; ///< cost (metric value), float to support soft inputs
- } vCand;
-
- /** Clear a structure. */
- void clear(vCand& v)
- {
- v.iState=0;
- v.oState=0;
- v.cost=0;
- }
-
-
- private:
-
- /**@name Survivors and candidates. */
- //@{
- vCand mSurvivors[mIStates]; ///< current survivor pool
- vCand mCandidates[2*mIStates]; ///< current candidate pool
- //@}
-
- public:
-
- unsigned iRate() const { return mIRate; }
- uint32_t cMask() const { return mCMask; }
- uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
- unsigned deferral() const { return mDeferral; }
-
-
- ViterbiR2O4();
-
- /** Set all cost metrics to zero. */
- void initializeStates();
-
- /**
- Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
- @return reference to minimum-cost candidate.
- */
- const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
-
- private:
-
- /** Branch survivors into new candidates. */
- void branchCandidates();
-
- /** Compute cost metrics for soft-inputs. */
- void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
-
- /** Select survivors from the candidate set. */
- void pruneCandidates();
-
- /** Find the minimum cost survivor. */
- const vCand& minCost() const;
-
- /**
- Precompute the state tables.
- @param g Generator index 0..((1/rate)-1)
- */
- void computeStateTables(unsigned g);
-
- /**
- Precompute the generator outputs.
- mCoeffs must be defined first.
- */
- void computeGeneratorTable();
-
-};
-
-
-
-
-class BitVector : public Vector {
-
-
- public:
-
/**@name Constructors. */
//@{
/**@name Casts of Vector constructors. */
- //@{
- BitVector(char* wData, char* wStart, char* wEnd)
- :Vector(wData,wStart,wEnd)
- { }
- BitVector(size_t len=0):Vector(len) {}
- BitVector(const Vector& source):Vector(source) {}
- BitVector(Vector& source):Vector(source) {}
- BitVector(const Vector& source1, const Vector source2):Vector(source1,source2) {}
- //@}
+ BitVector(VectorDataType wData, char* wStart, char* wEnd) : VectorBase(wData, wStart, wEnd) {}
+
+ // The one and only copy-constructor.
+ BitVector(const BitVector&other) : VectorBase() {
+ VECTORDEBUG("BitVector(%p)",(void*)&other);
+ if (other.getData()) {
+ this->clone(other);
+ } else {
+ this->makeAlias(other);
+ }
+ }
+
+ // (pat) Removed default value for len and added 'explicit'. Please do not remove 'explicit';
+ // it prevents auto-conversion of int to BitVector in constructors.
+ // Previous code was often ambiguous, especially for L3Frame and descendent constructors, leading to latent bugs.
+ explicit BitVector(size_t len) { this->vInit(len); }
+ BitVector() { this->vInit(0); }
+
+ /** Build a BitVector by concatenation. */
+ BitVector(const BitVector& other1, const BitVector& other2) : VectorBase()
+ {
+ assert(this->getData() == 0);
+ this->vConcat(other1,other2);
+ }
/** Construct from a string of "0" and "1". */
+ // (pat) Characters that are not '0' or '1' map to '0'.
BitVector(const char* valString);
//@}
- /** Index a single bit. */
- bool bit(size_t index) const
- {
- // We put this code in .h for fast inlining.
- const char *dp = mStart+index;
- assert(dpbegin() + start;
char* wEnd = wStart + span;
- assert(wEnd<=mEnd);
+ assert(wEnd<=this->end());
+#if BITVECTOR_REFCNTS
+ return BitVector(mData,wStart,wEnd);
+#else
return BitVector(NULL,wStart,wEnd);
+#endif
}
- BitVector alias()
- { return segment(0,size()); }
+ // (pat) Historically the BitVector segment method had const and non-const versions with different behavior.
+ // I changed the name of the const version to cloneSegment and replaced all uses throughout OpenBTS.
+ const BitVector cloneSegment(size_t start, size_t span) const
+ {
+ BitVector seg = const_cast(this)->segment(start,span);
+ // (pat) We are depending on the Return Value Optimization not to invoke the copy-constructor on the result,
+ // which would result in its immediate destruction while we are still using it.
+ BitVector result;
+ result.clone(seg);
+ return result;
+ }
- const BitVector segment(size_t start, size_t span) const
- { return (BitVector)(Vector::segment(start,span)); }
+ BitVector alias() const {
+ return const_cast(this)->segment(0,size());
+ }
BitVector head(size_t span) { return segment(0,span); }
- const BitVector head(size_t span) const { return segment(0,span); }
BitVector tail(size_t start) { return segment(start,size()-start); }
- const BitVector tail(size_t start) const { return segment(start,size()-start); }
+
+ // (pat) Please do NOT put the const version of head and tail back in, because historically they were messed up.
+ // Use cloneSegment instead.
+ //const BitVector head(size_t span) const { return segment(0,span); }
+ //const BitVector tail(size_t start) const { return segment(start,size()-start); }
//@}
@@ -288,8 +202,6 @@ class BitVector : public Vector {
uint64_t syndrome(Generator& gen) const;
/** Calculate the parity word for the vector with the given Generator. */
uint64_t parity(Generator& gen) const;
- /** Encode the signal with the GSM rate 1/2 convolutional encoder. */
- void encode(const ViterbiR2O4& encoder, BitVector& target);
//@}
@@ -342,25 +254,62 @@ class BitVector : public Vector {
* @returns true on success, false on error. */
bool unhex(const char*);
- void set(BitVector other) // That's right. No ampersand.
+ // For this method, 'other' should have been run through the copy-constructor already
+ // (unless it was newly created, ie foo.dup(L2Frame(...)), in which case we are screwed anyway)
+ // so the call to makeAlias is redundant.
+ // This only works if other is already an alias.
+ void dup(BitVector other) { assert(!this->getData()); makeAlias(other); assert(this->mStart == other.mStart); }
+ void dup(BitVector &other) { makeAlias(other); assert(this->mStart == other.mStart); }
+
+#if 0
+ void operator=(const BitVector& other) {
+ printf("BitVector::operator=\n");
+ assert(0);
+ //this->dup(other);
+ }
+#endif
+
+ bool operator==(const BitVector &other) const;
+
+ /** Copy to dst, not including those indexed in puncture. */
+ void copyPunctured(BitVector &dst, const unsigned *puncture, const size_t plth);
+
+ /** Index a single bit. */
+ // (pat) Cant have too many ways to do this, I guess.
+ bool bit(size_t index) const
{
- clear();
- mData=other.mData;
- mStart=other.mStart;
- mEnd=other.mEnd;
- other.mData=NULL;
+ // We put this code in .h for fast inlining.
+ const char *dp = this->begin()+index;
+ assert(dpend());
+ return (*dp) & 0x01;
+ }
+
+ char& operator[](size_t index)
+ {
+ assert(this->mStart+indexmEnd);
+ return this->mStart[index];
+ }
+
+ const char& operator[](size_t index) const
+ {
+ assert(this->mStart+indexmEnd);
+ return this->mStart[index];
}
/** Set a bit */
void settfb(size_t index, int value)
{
- char *dp = mStart+index;
- assert(dpmStart+index;
+ assert(dpmEnd);
*dp = value;
}
+ typedef char* iterator;
+ typedef const char* const_iterator;
};
+// (pat) BitVector2 was an intermediate step in fixing BitVector but is no longer needed.
+#define BitVector2 BitVector
std::ostream& operator<<(std::ostream&, const BitVector&);
@@ -430,13 +379,11 @@ class SoftVector: public Vector {
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
//@}
- /** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
- void decode(ViterbiR2O4 &decoder, BitVector& target) const;
-
// (pat) How good is the SoftVector in the sense of the bits being solid?
// Result of 1 is perfect and 0 means all the bits were 0.5
// If plow is non-NULL, also return the lowest energy bit.
float getEnergy(float *low=0) const;
+ float getSNR() const;
/** Fill with "unknown" values. */
void unknown() { fill(0.5F); }
@@ -452,6 +399,9 @@ class SoftVector: public Vector {
/** Slice the whole signal into bits. */
BitVector sliced() const;
+ /** Copy to dst, adding in 0.5 for those indexed in puncture. */
+ void copyUnPunctured(SoftVector &dst, const unsigned *puncture, const size_t plth);
+
/** Return a soft bit. */
float softbit(size_t index) const
{
@@ -467,7 +417,6 @@ class SoftVector: public Vector {
assert(dp
#include
+#include
using namespace std;
+// We must have a gConfig now to include BitVector.
+#include "Configuration.h"
+ConfigurationTable gConfig;
-int main(int argc, char *argv[])
+
+void origTest()
{
- BitVector v1("0000111100111100101011110000");
- cout << v1 << endl;
+ BitVector v0("0000111100111100101011110000");
+ cout << v0 << endl;
+ // (pat) The conversion from a string was inserting garbage into the result BitVector.
+ // Fixed now so only 0 or 1 are inserted, but lets check:
+ for (char *cp = v0.begin(); cp < v0.end(); cp++) cout << (int)*cp<<" ";
+ cout << endl;
+
+ BitVector v1(v0);
v1.LSB8MSB();
- cout << v1 << endl;
- ViterbiR2O4 vCoder;
- BitVector v2(v1.size()*2);
- v1.encode(vCoder,v2);
- cout << v2 << endl;
- SoftVector sv2(v2);
- cout << sv2 << endl;
- for (unsigned i=0; i
#include
#include
+#ifdef DEBUG_CONFIG
+#define debugLogEarly gLogEarly
+#else
+#define debugLogEarly(...)
+#endif
using namespace std;
@@ -47,12 +53,43 @@ static const char* createConfigTable = {
")"
};
+// (pat) LOG() may not be initialized yet, so use syslog directly.
+#define LOGNOW(fmt,...) syslog(LOG_ERR, "ERR %s:" fmt,Utils::timestr().c_str(),__VA_ARGS__)
+
+
+long ConfigurationRecord::number() const
+{
+ if (mValue.size() == 0 && ! mCRWarned) {
+ LOGNOW("request for configuration key '%s' as number: configuration value is null", mCRKey.c_str());
+ mCRWarned = true;
+ return 0;
+ }
+ char *endptr;
+ long result = strtol(mValue.c_str(),&endptr,0);
+ if (*endptr && ! mCRWarned) {
+ // (pat) Warn if the number is invalid.
+ LOGNOW("request for configuration key '%s' as number: value could not be fully converted: '%s'", mCRKey.c_str(),mValue.c_str());
+ mCRWarned = true;
+ }
+ return result;
+}
float ConfigurationRecord::floatNumber() const
{
- float val;
- sscanf(mValue.c_str(),"%f",&val);
+ if (mValue.size() == 0 && ! mCRWarned) {
+ LOGNOW("request for configuration key '%s' as float number: configuration value is null", mCRKey.c_str());
+ mCRWarned = true;
+ return 0;
+ }
+ //sscanf(mValue.c_str(),"%f",&val);
+ char *endptr;
+ float val = strtof(mValue.c_str(),&endptr);
+ if (*endptr && ! mCRWarned) {
+ // (pat) Warn if the number is invalid.
+ LOGNOW("request for configuration key '%s' as float number: value could not be fully converted: '%s'", mCRKey.c_str(),mValue.c_str());
+ mCRWarned = true;
+ }
return val;
}
@@ -78,10 +115,12 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
if (!sqlite3_command(mDB,createConfigTable)) {
gLogEarly(LOG_EMERG, "cannot create configuration table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
}
+
+ // Pat and David both do not want to use WAL mode on the config databases.
// Set high-concurrency WAL mode.
- if (!sqlite3_command(mDB,enableWAL)) {
- gLogEarly(LOG_EMERG, "cannot enable WAL mode on database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
- }
+ //if (!sqlite3_command(mDB,enableWAL)) {
+ // gLogEarly(LOG_EMERG, "cannot enable WAL mode on database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
+ //}
// Build CommonLibs schema
ConfigurationKey *tmp;
@@ -144,6 +183,32 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
// Init the cross checking callback to something predictable
mCrossCheck = NULL;
+
+#define DUMP_CONFIGURATION_TABLE 1
+#if DUMP_CONFIGURATION_TABLE
+ // (pat) Dump any non-default config variables...
+ try {
+ if (wCmdName == NULL) { wCmdName = ""; }
+ LOG(INFO) << wCmdName << ":" << " List of non-default config parameters:";
+ string snippet("");
+ ConfigurationKeyMap view = getSimilarKeys(snippet);
+ for (ConfigurationKeyMap::iterator it = view.begin(); it != view.end(); it++) {
+ string name = it->first;
+ ConfigurationKey key = it->second;
+ if (name != key.getName()) {
+ LOG(ALERT) << "SQL database is corrupt at name:"<getStr(name);
+ if (value != defaultValue) {
+ LOG(INFO) << "Config Variable"<second.getDescription();
+ // Try to use a quote that will work: if the description contains ' quote with " else '.
+ // This is not perfect because these quotes could themselves be quoted.
+ const char * quote = string::npos != description.find('\'') ? "\"" : "'";
+ ss << quote;
if (mp->second.getType() == ConfigurationKey::BOOLEAN) {
ss << "1=enabled, 0=disabled - ";
}
@@ -188,7 +257,7 @@ string ConfigurationTable::getDefaultSQL(const std::string& program, const std::
if (mp->second.isStatic()) {
ss << " Static.";
}
- ss << "'";
+ ss << quote;
ss << ");" << endl;
mp++;
}
@@ -208,15 +277,15 @@ string ConfigurationTable::getTeX(const std::string& program, const std::string&
ss << "% -- these sections were generated using: " << program << " --gentex" << endl;
ss << "% -- binary version: " << version << endl;
- ss << "\\subsection{Customer Site Parameters}" << endl;
+ ss << "\\section{Customer Site Parameters}" << endl;
ss << "These parameters must be changed to fit your site." << endl;
ss << "\\begin{itemize}" << endl;
mp = mSchema.begin();
while (mp != mSchema.end()) {
if (mp->second.getVisibility() == ConfigurationKey::CUSTOMERSITE) {
- ss << " \\item ";
+ ss << " \\item \\texttt{";
// name
- ss << mp->first << " -- ";
+ ss << mp->first << "} -- ";
// description
ss << mp->second.getDescription();
ss << endl;
@@ -226,7 +295,7 @@ string ConfigurationTable::getTeX(const std::string& program, const std::string&
ss << "\\end{itemize}" << endl;
ss << endl;
- ss << "\\subsection{Customer Tuneable Parameters}" << endl;
+ ss << "\\section{Customer Tuneable Parameters}" << endl;
ss << "These parameters can be changed to optimize your site." << endl;
ss << "\\begin{itemize}" << endl;
mp = mSchema.begin();
@@ -237,9 +306,9 @@ string ConfigurationTable::getTeX(const std::string& program, const std::string&
mp->second.getVisibility() == ConfigurationKey::CUSTOMERTUNE ||
mp->second.getVisibility() == ConfigurationKey::CUSTOMERWARN
)) {
- ss << " \\item ";
+ ss << " \\item \\texttt{";
// name
- ss << mp->first << " -- ";
+ ss << mp->first << "} -- ";
// description
ss << mp->second.getDescription();
ss << endl;
@@ -249,16 +318,16 @@ string ConfigurationTable::getTeX(const std::string& program, const std::string&
ss << "\\end{itemize}" << endl;
ss << endl;
- ss << "\\subsection{Developer/Factory Parameters}" << endl;
+ ss << "\\section{Developer/Factory Parameters}" << endl;
ss << "These parameters should only be changed by when developing new code." << endl;
ss << "\\begin{itemize}" << endl;
mp = mSchema.begin();
while (mp != mSchema.end()) {
if (mp->second.getVisibility() == ConfigurationKey::FACTORY ||
mp->second.getVisibility() == ConfigurationKey::DEVELOPER) {
- ss << " \\item ";
+ ss << " \\item \\texttt{";
// name
- ss << mp->first << " -- ";
+ ss << mp->first << "} -- ";
// description
ss << mp->second.getDescription();
ss << endl;
@@ -377,6 +446,46 @@ bool ConfigurationTable::isValidValue(const std::string& name, const std::string
break;
}
+ case ConfigurationKey::HOSTANDPORT_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::HOSTANDPORT: {
+ uint delimiter;
+ std::string host;
+ int port = -1;
+ bool validHost = false;
+
+ delimiter = val.find(':');
+ if (delimiter != std::string::npos) {
+ host = val.substr(0, delimiter);
+ std::stringstream(val.substr(delimiter+1)) >> port;
+ if (ConfigurationKey::isValidIP(host)) {
+ validHost = true;
+ } else {
+ regex_t r;
+ const char* expression = "^[a-zA-Z0-9_.-]+$";
+ int result = regcomp(&r, expression, REG_EXTENDED);
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ break;//abort();
+ }
+ if (regexec(&r, host.c_str(), 0, NULL, 0)==0) {
+ validHost = true;
+ }
+ regfree(&r);
+ }
+
+ if (validHost && 1 <= port && port <= 65535) {
+ ret = true;
+ }
+ }
+ break;
+ }
+
case ConfigurationKey::IPADDRESS_OPT: {
if (val.length() == 0) {
ret = true;
@@ -553,13 +662,13 @@ const ConfigurationRecord& ConfigurationTable::lookup(const string& key)
// value found, cache the result
if (value) {
- mCache[key] = ConfigurationRecord(value);
+ mCache[key] = ConfigurationRecord(key,value);
// key definition found, cache the default
} else if (keyDefinedInSchema(key)) {
- mCache[key] = ConfigurationRecord(mSchema[key].getDefaultValue());
+ mCache[key] = ConfigurationRecord(key,mSchema[key].getDefaultValue());
// total miss, cache the error
} else {
- mCache[key] = ConfigurationRecord(false);
+ mCache[key] = ConfigurationRecord(key,false);
throw ConfigurationTableKeyNotFound(key);
}
@@ -749,9 +858,11 @@ ConfigurationRecordMap ConfigurationTable::getAllPairs() const
const char* key = (const char*)sqlite3_column_text(stmt,0);
const char* value = (const char*)sqlite3_column_text(stmt,1);
if (key && value) {
- tmp[string(key)] = ConfigurationRecord(value);
+ string skey(key);
+ tmp[skey] = ConfigurationRecord(skey,value);
} else if (key && !value) {
- tmp[string(key)] = ConfigurationRecord(false);
+ string skey(key);
+ tmp[skey] = ConfigurationRecord(skey,false);
}
src = sqlite3_run_query(mDB,stmt);
}
@@ -773,7 +884,7 @@ bool ConfigurationTable::set(const string& key, const string& value)
bool success = sqlite3_command(mDB,cmd.c_str());
// Cache the result.
- if (success) mCache[key] = ConfigurationRecord(value);
+ if (success) mCache[key] = ConfigurationRecord(key,value);
return success;
}
@@ -944,55 +1055,6 @@ template bool ConfigurationKey::isInValRange(const ConfigurationKey &ke
return ret;
}
-const std::string ConfigurationKey::getARFCNsString() {
- stringstream ss;
- int i;
- float downlink;
- float uplink;
-
- // 128:251 GSM850
- downlink = 869.2;
- uplink = 824.2;
- for (i = 128; i <= 251; i++) {
- ss << i << "|GSM850 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
- downlink += 0.2;
- uplink += 0.2;
- }
-
- // 0:124 EGSM900
- downlink = 935.0;
- uplink = 890.0;
- for (i = 0; i <= 124; i++) {
- ss << i << "|PGSM900 #" << i << " : " << (downlink + (0.2*i)) << " MHz downlink / " << (uplink + (0.2*i)) << " MHz uplink,";
- }
- // 975:1023 EGSM900
- for (i = 975; i <= 1023 ; i++) {
- ss << i << "|PGSM900 #" << i << " : " << (downlink + (0.2*i)) << " MHz downlink / " << (uplink + (0.2*i)) << " MHz uplink,";
- }
-
- // 512:885 DCS1800
- downlink = 1805.2;
- uplink = 1710.2;
- for (i = 512; i <= 885; i++) {
- ss << i << "|DCS1800 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
- downlink += 0.2;
- uplink += 0.2;
- }
-
- // 512:810 PCS1900
- downlink = 1930.2;
- uplink = 1850.2;
- for (i = 512; i <= 810; i++) {
- ss << i << "|PCS1900 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
- downlink += 0.2;
- uplink += 0.2;
- }
-
- ss << endl;
-
- return ss.str();
-}
-
const std::string ConfigurationKey::visibilityLevelToString(const ConfigurationKey::VisibilityLevel& visibility) {
std::string ret = "UNKNOWN ERROR";
@@ -1045,6 +1107,12 @@ const std::string ConfigurationKey::typeToString(const ConfigurationKey::Type& t
case FILEPATH:
ret = "file path";
break;
+ case HOSTANDPORT_OPT:
+ ret = "hostname or IP address and port (optional)";
+ break;
+ case HOSTANDPORT:
+ ret = "hostname or IP address and port";
+ break;
case IPADDRESS_OPT:
ret = "IP address (optional)";
break;
@@ -1101,6 +1169,7 @@ void ConfigurationKey::printKey(const ConfigurationKey &key, const std::string&
void ConfigurationKey::printDescription(const ConfigurationKey &key, ostream& os) {
std::string tmp;
+ unsigned scope;
os << " - description: " << key.getDescription() << std::endl;
if (key.getUnits().length()) {
@@ -1155,6 +1224,28 @@ void ConfigurationKey::printDescription(const ConfigurationKey &key, ostream& os
} else if (key.getValidValues().length()) {
os << " - raw valid values: " << tmp << std::endl;
}
+
+ scope = key.getScope();
+ if (scope) {
+ if (scope & ConfigurationKey::GLOBALLYUNIQUE) {
+ os << " - scope: value must be unique across all nodes" << std::endl;
+ }
+ if (scope & ConfigurationKey::GLOBALLYSAME) {
+ os << " - scope: value must be the same across all nodes" << std::endl;
+ }
+ if (scope & ConfigurationKey::NEIGHBORSUNIQUE) {
+ os << " - scope: value must be unique across all neighbors" << std::endl;
+ }
+ if (scope & ConfigurationKey::NEIGHBORSSAME) {
+ os << " - scope: value must be the same across all neighbors" << std::endl;
+ }
+ if (scope & ConfigurationKey::NEIGHBORSSAME) {
+ os << " - scope: must be the same across all neighbors" << std::endl;
+ }
+ if (scope & ConfigurationKey::NODESPECIFIC) {
+ os << " - scope: specfic to each individual node" << std::endl;
+ }
+ }
}
diff --git a/Configuration.h b/Configuration.h
index 959cdf8..3a51622 100644
--- a/Configuration.h
+++ b/Configuration.h
@@ -1,7 +1,7 @@
/*
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
-* Copyright 2011, 2012 Range Networks, Inc.
+* Copyright 2011, 2012, 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -69,35 +69,45 @@ class ConfigurationTableKeyNotFound : public ConfigurationTableError {
};
+// (pat) 10-5-2013 Add the key to this record so we can print better error messages,
+// and warn if number() of floatNumber() have non-numeric values.
class ConfigurationRecord {
private:
+ std::string mCRKey;
std::string mValue;
- long mNumber;
bool mDefined;
+ mutable bool mCRWarned;
public:
- ConfigurationRecord(bool wDefined=true):
- mDefined(wDefined)
+ ConfigurationRecord() : mDefined(false), mCRWarned(false) {}
+ ConfigurationRecord(const std::string &key,bool wDefined):
+ mCRKey(key),
+ mDefined(wDefined),
+ mCRWarned(false)
{ }
- ConfigurationRecord(const std::string& wValue):
+ ConfigurationRecord(const std::string&key, const std::string& wValue):
+ mCRKey(key),
mValue(wValue),
- mNumber(strtol(wValue.c_str(),NULL,0)),
- mDefined(true)
+ //mNumber(strtol(wValue.c_str(),&endptr,0)),
+ mDefined(true),
+ mCRWarned(false)
{ }
- ConfigurationRecord(const char* wValue):
+ ConfigurationRecord(const std::string&key, const char* wValue):
+ mCRKey(key),
mValue(std::string(wValue)),
- mNumber(strtol(wValue,NULL,0)),
- mDefined(true)
+ //mNumber(strtol(wValue.c_str(),&endptr,0)),
+ mDefined(true),
+ mCRWarned(false)
{ }
const std::string& value() const { return mValue; }
- long number() const { return mNumber; }
+ long number() const;
bool defined() const { return mDefined; }
float floatNumber() const;
@@ -193,6 +203,7 @@ class ConfigurationTable {
ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic
+ // (pat) filename is the sql file name, wCmdName is the name of the executable we are running, used for better error messages.
ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap());
/** Generate an up-to-date example sql file for new installs. */
@@ -350,6 +361,8 @@ class ConfigurationKey {
CIDR,
FILEPATH_OPT,
FILEPATH,
+ HOSTANDPORT_OPT,
+ HOSTANDPORT,
IPADDRESS_OPT,
IPADDRESS,
IPANDPORT,
@@ -361,7 +374,17 @@ class ConfigurationKey {
REGEX,
STRING_OPT,
STRING,
- VALRANGE
+ VALRANGE // (pat) string format is: ':' [ '(' ')' ]
+ // step is not currently enforced.
+ };
+
+ enum Scope
+ {
+ GLOBALLYUNIQUE = 1,
+ GLOBALLYSAME = 2,
+ NEIGHBORSUNIQUE = 4,
+ NEIGHBORSSAME = 8,
+ NODESPECIFIC = 16
};
private:
@@ -374,11 +397,12 @@ class ConfigurationKey {
std::string mValidValues;
bool mIsStatic;
std::string mDescription;
+ unsigned mScope;
public:
- ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription):
+ ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription, unsigned wScope = 0):
mName(wName),
mDefaultValue(wDefaultValue),
mUnits(wUnits),
@@ -386,7 +410,8 @@ class ConfigurationKey {
mType(wType),
mValidValues(wValidValues),
mIsStatic(wIsStatic),
- mDescription(wDescription)
+ mDescription(wDescription),
+ mScope(wScope)
{ }
ConfigurationKey()
@@ -396,12 +421,14 @@ class ConfigurationKey {
const std::string& getDefaultValue() const { return mDefaultValue; }
void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; }
void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); }
+ void updateValidValues(const std::string& newValue) { mValidValues = newValue; }
const std::string& getUnits() const { return mUnits; }
const VisibilityLevel& getVisibility() const { return mVisibility; }
const Type& getType() const { return mType; }
const std::string& getValidValues() const { return mValidValues; }
bool isStatic() const { return mIsStatic; }
const std::string& getDescription() const { return mDescription; }
+ unsigned getScope() const { return mScope; }
static bool isValidIP(const std::string& ip);
static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping);
@@ -410,7 +437,6 @@ class ConfigurationKey {
static const std::string typeToString(const ConfigurationKey::Type& type);
static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os);
static void printDescription(const ConfigurationKey &key, std::ostream& os);
- static const std::string getARFCNsString();
};
diff --git a/ConfigurationTest.cpp b/ConfigurationTest.cpp
index 290db64..3d8014b 100644
--- a/ConfigurationTest.cpp
+++ b/ConfigurationTest.cpp
@@ -47,7 +47,7 @@ int main(int argc, char *argv[])
gConfig.setUpdateHook(purgeConfig);
- char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
+ const char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
for (int i=0; i<5; i++) {
gConfig.set(keys[i],i);
diff --git a/Defines.h b/Defines.h
new file mode 100644
index 0000000..5fb431d
--- /dev/null
+++ b/Defines.h
@@ -0,0 +1,52 @@
+// Pat added this file.
+// We need an include file that is included before any other include files.
+// Might I suggest that Range Networks specific global #defines be prefixed with RN_
+
+#ifndef DEFINES_H
+#define DEFINES_H
+
+#define GPRS_ENCODER 1 // Use SharedL1Encoder and SharedL1Decoder
+#define GPRS_TESTSI4 1
+#define GPRS_TEST 1 // Compile in other GPRS stuff.
+#define GPRS_PAT 1 // Compile in GPRS code. Turn this off to get previous non-GRPS code,
+ // although I am not maintaining it so you may have to fix compile
+ // problems to use it.
+
+
+// (pat) This removes the constness from a pointer, eg: const T *barfo; T *notsobarfo = Unconst(barfo);
+template
+T* Unconst(const T*foo) { return const_cast(foo); }
+
+// (pat) Like assert() but dont core dump unless we are testing.
+// Note: to use this macro you must include the dependencies first.
+#define devassert(code) {if (IS_LOG_LEVEL(DEBUG)) {assert(code);} else if (!(code)) {LOG(ERR)<<"assertion failed:"<< #code;}}
+
+// __GNUG__ is true for g++ and __GNUC__ for gcc.
+#if __GNUC__&0==__GNUG__
+
+#define RN_UNUSED __attribute__((unused))
+
+#define RN_UNUSED_PARAM(var) RN_UNUSED var
+
+// Pack structs onto byte boundaries.
+// Note that if structs are nested, this must appear on all of them.
+#define RN_PACKED __attribute__((packed))
+
+#else
+
+// Suppress warning message about a variable or function being unused.
+// In C++ you can leave out the variable name to suppress the 'unused variable' warning.
+#define RN_UNUSED_PARAM(var) /*nothing*/
+#define RN_UNUSED /*not defined*/
+#define RN_PACKED /*not defined*/
+#endif
+
+// Bound value between min and max values.
+#define RN_BOUND(value,min,max) ( (value)<(min) ? (min) : (value)>(max) ? (max) : (value) )
+
+#define RN_PRETTY_TEXT(name) (" " #name "=(") << name << ")"
+#define RN_PRETTY_TEXT1(name) (" " #name "=") << name
+#define RN_WRITE_TEXT(name) os << RN_PRETTY_TEXT(name)
+#define RN_WRITE_OPT_TEXT(name,flag) if (flag) { os << RN_WRITE_TEXT(name); }
+
+#endif
diff --git a/Interthread.h b/Interthread.h
index 42e6f7f..35294a2 100644
--- a/Interthread.h
+++ b/Interthread.h
@@ -26,12 +26,14 @@
#ifndef INTERTHREAD_H
#define INTERTHREAD_H
+#include "Defines.h"
#include "Timeval.h"
#include "Threads.h"
#include "LinkedLists.h"
#include