Simultaneous Reading from Both Rx Channels with Python Script

Follow our development of DSP and SDR tutorials
Post Reply
Dm_Yarem
Posts: 1
Joined: Sat Feb 03, 2024 2:46 am

Simultaneous Reading from Both Rx Channels with Python Script

Post by Dm_Yarem »

Greetings,

I'm currently delving into the realm of BladeRF SDR and I've been using the Python script provided in the BladeRF repository as a reference. Specifically, I'm referring to the script available at the following link: https://github.com/Nuand/bladeRF/blob/m ... rx/txrx.py

However, my focus is solely on receiving data at the moment, and thus, I have simplified the receiving function to better suit my needs. Here's the simplified function:

Code: Select all

def receive(device, channel : int, freq : int, rate : int, gain : int,
            rxfile : str = '', num_samples : int = 1024):

    status = 0

    if( device == None ):
        print( "RX: Invalid device handle." )
        return -1

    if( channel == None ):
        print( "RX: Invalid channel." )
        return -1

    # Configure BladeRF
    ch             = device.Channel(channel)
    ch.frequency   = freq
    ch.sample_rate = rate
    ch.gain        = gain

    # Setup synchronous stream
    device.sync_config(layout         = _bladerf.ChannelLayout.RX_X1,
                       fmt            = _bladerf.Format.SC16_Q11,
                       num_buffers    = 16,
                       buffer_size    = 8192,
                       num_transfers  = 8,
                       stream_timeout = 3500)

    # Enable module
    print( "RX: Start" )
    ch.enable = True

    # Create receive buffer
    bytes_per_sample = 4
    buf = bytearray(1024*bytes_per_sample)
    num_samples_read = 0

    # Save the samples
    with open(rxfile, 'wb') as outfile:
        while True:
            if num_samples > 0 and num_samples_read == num_samples:
                break
            elif num_samples > 0:
                num = min(len(buf)//bytes_per_sample,
                          num_samples-num_samples_read)
            else:
                num = len(buf)//bytes_per_sample

            # Read into buffer
            num = int(num)
            if num != 1024:
                print(num)
            device.sync_rx(buf, num)
            num_samples_read += num

            # Write to file
            outfile.write(buf[:num*bytes_per_sample])

    # Disable module
    print( "RX: Stop" )
    ch.enable = False

    print( "RX: Done" )

    return 0
My inquiry pertains to the possibility of simultaneously receiving data from both Rx channels. I've tinkered with various parameters, but unfortunately, I haven't been successful in achieving this.

Could you kindly provide me with any tips or advice on how to achieve simultaneous reading from both Rx channels using the Python script?

Thank you in advance for your assistance.
doja
Posts: 3
Joined: Mon Dec 11, 2023 11:18 pm

Re: Simultaneous Reading from Both Rx Channels with Python Script

Post by doja »

.
Vidal Sauer
Posts: 1
Joined: Thu Mar 21, 2024 5:45 am

Re: Simultaneous Reading from Both Rx Channels with Python Script

Post by Vidal Sauer »

JavaScript and SQL are two powerful programming languages that play a crucial role in digital marketingslope game
ulnaoperating
Posts: 1
Joined: Mon Mar 25, 2024 8:55 pm

Re: Simultaneous Reading from Both Rx Channels with Python Script

Post by ulnaoperating »

There are two powerful programming languages that are used in the digital marketing slope game, and those languages are JavaScript and SQL.
felixandrea
Posts: 1
Joined: Sat May 04, 2024 7:18 pm

Re: Simultaneous Reading from Both Rx Channels with Python Script

Post by felixandrea »

Hello,

To read data from both Rx channels of the BladeRF SDR simultaneously, you can follow these steps:

Modify the receive() function to be able to read data from both Rx channels:

Add a new parameter channel2: int to the receive() function to represent the second Rx channel.
Create a separate read stream for the second Rx channel by copying and modifying some of the code from the first Rx channel read stream.
Ensure that the two read streams for the two Rx channels are running concurrently.
Example of how to update the receive() function:

def receive(device, channel1: int, channel2: int, freq: int, rate: int, gain: int,
rxfile1: str = '', rxfile2: str = '', num_samples: int = 1024):
status = 0

# Configure Rx channel 1
ch1 = device.Channel(channel1)
ch1.frequency = freq
ch1.sample_rate = rate
ch1.gain = gain

# Configure Rx channel 2
ch2 = device.Channel(channel2)
ch2.frequency = freq
ch2.sample_rate = rate
ch2.gain = gain

# Set up synchronous stream
device.sync_config(layout=_bladerf.ChannelLayout.RX_X2,
fmt=_bladerf.Format.SC16_Q11,
num_buffers=16,
buffer_size=8192,
num_transfers=8,
stream_timeout=3500)

# Enable modules
print("RX: Start")
ch1.enable = True
ch2.enable = True

# Create receive buffers
bytes_per_sample = 4
buf1 = bytearray(1024 * bytes_per_sample)
buf2 = bytearray(1024 * bytes_per_sample)
num_samples_read1 = 0
num_samples_read2 = 0

# Save the samples
with open(rxfile1, 'wb') as outfile1, open(rxfile2, 'wb') as outfile2:
while True:
if num_samples > 0 and num_samples_read1 == num_samples and num_samples_read2 == num_samples:
break

# Read data from Rx channel 1
num1 = min(len(buf1) // bytes_per_sample, num_samples - num_samples_read1)
device.sync_rx(buf1, num1, channel=channel1)
num_samples_read1 += num1
outfile1.write(buf1[:num1 * bytes_per_sample])

# Read data from Rx channel 2
num2 = min(len(buf2) // bytes_per_sample, num_samples - num_samples_read2)
device.sync_rx(buf2, num2, channel=channel2)
num_samples_read2 += num2
outfile2.write(buf2[:num2 * bytes_per_sample])

# Disable modules
print("RX: Stop")
ch1.enable = False
ch2.enable = False

print("RX: Done")
return 0
In this example, the receive() function has been updated to read data from both Rx channels. You need to provide two new parameters channel1 and channel2 to specify the Rx channels to read from. Additionally, two separate buffers buf1 and buf2 are created to store the data from the two Rx channels, and the data is written to two corresponding files rxfile1 and rxfile2. rice purity test

Please try using the updated receive() function and see if you can successfully read data from both Rx channels simultaneously.
feraculix
Posts: 2
Joined: Fri May 03, 2024 4:22 am

Re: Simultaneous Reading from Both Rx Channels with Python Script

Post by feraculix »

... since I'm working on the very same thing right now, I just wanted to provide my feedback on the code given by felixandrea.
To me, it seems like everything felixandrea wrote is fine, except for:
felixandrea wrote: Sat May 04, 2024 7:52 pm
# Read data from Rx channel 1
num1 = min(len(buf1) // bytes_per_sample, num_samples - num_samples_read1)
device.sync_rx(buf1, num1, channel=channel1)
num_samples_read1 += num1
outfile1.write(buf1[:num1 * bytes_per_sample])

# Read data from Rx channel 2
num2 = min(len(buf2) // bytes_per_sample, num_samples - num_samples_read2)
device.sync_rx(buf2, num2, channel=channel2)
num_samples_read2 += num2
outfile2.write(buf2[:num2 * bytes_per_sample])
I don't think the .sync_rx function from the bladerf python library does accept the "channel=..." argument.

Well, here is the code that is working for me:
(just quick and dirty, adjust it to your needs)
(I'm a noob to python, but it does the trick)
(copied from the description of the python bindings + API)

Code: Select all

import numpy as np
from bladerf import _bladerf

def configure_bladeRF(sdr):
    sample_rate = 2e6
    center_freq = 100e6
    gain = 26 # -15 to 60 dB
    
    
    rx_ch1 = sdr.Channel(_bladerf.CHANNEL_RX(0)) # give it a 0 or 1
    rx_ch2 = sdr.Channel(_bladerf.CHANNEL_RX(1)) # give it a 0 or 1
    
    rx_ch1.frequency = center_freq
    rx_ch1.sample_rate = sample_rate
    rx_ch1.bandwidth = sample_rate/2
    rx_ch1.gain_mode = _bladerf.GainMode.Manual
    rx_ch1.gain = gain
    rx_ch2.frequency = center_freq
    rx_ch2.sample_rate = sample_rate
    rx_ch2.bandwidth = sample_rate/2
    rx_ch2.gain_mode = _bladerf.GainMode.Manual
    rx_ch2.gain = gain
    
    
    # Setup synchronous stream
    # "RX_X2" -> should actually be two channel reception, so with channel 1 and 2 at the same time?!
    sdr.sync_config(layout = _bladerf.ChannelLayout.RX_X2, # or RX_X2
                fmt = _bladerf.Format.SC16_Q11, # int16s
                num_buffers    = 16, # 16
                buffer_size    = 1024*1, # 8192 = multiples of 1024
                num_transfers  = 8, # 8
                stream_timeout = 3500) # 3500
    return rx_ch1,rx_ch2

def receive_bladeRF(rx_ch1, rx_ch2, num_samples):
    print("Starting receive")
    rx_ch1.enable = True
    rx_ch2.enable = True
    # Create receive buffer
    bytes_per_sample = 4 # don't change this, it will always use int16s
    buf = bytearray(1024 * bytes_per_sample * 2) # *2 for two channels
    
    x = np.zeros(num_samples, dtype=np.complex64) # storage for IQ samples
    y = np.zeros(num_samples, dtype=np.complex64) # storage for IQ samples
    
    num_samples_read = 0
    while True:
        if num_samples > 0 and num_samples_read == num_samples:
            break
        elif num_samples > 0:
            num = min(len(buf) // bytes_per_sample, 2 *(num_samples - num_samples_read))
        else:
            num = len(buf) // bytes_per_sample
        sdr.sync_rx(buf, num) # Read into buffer
        samples = np.frombuffer(buf, dtype=np.int16)
        xtmp = samples[0::4] + 1j * samples[1::4] # Convert to complex type
        ytmp = samples[2::4] + 1j * samples[3::4] # Convert to complex type
        
        xtmp /= 2048.0 # Scale to -1 to 1 (its using 12 bit ADC)
        ytmp /= 2048.0 # Scale to -1 to 1 (its using 12 bit ADC)
        
        num /= 2
        num = np.int_(num)
        
        x[num_samples_read:num_samples_read+num] = xtmp[0:num] # Store buf in samples array
        y[num_samples_read:num_samples_read+num] = ytmp[0:num] # Store buf in samples array
        
        num_samples_read += num
    
    print("Stopping")
    rx_ch1.enable = False
    rx_ch2.enable = False
    return x, y

# main function calls start here:


sdr = _bladerf.BladeRF()


rx_ch1, rx_ch2 = configure_bladeRF(sdr)
num_samples = int(1e4)

# now receive both channels coherently
ch1, ch2 = receive_bladeRF(rx_ch1, rx_ch2, num_samples)


As a side note .. maybe it's just me, but the first .. maybe 20% .. of the received samples (at each call of "sdr.sync_rx(buf, num) ") seem to be corrupted/have low values, until the receiver seems to work properly. This is highly annoying. If this persists (I will check this forum for a solution soon ..) one seems to be forced to directly work with the C API ..

good luck
Post Reply