Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- """
- Real-time FM audio demodulator for HackRF One.
- * Centre frequency : 100.1 MHz (edit FREQ_HZ below if needed)
- * RF sample rate : 2 MS/s
- * Audio sample rate: 48 kS/s (pushed straight to your speakers)
- Dependencies
- ------------
- pip install numpy scipy pyhackrf sounddevice
- Tested with:
- * HackRF One firmware 2024.02.1
- * pyhackrf 0.6.7
- * NumPy 2.0, SciPy 1.13
- * sounddevice 0.4.6 (PortAudio backend)
- Press Ctrl-C to quit.
- """
- import numpy as np
- import scipy.signal
- import sounddevice as sd
- from math import gcd
- from hackrf import HackRf
- # ------------------ editable constants ------------------ #
- FREQ_HZ = 100_100_000 # 100.1 MHz – change to your station
- RF_RATE = 2_000_000 # 2 MS/s I-Q
- AUDIO_RATE = 48_000 # 48 kS/s mono
- RF_GAIN_DB = 20 # front-end gain in dB
- BUF_SYMS = 262_144 # ≈131 ms at 2 MS/s
- DEEMPH_TC = 75e-6 # 75 µs FM de-emphasis
- # -------------------------------------------------------- #
- def fm_demodulate(iq: np.ndarray) -> np.ndarray:
- """Wide-band FM discriminator + de-emphasis + resample."""
- iq -= np.mean(iq) # DC-remove
- audio = np.diff(np.unwrap(np.angle(iq))) # phase diff
- audio = audio[::40] # decimate 2 MHz → 50 kHz
- # 75 µs de-emphasis (simple IIR)
- alpha = np.exp(-1.0 / (AUDIO_RATE * DEEMPH_TC))
- b0, a1 = 1.0 - alpha, alpha
- for i in range(1, audio.size):
- audio[i] = b0 * audio[i] + a1 * audio[i-1]
- audio = np.tanh(5 * audio) # soft-clip
- fs_in = RF_RATE // 40 # 50 kHz
- if fs_in != AUDIO_RATE: # resample to 48 kHz
- g = gcd(fs_in, AUDIO_RATE)
- audio = scipy.signal.resample_poly(audio,
- AUDIO_RATE // g,
- fs_in // g)
- return audio.astype(np.float32)
- def main():
- hackrf = HackRf()
- hackrf.open()
- hackrf.set_sample_rate(RF_RATE)
- hackrf.set_freq(FREQ_HZ)
- hackrf.set_lna_gain(RF_GAIN_DB)
- hackrf.set_vga_gain(0)
- hackrf.set_amp_enable(False)
- hackrf.start_rx_mode()
- print(f"► Receiving {FREQ_HZ/1e6:.3f} MHz – playing live audio…")
- sd.default.samplerate = AUDIO_RATE
- sd.default.channels = 1
- stream = sd.OutputStream(dtype='float32')
- stream.start()
- try:
- while True:
- raw = hackrf.receive(BUF_SYMS) # bytes: I0 Q0 I1 Q1…
- u8 = np.frombuffer(raw, dtype=np.uint8).astype(np.float32) - 128.0
- iq = (u8[::2] + 1j * u8[1::2]) / 128.0 # complex f32 −1…+1
- audio = fm_demodulate(iq)
- stream.write(audio)
- except KeyboardInterrupt:
- pass
- finally:
- stream.stop()
- hackrf.stop_rx_mode()
- hackrf.close()
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement