Wednesday, May 30, 2018

Generating RF with an osmo-fl2k VGA dongle

I've continued to play with the cheap ($10) USB VGA dongles which can be used as a fast digital to analog converter. Working with Ross, VK1UN, we've been climbing the learning curve of  GNURadio Companion to generate the samples we need to transmit modulated RF.

The fl2k_file command line tool needs a file with 8 bit signed samples. These are clocked out at 100M samples per second by default.

To get started I've written some pretty inefficient c to write out suitable files.

First here's a function that writes a suitable simple sine wave carrier. (I use 80 samples per cycle to get RF on 1.25MHz).

#include
#include
#include

const char *outFileName = "samples.dat";

void makeCarrier(int samplesPerCycle) {
    FILE *outfile = fopen(outFileName, "wb");
    int8_t byte;
    for(int sample = 0; sample < samplesPerCycle; sample++) {
double current_radian = M_PI * sample * 2 / samplesPerCycle;
        double carrier_sin_value = sin(current_radian);
        byte = (int8_t)(carrier_sin_value * 127.0);
        printf("%f, val = %f, byte = %d\n", current_radian, carrier_sin_value, (int)byte);
        fwrite(&byte, sizeof(byte), 1, outfile);
    }
    fclose(outfile);
}

Double sideband can be generated by simply multiplying samples of a modulating sine wave by the samples of the carrier.

// Produces an double sideband signal suitable for fl2k_file
void makeDsb(int samplesPerCycle) {
    // ratio of the carrier to the modulating sine wave
    int ratio = 3000;

    FILE *outfile = fopen(outFileName, "wb");
    // make sure we get enough samples for a full wave of the modulation
int totalSamples = samplesPerCycle * ratio;
    for(int sample = 0; sample < totalSamples; sample++) {
double carrier_radian = fmod((M_PI * sample * 2 / samplesPerCycle),(M_PI * 2));
        double mod_radian = fmod((M_PI * sample * 2 / samplesPerCycle) / ratio,(M_PI * 2));
        
//printf("%03d carrier r = %f, mod r = %f\n", sample, carrier_radian, mod_radian);

        double carrier_sin_value = sin(carrier_radian);
        double mod_sin_value = sin(mod_radian);
        
        double am_sample = carrier_sin_value * mod_sin_value;
        //printf("%f\t%f\t%f\n", carrier_sin_value, mod_sin_value, am_sample);

        int8_t byte = (int8_t)(am_sample * 127.0);
        //printf("byte = %d\n", byte);
        fwrite(&byte, sizeof(byte), 1, outfile);
    }
    fclose(outfile);
}

AM requires the modulating audio (ranging from -1 to +1) to have 1 added to it and the result divided by 2 before again multiplying by samples in the carrier sine wave.

// Produces an AM'd signal suitable for fl2k_file
void makeAm(int samplesPerCycle) {
    // ratio of the carrier to the modulating sine wave
    int ratio = 3000;

    FILE *outfile = fopen(outFileName, "wb");
    // make sure we get enough samples for a full wave of the modulation
int totalSamples = samplesPerCycle * ratio;
    for(int sample = 0; sample < totalSamples; sample++) {
double carrier_radian = fmod((M_PI * sample * 2 / samplesPerCycle),(M_PI * 2));
        double mod_radian = fmod((M_PI * sample * 2 / samplesPerCycle) / ratio,(M_PI * 2));
        
//printf("%03d carrier r = %f, mod r = %f\n", sample, carrier_radian, mod_radian);

        double carrier_sin_value = sin(carrier_radian);
        double mod_sin_value = (sin(mod_radian) + 1.0) / 2.0;
        
        double am_sample = carrier_sin_value * mod_sin_value;
        //printf("%f\t%f\t%f\n", carrier_sin_value, mod_sin_value, am_sample);

        int8_t byte = (int8_t)(am_sample * 127.0);
        //printf("byte = %d\n", byte);
        fwrite(&byte, sizeof(byte), 1, outfile);
    }
    fclose(outfile);
}

To look at the output file, to see if it's reasonable, I wrote a utility to read the bytes, spit out a text file with the numbers in it, feed that into gnuplot and look at the graph. Great but I've just had a better idea, the audio editing software Audacity can import files and setting it to signed 8 bit PCM works well and the waveform is easy to see.


Here's how AM looks:


The file is transmitted with fl2k_file simply as:

fl2k_file samples.dat

The output AM can view viewed on a CRO.


And shown on an SDR, here it is in SDR#. You can see the carrier and two nice sidebands.


On an AM radio, I hear a strong carrier with nice sounding tone.

I realise this is all very basic stuff but it's been great to learn a bit about it. The real power tool in all this is GRC (GNU Radio Companion).

Here's how I generate an amplitude modulated carrier suitable for sending to the fl2k dongle. (Click to enlarge it so you can read it).


The GNU Radio flow document is here.

Note that GRC always defaults to complex and in this case we want floats. This will run indefinitely and fill up your disk so just run for a few seconds and then open the file in Audacity to have a look at the waveform.

To avoid local AM stations I've been sending it with "fl2k_file -s 80e6 am_out.dat" which puts it on about 900kHz on the broadcast band.

Here's a GRC flow graph that shows how I read a WAV file with audio (ELO don't bring me down), resample it up and produce AM'd carrier to transmit. (Again click to read it).


Here's how it sounds on an AM radio.


Anyone know how to do this but with single side band?

Update: Ross transmits decodable WSPR

Transmitting a captured audio output from WSJT-X on AM, Ross has been able to decode the transmission.


And again but with double sideband modulation:


Next challenge is single sideband modulation.

No comments: