You are here:

C++/filling up of a frame with 10bit words

Advertisement


Question
consider a frame of 720 words
each word is of 10 bits

how can i fill up my frame of 720 words with numbers starting from 0 to 720.

if we store this frame in a file then size taken by this frame should not be more than 900 bytes.

note:-each number should be represented in 10 bit form
will BitArray or BitFields will be helpful if yes then how please explain ur answers with some sample code


Answer
In this case, no I do not think std::bitarray will be of much use as it cannot output the data in the format you require.

In fact at first look I think the best thing to do is to write a class that wraps a collection of 900 byte values (unsigned char) and provides functions to set and get 10 bit values (passed as 16 bit integer types if your implementation supports them - e.g. as unsigned short values) using the 10 bit index (0 .. 719).

Note that if you have 720 10 bit numbers and they are indexed from 0 then the highest index is 719 not 720, so if you set each value to the number of its index you can fill them with values 0 to 719, not 0 to 720, as your question claims. If you require 0 to 720 (i.e. 721 10 bit values) then you will need 902 bytes to store them.

Assuming you require 720 10 bit values stored as 900 bytes then you wish to be able to do something like the following:
      
       int main()
       {
       // create a frame object
         Frame frame;

       // Set 10 bit values from 10-bit value index values
         for ( int i = 0; i < Frame::FrameSize; ++i )
         {
         frame.SetAt( i, i );
         }

       // Get 10-bit values from 10 bit value index values
         for ( int i = 0; i < Frame::FrameSize; ++i )
         {
         std::cout << "frame[" << i << "] = "
         << frame.GetAt( i ) << '\n';
         }

       // Write and read a frame to and from files:
         std::ofstream outFile("newFrameFile");
         std::ifstream inFile("existingFrameFile");

         frame.Write( outFile );
         frame.Read( inFile );
       }

All code shown is for example usage only, so omits error checking etc., is formatted to pass through AllExperts and is best viewed using a mono-spaced font such as Courier.

The above shows the usage required. For the moment I have steered clear of using C++ operators and the like. This initially gives us a class that looks like this:

       class Frame
       {
       public:
         Frame();

         ValueType GetAt( SizeType idx );

         void SetAt( SizeType idx, ValueType value );

         void Write( std::ostream & out );

         void Read( std::istream & in );

       };

SizeType and ValueType are integer types that represent the types used for the size of arrays and indexes and the like and the type used for transferring 10-bit types. These we can add to the class as type aliases (typedefs). In addition we require a collection of bytes, let's say a std::vector of byte type integers. These types can also be added as type aliases, and the Frame byte collection as a private member:

       #include <vector>

       class Frame
       {
       public:
         typedef unsigned char          ByteType;
         typedef std::vector<ByteType>
         ByteCollectionType;
         typedef ByteCollectionType::size_type
         SizeType;
         typedef unsigned short          ValueType;

         Frame();

         ValueType GetAt( SizeType idx );

         void SetAt( SizeType idx, ValueType value );

         void Write( std::ostream & out );

         void Read( std::istream & in );
       private:
         ByteCollectionType    iBytes;
       };

The implementation of the constructor is trivial we just set the size of the vector to be the number of bytes required to hold the number of 10 bit values in a frame:

       class Frame
       {
       public:

       // ...

         static SizeType const      ValueBitSize = 10;
         static SizeType const      ByteBitSize = 8;
         static SizeType const      FrameSize = 720;
         static SizeType const      FrameByteSize =
         (FrameSize*ValueBitSize)/ByteBitSize;

         Frame()
         : iBytes( FrameByteSize )
         {
         }
       // ...

       };

Note the above calculation is not perfect as it does not round up if FrameSize does not equate to a whole number of bytes.

The read and write operations are trivial to implement, but note that we will be storing the values as pure binary and not bytes that are text, so we need to open the files in binary mode. All this does is suppress translation to and from C/C++ end of lines ('\n') and operating system text file end of lines (e.g. "\r\n" on Microsoft operating systems such as MS-DOS and Windows). On UN*X and Linux systems this is not so important and the operating system text file end of line is the same as the C/C++ end of line. Doing this forces the file size to be the 900 characters as expected on MS operating systems, and the byte values to be as expected on all systems. Otherwise each byte that happens to represent the '\n' character (byte value of 10 or 0xA) will be translated and appear as (for example on Windows) 13 10 - increasing the file size.

Here are example implementation for Read and Write:

     void Frame::Write( std::ostream & out )
     {
         char const *
         bytesPtr( reinterpret_cast<char const *>(&iBytes[0]) );
         out.write( bytesPtr, FrameByteSize );
     }

     void Frame::Read( std::istream & in )
     {
         char * bytesPtr( reinterpret_cast<char *>(&iBytes[0]) );
         in.read( bytesPtr, FrameByteSize );
     }

We have to cast the address of the ByteType unsigned char at the first (0) vector element to char pointers (pointers to const char for write). Using char as the ByteType may remove the need for the casts but complicates the setting and getting of frame values (if char is signed by default as shifting can sign extend to maintain the sign of a value which we are not interested in).

Once we have a pointer to the byte data we just pass the request along to the stream write or read member functions.

We need to look at the frame data in several different ways:

- as a collection of 8 bit bytes as used by the computer
- as a collection of 10 bit values as required by the application
- as a collection of bits, useful as an intermediate result when converting from 10 bit to 8 bit values.

Each 10 bit value will be split across 2 bytes. The first value (index 0) will occupy all 8 bits in the first byte and the 2 lowest bits in the second. The next (second) value (at index 1) will occupy the remaining 6 highest bits in the second byte and the 4 lowest bits in the third and so on.

The way to handle packing and unpacking the 10 bit values involves a lot of bit calculations and manipulation and is not something that you can understand without thinking about. The explanation is not going to be clear unless you are following carefully. I suggest you get a pen or pencil and paper and start by drawing a sequence of 5 bytes and then pack 4 10 bit values in them noting which positions in which bytes each value occupies. You can also note the absolute bit number each value starts at, viewing the 5 bytes as a 40 bit word with bits numbered 0 to 39. You should draw additional diagrams or add notations and try some examples of values as the explanation proceeds. I suggest you use a value index in the range 0 to 3 for your examples.

Now to get and set 10-bit values we need to locate the two bytes in the byte vector in which the value resides. This is based on the value's index. So first translate the 10-bit index into a bit number where it starts:

       SizeType bit0( idx * ValueBitSize );

Where idx is 0 to 719.

We can then locate the byte by dividing by the number of bits in a byte:

       SizeType byteIdx( bit0 / ByteBitSize );

The value will spill over into the next byte and we can obtain that easily enough by incrementing (i.e. ++byteIdx).

Next we need to obtain information about which parts of the two bytes used to store the value that value occupies. The least significant bit of the value in the first byte is given by the remainder of the start bit number (bit0) and the number of bits in a byte:

       SizeType byte0LSBit( bit0 % ByteBitSize );

The most significant bit of the value in the second byte can be similarly calculated, but is more complex as it is based on the number of remaining bits to be placed in the second byte, which is dependent on the number of bits of the value in the first byte and the number of bits in the values:

       SizeType byte1MSBit(ValueBitSize-(ByteBitSize-byte0LSBit)-1);

This tells us where the value goes but does not help us extract it (for getting) or placing it (for setting). For such operations we need mask values which are values that contain one values for each bit in each of the bytes the value occupies. These can be produced by using shifting:

       ByteType maskByte0( AllByteBits << byte0LSBit );
       ByteType maskByte1(AllByteBits >> (ByteBitSize-byte1MSBit-1));

AllByteBits is a constant having the value of a ByteType when all bits are 1, so has the value 0xff. Shifting is performed by the << and >> operators and is the original use for these operators.

v << n shifts v n bits to the left. For each bit shifted the hightest bit is lost and the zeroth bit becomes 0.

v >> n shifts v n bits to the right. For each bit shifted the hightest bit becomes 0 (or the value of the sign bit if v is signed) and the zeroth bit is lost.

So 11111111 << 2 becomes 11111100 and 11111111 >> 2 becomes 00111111.

We can now form the 10 bit value at an index by extracting the bytes from each of the two byte value locations, removing the parts which are not part of the value in question using the mask values with bitwise AND operations, and combining them using shifting and adding (or ORing):

       ValueType value( (iBytes[byteIdx] & maskByte0) >> byte0LSBit );

gets the value part in the first byte, and:

       ValueType valueUpper(iBytes[ ++byteIdx ] & maskByte1);

the value part in the second byte. They can then be combined:

       value += valueUpper << (ByteBitSize - byte0LSBit);

Note that the upper part is shifted to the correct starting bit in the 10-bit value before being added to the (lower part) value.

Setting a value is a little trickier as we need to split the value into two parts at the appropriate bit position and then place these values into the calculated byte positions in the frame byte vector. Again, this can be done with a combination of masking and shifting:

       ByteType maskValueLower( maskByte0 >> byte0LSBit );
       ByteType byte0( value & maskValueLower );
       ByteType byte1( value >> (ByteBitSize - byte0LSBit) );

Next we have to place the two parts of the value into the byte vector, but as it is likely that there are bits in these bytes that are used for parts of other values we cannot just assign the value's part bytes them. We have to keep the bits in each of the value's bytes that form part of other values an merge in the bits that are for the value we are setting. We can use the ones complement of the value byte masks to extract the bits used for other values as this gives us the bits that are NOT the bits of our values, for example to obtain just the bits used for another value in byte 0 we use:

       iBytes[ byteIdx ] &= ~maskByte0;

We can then add or OR in the bits of the value we are interested in for each byte, shifting into the relevant position. The whole sequence for setting the value byte values into their correct byte vector position is as follows:

       iBytes[ byteIdx ] &= ~maskByte0;
       iBytes[ byteIdx ] |= (byte0 << byte0LSBit);
       ++byteIdx;
       iBytes[ byteIdx ] &= ~maskByte1;
       iBytes[ byteIdx ] |= byte1;

I shall leave it to you to form the above into an implementation of the GetAt and SetAt member functions.

The following can be used as a test program:

       int main()
       {
         Frame frame;
         for ( Frame::ValueType i = 0; i<Frame::FrameSize; ++i )
         {
         frame.SetAt( i, i );
         }


         {
         std::ofstream outFile( "newFrameFile"
         , std::ios::out
         | std::ios::binary
         );
         frame.Write( outFile );
         }

         {
         std::ifstream inFile( "newFrameFile"
         , std::ios::in
         | std::ios::binary
         );
         frame.Read( inFile );
         }

         for ( Frame::ValueType i = 0; i<Frame::FrameSize; ++i )
         {
         std::cout << "frame[" << i << "] = "
         << frame.GetAt( i )
         << "\n";
         }

       }

You should include:

       #include <vector>
       #include <istream>
       #include <ostream>
       #include <fstream>
       #include <iostream>

Note that this is most likely not the only way to solve this problem and my solution may not be quite how you wish, especially the interface used: maybe you would like to pass in the files as pathnames to Read and Write. Maybe you would like to use operator[] or operator* to set and get values. I'll leave such details up to you. However you may find the latter tricky as such operators usually return their values by reference (as a pointer or reference) and this is not possible here without some additional complexity. You should of course add error and range checking and the like.

I should note that my bit-manipulation operations are not at all optimised for simplicity or speed, as they came straight out of my head. You might like to consider using lookup tables (fixed arrays) to get some of the currently on-the-fly calculated values. This is where the observation that 4 10 bit values fits into 5 8 bit bytes would be useful as the bit positions and mask values for values at an index in the byte array repeats every 4 values / 5 bytes.

I appologise for any errors or typos.

Finally, this has taken a long time to put together. I will reject any further questions of this complexity as I simply cannot spend this amount of time putting together a reply. You will have start using your own brain, sorry.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.