Advertisement
Guest User

Untitled

a guest
May 9th, 2025
17
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 2.93 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. """
  3. hackrf_fm_demod.py
  4.  
  5. A HackRF frequency translator + FM demodulator.
  6.  
  7. Usage:
  8. python3 hackrf_fm_demod.py <center_freq_Hz> <signal_offset_Hz> <output_wav>
  9.  
  10. Example:
  11. # Tune HackRF to 100 MHz, translate a 200 kHz-offset signal to baseband,
  12. # demodulate FM, and save audio to 'out.wav'
  13. python3 hackrf_fm_demod.py 100e6 200e3 out.wav
  14. """
  15. import sys
  16. import numpy as np
  17. from SoapySDR import Device, SOAPY_SDR_RX, SOAPY_SDR_CF32
  18. from scipy.signal import firwin, lfilter, resample_poly
  19. import soundfile as sf
  20.  
  21. def main():
  22. if len(sys.argv) != 4:
  23. print("Usage: python3 {} <center_freq_Hz> <signal_offset_Hz> <output_wav>".format(sys.argv[0]))
  24. sys.exit(1)
  25.  
  26. # Parse arguments
  27. center_freq = float(sys.argv[1]) # in Hz
  28. freq_offset = float(sys.argv[2]) # in Hz (positive shifts lower frequencies down)
  29. output_wav = sys.argv[3]
  30.  
  31. # SDR parameters
  32. sample_rate = 2_000_000.0 # 2 MS/s
  33. rx_gain = 40.0 # adjust per your environment
  34.  
  35. # Audio parameters
  36. audio_rate = 48_000 # 48 kHz output
  37. record_secs = 10 # seconds to record
  38.  
  39. # Initialize HackRF via SoapySDR
  40. args = dict(driver="hackrf")
  41. sdr = Device(args)
  42. sdr.setSampleRate(SOAPY_SDR_RX, 0, sample_rate)
  43. sdr.setGain (SOAPY_SDR_RX, 0, rx_gain)
  44. sdr.setFrequency (SOAPY_SDR_RX, 0, center_freq)
  45.  
  46. # Set up the RX stream
  47. rx_stream = sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0])
  48. sdr.activateStream(rx_stream)
  49.  
  50. # Design a low-pass filter for FM baseband (cutoff ~100 kHz)
  51. num_taps = 101
  52. lpf_taps = firwin(num_taps, cutoff=100e3, fs=sample_rate)
  53.  
  54. # Buffers for audio
  55. audio_chunks = []
  56. block_size = 16_384
  57. total_blocks = int(record_secs * sample_rate / block_size)
  58. sample_counter = 0
  59.  
  60. for _ in range(total_blocks):
  61. sr = sdr.readStream(rx_stream, [block_size])
  62. buff = np.array(sr[0], dtype=np.complex64)
  63.  
  64. # Frequency translation (mix down by freq_offset)
  65. t = (np.arange(len(buff)) + sample_counter) / sample_rate
  66. buff *= np.exp(-2j * np.pi * freq_offset * t)
  67. sample_counter += len(buff)
  68.  
  69. # FM demodulation: phase difference
  70. phase_diff = np.angle(buff[1:] * np.conj(buff[:-1]))
  71.  
  72. # Filter the demodulated signal
  73. filtered = lfilter(lpf_taps, 1.0, phase_diff)
  74.  
  75. # Resample directly to audio_rate
  76. audio = resample_poly(filtered, audio_rate, int(sample_rate))
  77. audio_chunks.append(audio.astype(np.float32))
  78.  
  79. # Clean up SDR
  80. sdr.deactivateStream(rx_stream)
  81. sdr.closeStream(rx_stream)
  82.  
  83. # Concatenate and write to WAV
  84. audio_data = np.concatenate(audio_chunks)
  85. sf.write(output_wav, audio_data, audio_rate)
  86. print(f"✅ Demodulated audio saved to '{output_wav}'")
  87.  
  88. if __name__ == "__main__":
  89. main()
  90.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement