Hi everyone,
I’m currently working with a LimeSDR Mini 2.4 board, using it as a lab-grade transmitter to evaluate the performance and reliability of various sub-GHz receivers. The idea is to transmit radio packets with different RF parameters (e.g., output power, baseband signal characteristics, modulation depth, bit timing, etc.) and observe how many packets are successfully received -allowing me to quantify receiver performance across varying conditions.
So far, I’ve been able to generate and transmit these packets using python SoapySDR, which gives me the automation I need for more systematic testing (code included). I still need to fine-tune the TXTSP block, especially to apply the necessary SINC filtering when working with unfiltered baseband waveforms, but overall, the LimeSDR seems to offer all the flexibility and features I need for this task.
However, I’ve run into a challenge.
Since I want to test at various power levels, I need to adjust the TX gain dynamically. The problem is that increasing the gain also increases carrier (local oscillator) leakage, which becomes significant at high gain settings. For instance, at a gain of 30, I measure a leakage signal of around –50 dBm, even when no data is being transmitted. This is problematic for high-sensitivity receivers, as they may falsely detect packets or be desensitized by the leakage.
During active transmission, the issue is minimal due to the high signal-to-noise ratio. ASK modulation still achieves around 35 dB modulation depth, which is sufficient. But when no signal is being transmitted, I’d ideally like the RF output to drop below the sensitivity threshold of the receiver (-95 dBm), effectively implementing a “mute” function" between packets.
What I’ve Tried
My initial approach was to set the TX gain to zero between transmissions. Functionally, this does the job, but the delay between the host and the SDR is inconsistent and often exceeds 700 µs, making it unsuitable for precise timing requirements (see attached image).
I suspect this latency is due to the control path from host to device, and the internal steps involved in changing the gain setting.
My Question
Would it be possible to implement a more responsive mute switch? For example, could the FPGA monitor the TX baseband stream for all-zero samples and, when detected, either:
- Automatically reduce gain to 0, or
- Disable the RF output via some internal switch or control register?
Alternatively, are there other approaches I could consider, like using the internal RF switches, loopback paths, or external absorptive RF switches to achieve microsecond muting?
I’d really appreciate any ideas, tips, or experiences from the community on this. Has anyone tried something similar?
Thanks in advance!
Code I’ve used:
# tx.py
import numpy as np, time
import SoapySDR
from waveform import bits_to_wave, packet_to_bits
SDR_ARGS = "driver=lime" # auto-detect first LimeSDR
FREQ = 868.3e6 # Hz
RATE = 1_000_000 # 1 MSps
BWIDTH = 1_500_000 # Hz
GAIN_TX = 30 # dB; start moderate
MOD_DEPTH= 1.0 # ASK high amplitude
def main():
dev = SoapySDR.Device(SDR_ARGS)
dev.setGain (SoapySDR.SOAPY_SDR_TX, 0, 0)
dev.setSampleRate(SoapySDR.SOAPY_SDR_TX, 0, RATE)
dev.setFrequency (SoapySDR.SOAPY_SDR_TX, 0, FREQ)
dev.setBandwidth(SoapySDR.SOAPY_SDR_TX, 0, BWIDTH)
# Random radio packet
pkt = bytes([0xaa, 0x9a, 0x25, 0xee, 0xde, 0xed, 0xd9, 0x9d, 0x99, 0x55, 0x51, 0xe1, 0x1e, 0x11, 0x11, 0x11, 0x1d, 0x52, 0xc0])
print(pkt.hex(' '))
pkt = bytes([~b & 0xFF for b in pkt])
print(pkt.hex(' '))
# Convert the series of bits to np complex array containing the IQ samples.
env = bits_to_wave(packet_to_bits(pkt), sample_rate=RATE, rise_fall=0)
# ASK "modulation"
iq = (env * MOD_DEPTH).astype(np.complex64) # pure real, imag = 0
# Lime prefers multiples of 1024 samples
pad = (-len(iq)) % 1024
iq = np.concatenate([iq, np.zeros(pad, np.complex64)])
stream = dev.setupStream(SoapySDR.SOAPY_SDR_TX, SoapySDR.SOAPY_SDR_CF32)
dev.activateStream(stream)
for i in range(50):
dev.setGain(SoapySDR.SOAPY_SDR_TX, 0, GAIN_TX)
sr = dev.writeStream(stream, [iq], len(iq))
print("TX writeStream result:", sr)
dev.setGain(SoapySDR.SOAPY_SDR_TX, 0, 0)
time.sleep(1)
dev.deactivateStream(stream)
dev.closeStream(stream)
if __name__ == "__main__":
main()

