Thursday, August 23, 2018

Transmitting with the LimeSDR Mini

I was an early bird purchaser of the LimeSDR Mini, and I acquired a pair for $99 each. They arrived on February 10, 2018. I described in a previous post how I was able to use them for receiving, but it wasn't until August 17 that I was able to transmit a clean and reliable signal.

Right now, the transmit function seems to only work on Linux. I tried on Windows and got varying results. Initially I was able to transmit some distorted FM audio in the Windows version of GNUradio, but I could not transmit a digital QPSK signal. It's been a while since I tried, but I don't think I can transmit anything in Windows anymore. Currently, when I try to transmit from Windows, I get an unstable solid carrier.

On the LostCarrier.Online Discord channel, Ballistic Autistic told everyone that he succeeded in transmitting good FM audio with his LimeSDR Mini from GNUradio on Ubuntu. I asked him if he would try QPSK and send me a reception screenshot, which he did.

Credit: Ballistic Autistic

Once I knew that it was possible to make it work, I downloaded the GNUradio Live DVD image, which is a Ubuntu distribution, and used Universal USB Installer to install it to a flash drive. I installed the driver, compiled and installed gr-limesdr, and then had a working transmit setup. Since it's hard to find instructions for using the LimeSDR Mini, I'll explain the full process.

I would recommend updating the firmware on your LimeSDR Mini, but I don't think it's necessary if you don't want to.

Transmitting with a LimeSDR Mini, start to finish

1. Download the GNUradio Live DVD image. Get it from here: GNU Radio Live SDR Environment

2. Install it to a flash drive using Universal USB Installer. I chose to have a 1GB persistent area.

3. Boot from the drive

4. Open a terminal (command prompt) and enter the commands linked here to install the driver in Ubuntu.

5. Run "mkdir gr-limesdr"

6. Visit and enter the commands shown for Linux installation.

7. Open GNUradio Companion. You should now have LimeSDR source and sink blocks. You can click in the list of blocks on the right and press Ctrl+F to search for them.

If you haven't already, plug in your LimeSDR Mini. Now let's put together a flowgraph to test the transmitting function. You should have a second SDR on another computer to receive with.

Transmitting FM audio

Before starting, make sure you have a WAV file recorded as 48 kHz mono.

In GNUradio Companion, click Open and you should see a "ubuntu" folder on the left. Navigate there if it's not already selected and then open gr-limesdr/examples/FM_transmitter.grc.

1. Remove the "LimeSuite Sink (TX)" block, and add a fresh one from the list on the right.

2. Connect the output of "Rational Resampler" to the input of the new "LimeSuite Sink (TX)" block.

3. Double-click the LimeSuite block and you'll see that the "Device serial" field is empty. Make sure your LimeSDR Mini is connected with USB, then open a terminal and run "LimeUtil --find". This will print a list of all Lime devices. Copy the serial number of yours and paste it into the LimeSuite block's "Device serial" field.

4. Change "Device type" to LimeSDR-Mini

5. Choose an "RF frequency" that you want to transmit on. Even though there is very little transmit power, you want to choose a frequency that won't interfere with anything. I like 903.4 MHz but that's not usually license-free outside North America. Frequencies are measured in Hz, so you could type something like 903400000 or just 903.4e6, with e6 meaning MHz.

6. Set the "Sample rate" to 5e6, which means 5 MHz. Click OK.

7. Double-click the "Rational Resampler" block. Change "Interpolation" to 125 and "Decimation" to 12. Click OK.

The Resampler converts the 480 kHz FM quadrature (a.k.a. IQ data; in the next step) to 5 MHz. This block converts between IQ sample rates using the formula
    output_rate = input_rate * (Interpolation/Decimation).

Since the FM block will output IQ data at a rate of 480 kHz and we're transmitting with 5 MHz bandwidth,
    480000 * (125/12) = 5000000

8. Double-click the "NBFM Transmit" block. Change "Audio Rate" to 48000. Change "Quadrature Rate" to 480000. Change "Max Deviation" to your desired FM deviation. I'm going to use 24e3 (24 kHz). Click OK.

9. Finally, double-click the "Wav File Source" block. For the "File" field, click the "..." button to browse for the WAV file you want to transmit. Once it's in the "File" field, click OK.

Connect a receive SDR to another computer, open your desired SDR program, and navigate to the frequency you chose. At the computer you'll be transmitting with, connect your LimeSDR Mini to an antenna and click the green play button at the top. The result should be clear FM audio.

For some reason, I can't get my SDR to transmit on low frequencies like FM broadcast or the 6m ham band. Frequencies like 400 MHz and higher work well, but 900 MHz seems to work best.

The resulting signal received with an SDRplay RSP1

Transmitting QPSK

For this experiment, I'm going to transmit a QPSK signal with a rate of 1.25 Mbit/sec.

1. Create a new flowgraph and follow the instructions in the previous example to set up a "LimeSuite Sink (TX)" block. For this section I set mine, on the CH0 tab, to use a digital filter of about 750 kHz. This makes the signal edges look cleaner in the waterfall.

2. Add the following blocks: "Constellation Modulator" and "Constellation Object".

3. Double-click the "Constellation Modulator" block. In "Constellation", point it to the "Constellation Object" block by entering its name, variable_constellation_0. Change "Differential Encoding" to No and "Samples/Symbol" to 8. Click OK.

We're going to try transmitting a "Random Source" with random values evenly distributed between 0 and 255, and optionally a file with non-random data.

4. Add a "Random Source" block and double-click it. Change "Output Type" to Byte, "Minimum" to 0, and "Maximum" to 255. Click OK.

5. Connect the output of "Random Source" to the input of "Constellation Modulator". Connect the output of "Constellation Modulator" to the input of "LimeSuite Sink (TX)".

6. (Optional) Now add a "File Source" block and double-click it. Change "Output Type" to Byte. Choose a file and click OK. Right-click the block and choose Disable, then connect its output to the input of "Constellation Modulator".

Click the green play button to run the flowgraph.

For my optional file, I chose the Monero blockchain. Notice that the signal is not as smooth because the file isn't very random. If you want to switch between Random Source and File Source, just disable one and enable the other.

Random bytes from 0 to 255 inclusive

Monero blockchain

I had to reduce the transmit power by 5 dB for the random source because it appears to produce a much stronger signal.

Transmitting 16-QAM

This experiment will produce a 16-QAM signal with a rate of 2.5 Mbit/sec. It will occupy the same RF bandwidth as the QPSK signal in the previous section.

1. Follow the instructions above to create the flowgraph but don't transmit yet.

2. Double-click the "Constellation Object" block and change "Constellation Type" to 16QAM. Click OK and then try transmitting.

You'll notice that 16-QAM produces a smoother signal than QPSK.

Random bytes from 0 to 255 inclusive

Monero blockchain

Like the previous section, the random source transmission power had to be reduced by 5 dB so the waterfalls would look the same.

Thursday, August 2, 2018

JMemPGP: Java PGP API for handling strings

I've been looking for ways to use PGP in Java programs and the Bouncy Castle API seems to be the most common method. The problem is that almost every example involves reading a file and writing the result to another file. Others have asked on Stack Exchange about processing data solely from memory but solutions are very hard to find. I decided I would write my own API based on the Bouncy Castle methods so I could use PGP to operate on Java Strings and byte[] arrays.

My API is called JMemPGP (Java Memory PGP). The only files it needs are public and private keys, depending on what operation you want. The actual input and output data consist of a pair of byte[] arrays. If you want to use a String, you can use the String.getBytes() method.

I'm going to demonstrate the 4 basic PGP operations using JMemPGP: encrypt, decrypt, sign, and verify.

For this tutorial, you need GPG4Win, GPGshell, NetBeans, and two files from the Bouncy Castle website. Start NetBeans downloading now. Make sure to get a version that contains the JDK.

First, download and install GPG4Win and GPGshell. Then open Kleopatra and create a certificate. If you're not prompted to create one at startup, then navigate to File->New Certificate...

Click "Create a personal OpenPGP key pair", fill in the fields on the next page, and then I would suggest going into "Advanced Settings" and changing the key size to 4096, but that's not necessary to continue. Click Next and then Create Key. Follow the instructions shown for providing random input. When you're done, you should see your new certificate in the list.

Right-click it and choose "Export Certificates..."

Let's save it to the C drive. You might have to save it to a different folder if you're on Windows 10. Let's name it pub.gpg.

Now right-click the certificate again and choose "Export Secret Keys..." Make sure "ASCII armor" is unchecked. Save it as sec.gpg and click OK.

You should now have two files, as shown:

Now we need those two files from the Bouncy Castle website. Navigate to and scroll down to the "Signed Jar Files" section.

You need the two files that are highlighted. There may be a newer version by the time you download it and that's fine.

Now it's time to install NetBeans. The installer is pretty simple so just run it. Once it's done, open NetBeans and navigate to File->New Project...

The default project type should be a Java Application, so click Next. For a project name, just type PGPTutorial.

Now right-click the project's package in the pane on the left and choose New->Java Class...

Call the new class JMemPGP and click Finish. Now we need to install the Bouncy Castle API. Right-click the project this time, the item at the top with capital letters, and choose Properties at the bottom of the menu. Now choose the Libraries category and click "Add JAR/Folder".

Use the Ctrl key to select both JAR files, click Open, and then click OK to exit the Properties dialog.

Now visit my article on to get the JMemPGP API. It costs $1 to unlock the paywall. Once you're in, select the code and copy it to the clipboard. In NetBeans, go to your file, which should be open in the editor already, and replace the contents with what you just copied, but make sure to preserve the line "package pgptutorial;". Now click the Save All button at the top or press Ctrl+S.

There are just a couple more things we need. Add the following imports to your main file,
    import org.bouncycastle.jce.provider.BouncyCastleProvider;

Change your main() method to:
    public static void main(String[] args) throws Exception{

Finally, add this line to the beginning of your main() method:
    Security.addProvider(new BouncyCastleProvider());

Now we're ready to start using the API for the 4 basic PGP operations. Here is what should look like when you're done.

The 4 basic PGP operations


Let's say we want to encrypt the string "OneDirection" with our PGP public key. Copy this code to update your main() method:

This code starts with a String, converts it to a byte[] array, connects a ByteArrayInputStream to the byte[] array, encrypts the data, and returns it in a ByteArrayOutputStream. This is converted back to a byte[] array, and then to a String for printing to the screen.

Run the app and you should get output similar to this:

You can copy-paste the PGP message block and decrypt it with GPGtray. You could paste it into GPGtray's text window and decrypt from there, but we'll just use the quick decrypt option. Right-click the tray icon and select "Clipboard Decrypt.../Verify".

You should be prompted for the passphrase you used when you created your certificate. Enter it and click OK. Here is what your output should look like.

Notice that it says "0/12 Bytes". This means that our program encrypted just the 12 bytes in "OneDirection", with no padding.

You can also encrypt custom byte arrays, such as binary data.

Again, notice that we get an output of precisely 5 bytes.


We can also decrypt from within Java. Notice that this time we have to provide our passphrase within the program. I used "test" as mine.

Output should be similar to this:


The output will be a detached signature. If you were to type the text "OneDirection" into Notepad and save it as a *.txt file, you could copy-paste this detached signature into a file and save it as *.txt.asc and verify it with GpgEx.

Now right-click file.txt.asc and choose More GpgEx options->Verify.

Click "Decrypt/Verify".

As you would expect, if you change file.txt at all, the signature will not work. Let's change the text to "OneRepublic" and see what happens.

Save file.txt and try verifying it again.


You can also verify signatures from within Java.

Copy and paste this code, run it, and look at the last line it prints.

Let's change the line that says
    bIn = new ByteArrayInputStream(str);
to say
    bIn = new ByteArrayInputStream("OneDirection".getBytes());

Run the program again and you'll see that the signature is still valid. But if you change the string to say "OneRepublic" like in the last example, the signature will not match.

Run the program again and see what the last line says.