-1

so I am working with a data set that comes in as an array. For example:

{0x00, 0x00, 0xFF, 0xF1, 0x6D, 0xA2, 0x00 0x00}

{0b00000000, 0b00000000, 0b11111111, 0b11110001, 0b01101101, 0b10100010, 0b00000000, 0b00000000}

I need to be able to extract information from this array based on two values:

  • The start bit.
  • The number of bits.

For example, if the start bit was 17, and the bit length was 13, (assuming the first bit is index 0), my data would be:

0b1111111111100

Decimal: 8188

This decimal value (or uint32_t or w/e) would then be stored in a variable and output. I can do this for a single case, but am having a hard time writing code where start bit and number of bits are variables into a function or block of code...


I've tried using memcpy, but this is only byte addressable.

I would appreciate any feedback!

Punchki
  • 441
  • 1
  • 5
  • 9
  • 2
    Something based on `std::bitset` maybe? Also `boost::dynamic_bitset` might be viable. – πάντα ῥεῖ Aug 28 '19 at 21:04
  • First create a function that returns a single bit. Then call this in a loop and get your desired bits one-by-one. Once that works, if you need more performance, you should be better equipped to figure out how to get bits in larger chunks instead of one-by-one. – hyde Aug 28 '19 at 21:05
  • `&` for bitwise and, and `<<` for bit shift inside a `for` loop perhaps? I suppose my ancient old C is showing instead of the fancy new C++... – PaulProgrammer Aug 28 '19 at 21:05
  • 3
    I don't understand "_if the start bit was 17_". Please show the code you have for the single case you mentioned. – Ted Lyngmo Aug 28 '19 at 21:16

3 Answers3

2

From the desired starting bit index, you can use the / and % operators to calculate the exact byte in the array, and the initial bit index within that byte. Then it is just a matter of iterating the byte array and shifting bits around until the desired number of bits have been copied into the result.

For example:

uint32_t getBits(const void *Data, uint32_t DataLen, uint32_t StartBit, uint8_t NumBits)
{
    uint32_t Result = 0;

    // are there any bytes at all?
    if (DataLen != 0)
    {
        // get a pointer to the starting byte...
        const uint8_t *pData = &(static_cast<const uint8_t*>(Data)[StartBit / 8]);
        uint8_t b = *pData;

        // calculate the starting bit within that byte...
        int BitOffset = 7 - (StartBit % 8);

        // iterate for the desired number of bits...
        for(int i = 0; i < NumBits; ++i)
        {
            // make room for the next bit...
            Result <<= 1;
            // copy the bit...
            Result |= ((b >> BitOffset) & 0x01);
            // reached the end of the current byte?
            if (--BitOffset < 0)
            {
                b = *(++pData); // go to the next byte...
                BitOffset = 7; // restart at the first bit in that byte...
            }
        }
    }

    // all done...
    return Result;
}
uint8_t arr[] = {0x00, 0x00, 0xFF, 0xF1, 0x6D, 0xA2, 0x00, 0x00};
uint32_t value = getBits(arr, 8, 17, 13);

Live Demo

If needed, you can then optimize this further to make the code shift+copy more than 1 bit at a time. For instance, before entering the loop, if the starting bit index is not at a byte boundary then shift off the unwanted bits from the initial b and OR the remaining bits to Result. Then have the loop OR 1 whole byte at a time into Result while the desired number of bits is >= 8. Then after exiting the loop, if the desired number of bits is still > 0 then shift off the unwanted bits from the final b and OR the remaining bits to Result.

I leave that as an exercise for you to figure out.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

@Punchki If you want to build a machine yourself,perheps this idea could help.

                 First (17)   Last (inclusive)
                   !            !
                   !            ! +- The least significant bit in the least significant byte
                   !            ! !
00000000 00000000 11111111 11110001 01101101 10100010 00000000 00000000
                      &        &     <- AND
                  01111111 11111100  <- 13 bit bit-mask
                                     <- cast all bytes to std::uintmax_t and ...
<<(24-2) <<(16-2)  <<(8-2)  >>(0+2)  <- ... shift and ...
                 |                   <- ... OR all into the result variable
                  00011111 11111100

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

You could use something like this, it avoids any loops:

uint32_t startBit{0};
uint32_t nrOfBits{4};
uint64_t signalValTemp{0};
uint8_t startByte = startBit / CHAR_BIT;
//copy 8 bytes from starting byte
std::memcpy(&signalValTemp, framePayload.data() + startByte, sizeof(signalValTemp));
//mask out the msb that we don't need.
signalValTemp <<= (startBit % CHAR_BIT);
//add the missing bits to have a full 64 bits signal
uint64_t missingBits = static_cast<uint64_t>(framePayload.at(startByte + 8)) >> (startBit % CHAR_BIT);
signalValTemp |= missingBits;
//keep the value we need
signalValTemp >>= (std::numeric_limits<uint64_t>::digits - nrOfBits);