431 lines
12 KiB
C++
431 lines
12 KiB
C++
/*
|
|
* 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.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
#ifndef BITVECTORS_H
|
|
#define BITVECTORS_H
|
|
|
|
#include "Vector.h"
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
|
|
class BitVector;
|
|
class SoftVector;
|
|
|
|
|
|
|
|
|
|
/** Shift-register (LFSR) generator. */
|
|
class Generator {
|
|
|
|
private:
|
|
|
|
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
|
|
uint64_t mState; ///< shift register state. LSB is most recent.
|
|
uint64_t mMask; ///< mask for reading state
|
|
unsigned mLen; ///< number of bits used in shift register
|
|
unsigned mLen_1; ///< mLen - 1
|
|
|
|
public:
|
|
|
|
Generator(uint64_t wCoeff, unsigned wLen)
|
|
:mCoeff(wCoeff),mState(0),
|
|
mMask((1ULL<<wLen)-1),
|
|
mLen(wLen),mLen_1(wLen-1)
|
|
{ assert(wLen<64); }
|
|
|
|
void clear() { mState=0; }
|
|
|
|
/**@name Accessors */
|
|
//@{
|
|
uint64_t state() const { return mState & mMask; }
|
|
unsigned size() const { return mLen; }
|
|
//@}
|
|
|
|
/**
|
|
Calculate one bit of a syndrome.
|
|
This is in the .h for inlining.
|
|
*/
|
|
void syndromeShift(unsigned inBit)
|
|
{
|
|
const unsigned fb = (mState>>(mLen_1)) & 0x01;
|
|
mState = (mState<<1) ^ (inBit & 0x01);
|
|
if (fb) mState ^= mCoeff;
|
|
}
|
|
|
|
/**
|
|
Update the generator state by one cycle.
|
|
This is in the .h for inlining.
|
|
*/
|
|
void encoderShift(unsigned inBit)
|
|
{
|
|
const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01;
|
|
mState <<= 1;
|
|
if (fb) mState ^= mCoeff;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Parity (CRC-type) generator and checker based on a Generator. */
|
|
class Parity : public Generator {
|
|
|
|
protected:
|
|
|
|
unsigned mCodewordSize;
|
|
|
|
public:
|
|
|
|
Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize)
|
|
:Generator(wCoefficients, wParitySize),
|
|
mCodewordSize(wCodewordSize)
|
|
{ }
|
|
|
|
/** Compute the parity word and write it into the target segment. */
|
|
void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true);
|
|
|
|
/** Compute the syndrome of a received sequence. */
|
|
uint64_t syndrome(const BitVector& receivedCodeword);
|
|
};
|
|
|
|
|
|
// (pat) Nov 2013. I rationalized the behavior of BitVector and added assertions to core dump code
|
|
// that relied on the bad aspects of the original behavior. See comments at VectorBase.
|
|
class BitVector : public VectorBase<char>
|
|
{
|
|
public:
|
|
/**@name Constructors. */
|
|
//@{
|
|
|
|
/**@name Casts of Vector constructors. */
|
|
BitVector(VectorDataType wData, char* wStart, char* wEnd) : VectorBase(wData, wStart, wEnd) {}
|
|
|
|
// The one and only copy-constructor.
|
|
BitVector(const BitVector&other) : VectorBase<char>() {
|
|
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<char>()
|
|
{
|
|
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);
|
|
//@}
|
|
|
|
/**@name Casts and overrides of Vector operators. */
|
|
//@{
|
|
// (pat) Please DO NOT add a const anywhere in this method. Use cloneSegment instead.
|
|
BitVector segment(size_t start, size_t span)
|
|
{
|
|
char* wStart = this->begin() + start;
|
|
char* wEnd = wStart + span;
|
|
assert(wEnd<=this->end());
|
|
#if BITVECTOR_REFCNTS
|
|
return BitVector(mData,wStart,wEnd);
|
|
#else
|
|
return BitVector(NULL,wStart,wEnd);
|
|
#endif
|
|
}
|
|
|
|
// (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<BitVector*>(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;
|
|
}
|
|
|
|
BitVector alias() const {
|
|
return const_cast<BitVector*>(this)->segment(0,size());
|
|
}
|
|
|
|
BitVector head(size_t span) { return segment(0,span); }
|
|
BitVector tail(size_t start) { 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); }
|
|
//@}
|
|
|
|
|
|
void zero() { fill(0); }
|
|
|
|
/**@name FEC operations. */
|
|
//@{
|
|
/** Calculate the syndrome of the vector with the given Generator. */
|
|
uint64_t syndrome(Generator& gen) const;
|
|
/** Calculate the parity word for the vector with the given Generator. */
|
|
uint64_t parity(Generator& gen) const;
|
|
//@}
|
|
|
|
|
|
/** Invert 0<->1. */
|
|
void invert();
|
|
|
|
/**@name Byte-wise operations. */
|
|
//@{
|
|
/** Reverse an 8-bit vector. */
|
|
void reverse8();
|
|
/** Reverse groups of 8 within the vector (byte reversal). */
|
|
void LSB8MSB();
|
|
//@}
|
|
|
|
/**@name Serialization and deserialization. */
|
|
//@{
|
|
uint64_t peekField(size_t readIndex, unsigned length) const;
|
|
uint64_t peekFieldReversed(size_t readIndex, unsigned length) const;
|
|
uint64_t readField(size_t& readIndex, unsigned length) const;
|
|
uint64_t readFieldReversed(size_t& readIndex, unsigned length) const;
|
|
void fillField(size_t writeIndex, uint64_t value, unsigned length);
|
|
void fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length);
|
|
void writeField(size_t& writeIndex, uint64_t value, unsigned length);
|
|
void writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length);
|
|
void write0(size_t& writeIndex) { writeField(writeIndex,0,1); }
|
|
void write1(size_t& writeIndex) { writeField(writeIndex,1,1); }
|
|
|
|
//@}
|
|
|
|
/** Sum of bits. */
|
|
unsigned sum() const;
|
|
|
|
/** Reorder bits, dest[i] = this[map[i]]. */
|
|
void map(const unsigned *map, size_t mapSize, BitVector& dest) const;
|
|
|
|
/** Reorder bits, dest[map[i]] = this[i]. */
|
|
void unmap(const unsigned *map, size_t mapSize, BitVector& dest) const;
|
|
|
|
/** Pack into a char array. */
|
|
void pack(unsigned char*) const;
|
|
|
|
/** Unpack from a char array. */
|
|
void unpack(const unsigned char*);
|
|
|
|
/** Make a hexdump string. */
|
|
void hex(std::ostream&) const;
|
|
std::string hexstr() const;
|
|
|
|
/** Unpack from a hexdump string.
|
|
* @returns true on success, false on error. */
|
|
bool unhex(const char*);
|
|
|
|
// 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
|
|
{
|
|
// We put this code in .h for fast inlining.
|
|
const char *dp = this->begin()+index;
|
|
assert(dp<this->end());
|
|
return (*dp) & 0x01;
|
|
}
|
|
|
|
char& operator[](size_t index)
|
|
{
|
|
assert(this->mStart+index<this->mEnd);
|
|
return this->mStart[index];
|
|
}
|
|
|
|
const char& operator[](size_t index) const
|
|
{
|
|
assert(this->mStart+index<this->mEnd);
|
|
return this->mStart[index];
|
|
}
|
|
|
|
/** Set a bit */
|
|
void settfb(size_t index, int value)
|
|
{
|
|
char *dp = this->mStart+index;
|
|
assert(dp<this->mEnd);
|
|
*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&);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
The SoftVector class is used to represent a soft-decision signal.
|
|
Values 0..1 represent probabilities that a bit is "true".
|
|
*/
|
|
class SoftVector: public Vector<float> {
|
|
|
|
public:
|
|
|
|
/** Build a SoftVector of a given length. */
|
|
SoftVector(size_t wSize=0):Vector<float>(wSize) {}
|
|
|
|
/** Construct a SoftVector from a C string of "0", "1", and "X". */
|
|
SoftVector(const char* valString);
|
|
|
|
/** Construct a SoftVector from a BitVector. */
|
|
SoftVector(const BitVector& source);
|
|
|
|
/**
|
|
Wrap a SoftVector around a block of floats.
|
|
The block will be delete[]ed upon desctuction.
|
|
*/
|
|
SoftVector(float *wData, unsigned length)
|
|
:Vector<float>(wData,length)
|
|
{}
|
|
|
|
SoftVector(float* wData, float* wStart, float* wEnd)
|
|
:Vector<float>(wData,wStart,wEnd)
|
|
{ }
|
|
|
|
/**
|
|
Casting from a Vector<float>.
|
|
Note that this is NOT pass-by-reference.
|
|
*/
|
|
SoftVector(Vector<float> source)
|
|
:Vector<float>(source)
|
|
{}
|
|
|
|
|
|
/**@name Casts and overrides of Vector operators. */
|
|
//@{
|
|
SoftVector segment(size_t start, size_t span)
|
|
{
|
|
float* wStart = mStart + start;
|
|
float* wEnd = wStart + span;
|
|
assert(wEnd<=mEnd);
|
|
return SoftVector(NULL,wStart,wEnd);
|
|
}
|
|
|
|
SoftVector alias()
|
|
{ return segment(0,size()); }
|
|
|
|
const SoftVector segment(size_t start, size_t span) const
|
|
{ return (SoftVector)(Vector<float>::segment(start,span)); }
|
|
|
|
SoftVector head(size_t span) { return segment(0,span); }
|
|
const SoftVector head(size_t span) const { return segment(0,span); }
|
|
SoftVector tail(size_t start) { return segment(start,size()-start); }
|
|
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
|
|
//@}
|
|
|
|
// (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); }
|
|
|
|
/** Return a hard bit value from a given index by slicing. */
|
|
bool bit(size_t index) const
|
|
{
|
|
const float *dp = mStart+index;
|
|
assert(dp<mEnd);
|
|
return (*dp)>0.5F;
|
|
}
|
|
|
|
/** 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
|
|
{
|
|
const float *dp = mStart+index;
|
|
assert(dp<mEnd);
|
|
return *dp;
|
|
}
|
|
|
|
/** Set a soft bit */
|
|
void settfb(size_t index, float value)
|
|
{
|
|
float *dp = mStart+index;
|
|
assert(dp<mEnd);
|
|
*dp = value;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream&, const SoftVector&);
|
|
|
|
|
|
|
|
|
|
#endif
|
|
// vim: ts=4 sw=4
|