2

I am reading in a buffer of IQ data from a Software Defined Radio which I want to demodulate. The data I am receiving is a buffer of 8 bit unsigned int's. I need to convert this to buffer to type complex float for demodulation of the signal (I plan on using Liquid DSP Library). I am having difficulties in converting the buffer.

In GNURadio I have worked out my logic and am writing the output of my code to a binary file which I can then using as an input source for testing. So far the only thing that working is writing the uint8_t buffer to the file, other manipulation on the data breaks the logic.

GNURadio Block Diagram

Here is a snippet of the C++ code I have tried:

uint8_t buffer[buffersize];

uint8_t I;
uint8_t Q;

float Ifloat;
float Qfloat;

complex<float> complexsample;

ofstream myfile;
myfile.open("example.bin", ios::out | ios::app | ios::binary);

for (int x = 0; x < (buffersize/2); x++) {

    memcpy(&I, buffer + (2 * x), 1);
    memcpy(&Q, buffer + (1 + (2 * x)), 1);
    //writing I and Q above to a binary file works
        //myfile.write((char*) &I, sizeof(I));
        //myfile.write((char*) &Q, sizeof(Q));

    Ifloat = (float) I;
    Qfloat = (float) Q;
    //when I write Ifloat and Qfloat to a binary file then pass the 
    //file as an input source into the Add Const block things stop working
        //myfile.write((char*) &IIfloat, sizeof(Ifloat));
        //myfile.write((char*) &Qfloat, sizeof(Qfloat));


    Ifloat -= 127.0;
    Qfloat -= 127.0;
    //what I would do to turn the turn the unsigned value into a signed value
        //myfile.write((char*) &IIfloat, sizeof(Ifloat));
        //myfile.write((char*) &Qfloat, sizeof(Qfloat));

    complexsample.real(Ifloat);
    complexsample.imag(Qfloat);
    //what I would do to turn the I and Q floats into a single complex sample
        //myfile.write((char*) &complexsample, sizeof(complexsample));
}
jprince14
  • 191
  • 2
  • 13
  • Are you sure this code is what you actually wrote? because I don't think `memcpy(I, buffer + (2 * x), 1);` makes much sense if `I` is of type `uint8_t` and not `uint8_t [4]` or `uint8_t *`. Also, usually a float is 4 bytes and not 2 bytes or 1 byte as you seem to assume to various degrees in your code. – PeterT Oct 31 '14 at 21:55
  • 1
    At least it should be `memcpy(&I, buffer + (2 * x), 1);` or simply `I = buffer[2 * x]`. – Anton Savin Oct 31 '14 at 21:57
  • I actually had the memcpy(&I, buffer + (2 * x), 1) in my code, it was a typo in my post when I simplified it for posting. In the code I am running I am receiving from a tcp socket so I just removed that part for simplification. – jprince14 Oct 31 '14 at 22:02
  • I may have misunderstood, so you actually have `uint8_t` values from 0-255 and you really just want them as floats? – PeterT Oct 31 '14 at 22:03
  • @jprince14 what is the endianess that liquid-dsp or gnuradio or whatever you plug it into expects? – PeterT Oct 31 '14 at 22:08
  • The end goal is to convert the I/Q pair (I and Q are both a uint8_t) into a complex number. The logic I am trying to use to to replicate the logic that I confirmed works through GNURadio: uint8_t (same as uchar) -> unsigned float -> signed float -> complex – jprince14 Oct 31 '14 at 22:08
  • @PeterT I am not positive about the endianess but assume big endian. When I receive the tcp buffer it is in big endian. I have tried using the tcp input source for gnuradio and it works without any need to change endianess. I will try writing the Ifloat/Qfloat values to a file in little endian just to be certain. – jprince14 Oct 31 '14 at 22:25
  • If you are on an x86 machine then `myfile.write((char*) &IIfloat, sizeof(Ifloat));` is already writing in little endian, which is why I'd suggest swapping endianess. Conditional `bswap32` or `htonl` or whatever else you prefer, just make sure you don't implicitly convert the floats to ints in the process – PeterT Oct 31 '14 at 22:28
  • what are `I` and `Q` ? – M.M Nov 01 '14 at 01:04
  • @MattMcNabb the I and Q are the real and imaginary components of the signal I am readding off the SDR – jprince14 Nov 02 '14 at 00:43
  • @PeterT I tried `Ifloat = ntohs (Ifloat); Qfloat = ntohs (Qfloat); myfile.write((char*) (&Ifloat), sizeof(float)); myfile.write((char*) (&Qfloat), sizeof(float));` with no success – jprince14 Nov 02 '14 at 00:45
  • @jprince14 that will implicitly convert your floats to shorts and back, that's why I warned you about this behavior. – PeterT Nov 02 '14 at 00:47
  • @jprince14 nevermind my last comment, writing it directly into the float seems headed for disaster just do `int tempInt; tempInt = htonl(*((int *)&Ifloat)); myfile.write((char*) (&tempInt), sizeof(int)); tempInt = htonl(*((int *)&Qfloat)); myfile.write((char*) (&tempInt), sizeof(int));` – PeterT Nov 02 '14 at 01:02
  • Just because the two values have meaning as a pair, doesn't mean you actually need to use a complex data type. If you know the output file format, you can just write the I and Q parts separately, in the right place in the file. – Ben Voigt Nov 02 '14 at 01:49
  • Also, do yourself a favor and use `fopen`, `fread`, `fwrite` for this. C++ iostreams are for text files, they have a non-optional character encoding step which is different from the newline translation that `ios::binary` turns off. – Ben Voigt Nov 02 '14 at 01:51

1 Answers1

0

DrPaulBrewer wrote a simple program to do the exact conversion I was looking for. The link to his GitHub is https://gist.github.com/DrPaulBrewer/917f990cc0a51f7febb5

His source code is:

void main(int argc, char *argv[])
{
int byte1, byte2; // int -- not unsigned char -- see fgetc man page
float _Complex fc;
const size_t fc_size = sizeof(fc);
FILE *infile,*outfile;
const float scale = 1.0/128.0;
const char *infilename = argv[1];
const char *outfilename = argv[2];
if (argc<3){
printf("usage: rtlsdr-to-gqrx infile outfile\n");
exit(1);
}
// printf("in= %s out= %s \n", infilename, outfilename);
infile=fopen(infilename,"rb");
outfile=fopen(outfilename,"wb");
if ((infile==NULL) || (outfile==NULL)){
printf("Error opening files\n");
exit(1);
}
while ((byte1=fgetc(infile)) != EOF){
if ((byte2=fgetc(infile)) == EOF){
exit(0);
}
fc = scale*(byte1-127) + I*scale*(byte2-127);
fwrite(&fc,fc_size,1,outfile);
}
}
jprince14
  • 191
  • 2
  • 13
  • looks like endianess conversion is not necessary after all, maybe the issue was in your input or the order in which the comlex and imaginary part were written – PeterT Nov 02 '14 at 01:21
  • It looks like all you are missing in your code is the division by 128.0 – Ben Voigt Nov 02 '14 at 01:50
  • I got the code working in C and C++99 but am getting the error `unable to find numeric literal operator ‘operator"" iF'` when I use C++11. I want to use to pass the complex number into the liquid DSP library and it requires input of type float complex. Or is there a way to convert a C++11 complex number into a C complex number? I am currently using a version of my original code with `memcpy(&c_complex,&complexsample,sizeof(complexsample))` to convert from the C++ Complex number to a C complex number. This works but is there a more elegant way to do this conversion or avoid needing to convert? – jprince14 Nov 02 '14 at 19:21
  • @BenVoigt I tried removing the division by 128 and the code still worked. I'm stumped why the original code didn't work. – jprince14 Nov 02 '14 at 19:35