51 lines
2.2 KiB
Python
51 lines
2.2 KiB
Python
import numpy as np
|
|
import scipy.signal
|
|
import wave
|
|
|
|
def read_raw_iq_samples(file_path):
|
|
with open(file_path, 'rb') as file:
|
|
raw_data = file.read()
|
|
iq_samples = np.frombuffer(raw_data, dtype=np.int8)
|
|
return iq_samples
|
|
|
|
def fm_demodulation(iq_samples, sample_rate, fm_deviation=75e3):
|
|
# Hilbert transform for FM demodulation
|
|
analytic_signal = scipy.signal.hilbert(iq_samples)
|
|
instantaneous_phase = np.unwrap(np.angle(analytic_signal))
|
|
demodulated_signal = np.unwrap(np.angle(iq_samples[1:] * np.conj(iq_samples[:-1]))) / (2.0 * np.pi * 1.0/sample_rate * fm_deviation)
|
|
return demodulated_signal
|
|
|
|
def decimate_audio(signal, decimation_factor):
|
|
return scipy.signal.resample(signal, len(signal)//decimation_factor)
|
|
|
|
def low_pass_filter(signal, cutoff_frequency, sample_rate):
|
|
nyquist = 0.5 * sample_rate
|
|
normal_cutoff = cutoff_frequency / nyquist
|
|
b, a = scipy.signal.butter(6, normal_cutoff, btype='low', analog=False)
|
|
filtered_signal = scipy.signal.filtfilt(b, a, signal)
|
|
return filtered_signal
|
|
|
|
def save_wav_file(signal, sample_rate, output_file):
|
|
scaled_signal = np.int16(signal / np.max(np.abs(signal)) * 32767)
|
|
with wave.open(output_file, 'wb') as wav_file:
|
|
wav_file.setnchannels(1)
|
|
wav_file.setsampwidth(2)
|
|
wav_file.setframerate(sample_rate)
|
|
wav_file.writeframes(scaled_signal.tobytes())
|
|
|
|
def main(input_file, output_file, sample_rate, channel_width, decimation_factor=10, cutoff_frequency=75e3):
|
|
iq_samples = read_raw_iq_samples(input_file)
|
|
demodulated_signal = fm_demodulation(iq_samples, sample_rate)
|
|
decimated_signal = decimate_audio(demodulated_signal, decimation_factor)
|
|
filtered_signal = low_pass_filter(decimated_signal, cutoff_frequency, sample_rate/decimation_factor)
|
|
new_sample_rate = sample_rate / decimation_factor
|
|
save_wav_file(filtered_signal, new_sample_rate, output_file)
|
|
|
|
if __name__ == "__main__":
|
|
input_file_path = "fm_capture.bin"
|
|
output_wav_path = "output.wav"
|
|
sample_rate = 2e6 # Replace with the actual sample rate of your FM stream
|
|
channel_width = 200e3 # Replace with the channel width of your FM signal
|
|
|
|
main(input_file_path, output_wav_path, sample_rate, channel_width)
|