fm demod
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
*/build/
|
*/build/
|
||||||
|
*/.cache/
|
||||||
|
|||||||
50
fm/.clang-format
Normal file
50
fm/.clang-format
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
BasedOnStyle: Microsoft
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveMacros: 'true'
|
||||||
|
AlignConsecutiveAssignments: 'false'
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: 'true'
|
||||||
|
AlignTrailingComments: 'true'
|
||||||
|
AllowAllArgumentsOnNextLine: 'true'
|
||||||
|
AllowShortBlocksOnASingleLine: 'false'
|
||||||
|
AllowShortCaseLabelsOnASingleLine: 'true'
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
BinPackArguments: 'false'
|
||||||
|
BinPackParameters: 'false'
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: true
|
||||||
|
AfterControlStatement: true
|
||||||
|
AfterEnum: true # <-- You need this
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: true
|
||||||
|
AfterObjCDeclaration: true
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: true
|
||||||
|
AfterExternBlock: false # <-- And this
|
||||||
|
BeforeCatch: true
|
||||||
|
BeforeElse: true
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
BreakBeforeBraces: Custom # <-- And this
|
||||||
|
BreakBeforeBinaryOperators: All
|
||||||
|
ColumnLimit: 0
|
||||||
|
IndentCaseLabels: 'false'
|
||||||
|
Language: Cpp
|
||||||
|
MaxEmptyLinesToKeep: '1'
|
||||||
|
PointerAlignment: Left
|
||||||
|
ReflowComments: 'true'
|
||||||
|
SortIncludes: 'true'
|
||||||
|
SortUsingDeclarations: 'true'
|
||||||
|
SpaceAfterLogicalNot: 'false'
|
||||||
|
SpaceBeforeAssignmentOperators: 'true'
|
||||||
|
SpaceBeforeCpp11BracedList: 'true'
|
||||||
|
SpaceBeforeInheritanceColon: 'true'
|
||||||
|
TabWidth: '4'
|
||||||
|
UseTab: Never
|
||||||
|
AccessModifierOffset: '-4'
|
||||||
|
|
||||||
|
...
|
||||||
21
fm/CMakeLists.txt
Normal file
21
fm/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(fm_demodulate)
|
||||||
|
|
||||||
|
file(GLOB SRC_FILES src/*.cpp)
|
||||||
|
|
||||||
|
add_executable(fm_receiver ${SRC_FILES})
|
||||||
|
|
||||||
|
target_include_directories(fm_receiver PRIVATE src)
|
||||||
|
|
||||||
|
find_package(ALSA REQUIRED)
|
||||||
|
target_link_libraries(fm_receiver PRIVATE ALSA::ALSA)
|
||||||
|
find_library(RTLSDR_LIB rtlsdr)
|
||||||
|
find_path(RTLSDR_INCLUDE rtl-sdr.h)
|
||||||
|
|
||||||
|
if(NOT RTLSDR_LIB OR NOT RTLSDR_INCLUDE)
|
||||||
|
message(FATAL_ERROR "librtlsdr not found")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(fm_receiver PRIVATE ${RTLSDR_INCLUDE})
|
||||||
|
target_link_libraries(fm_receiver PRIVATE ${RTLSDR_LIB})
|
||||||
|
|
||||||
9003
fm/src/dr_wav.h
Normal file
9003
fm/src/dr_wav.h
Normal file
File diff suppressed because it is too large
Load Diff
109
fm/src/main.cpp
Normal file
109
fm/src/main.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <cmath>
|
||||||
|
#include <complex>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <rtl-sdr.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define DR_WAV_IMPLEMENTATION
|
||||||
|
#include "dr_wav.h"
|
||||||
|
|
||||||
|
#define DEFAULT_SAMPLE_RATE 2400000 // 2.4 MS/s
|
||||||
|
#define AUDIO_SAMPLE_RATE 48000
|
||||||
|
#define RTL_FM_DEMOD_BUF_LEN 16384 // 16kB
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
volatile bool stop_flag = 0;
|
||||||
|
|
||||||
|
void handle_sigint(int sig)
|
||||||
|
{
|
||||||
|
(void)sig;
|
||||||
|
stop_flag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
signal(SIGINT, handle_sigint);
|
||||||
|
|
||||||
|
rtlsdr_dev_t* dev = NULL;
|
||||||
|
rtlsdr_open(&dev, 0);
|
||||||
|
rtlsdr_set_center_freq(dev, 96700000);
|
||||||
|
rtlsdr_set_sample_rate(dev, DEFAULT_SAMPLE_RATE);
|
||||||
|
rtlsdr_set_tuner_gain_mode(dev, 1);
|
||||||
|
rtlsdr_set_tuner_gain(dev, 400); // 40dB
|
||||||
|
rtlsdr_reset_buffer(dev);
|
||||||
|
|
||||||
|
snd_pcm_t* pcm_handle;
|
||||||
|
snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
||||||
|
snd_pcm_set_params(pcm_handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 1, AUDIO_SAMPLE_RATE, 1, 500000);
|
||||||
|
|
||||||
|
drwav wav;
|
||||||
|
drwav_data_format format = {
|
||||||
|
.container = drwav_container_riff,
|
||||||
|
.format = DR_WAVE_FORMAT_PCM,
|
||||||
|
.channels = 1,
|
||||||
|
.sampleRate = 48000,
|
||||||
|
.bitsPerSample = 16};
|
||||||
|
|
||||||
|
// Open file for writing
|
||||||
|
drwav_init_file_write(&wav, "output.wav", &format, NULL);
|
||||||
|
|
||||||
|
uint8_t rtl_buf[RTL_FM_DEMOD_BUF_LEN];
|
||||||
|
float iq_buf[RTL_FM_DEMOD_BUF_LEN / 2];
|
||||||
|
float demod_buf[RTL_FM_DEMOD_BUF_LEN / 2];
|
||||||
|
int16_t audio_buf[8192];
|
||||||
|
|
||||||
|
while (!stop_flag)
|
||||||
|
{
|
||||||
|
int n_read;
|
||||||
|
rtlsdr_read_sync(dev, rtl_buf, RTL_FM_DEMOD_BUF_LEN, &n_read);
|
||||||
|
int n_samples = n_read / 2;
|
||||||
|
|
||||||
|
// i/q around 0
|
||||||
|
for (int i = 0; i < n_samples; i++)
|
||||||
|
{
|
||||||
|
int8_t i_sample = rtl_buf[2 * i] - 127;
|
||||||
|
int8_t q_sample = rtl_buf[2 * i + 1] - 127;
|
||||||
|
iq_buf[i] = atan2(q_sample, i_sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
// differential phase
|
||||||
|
static float prev_phase = 0;
|
||||||
|
for (int i = 0; i < n_samples; i++)
|
||||||
|
{
|
||||||
|
float dphi = iq_buf[i] - prev_phase;
|
||||||
|
// keep dphi within -pi and pi (as phase is circular)
|
||||||
|
if (dphi > M_PI)
|
||||||
|
dphi -= 2 * M_PI;
|
||||||
|
if (dphi < -M_PI)
|
||||||
|
dphi += 2 * M_PI;
|
||||||
|
prev_phase = iq_buf[i];
|
||||||
|
|
||||||
|
// low pass
|
||||||
|
static float y = 0;
|
||||||
|
float x = dphi;
|
||||||
|
y = y + (x - y) * 0.294f; // alpha 50us 48kHz
|
||||||
|
iq_buf[i] = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// then decimate
|
||||||
|
int out_index = 0;
|
||||||
|
for (int i = 0; i < n_samples; i += 50)
|
||||||
|
{
|
||||||
|
if (out_index < 8192)
|
||||||
|
{
|
||||||
|
audio_buf[out_index++] = (int16_t)(iq_buf[i] * 32767.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_writei(pcm_handle, audio_buf, out_index);
|
||||||
|
drwav_write_pcm_frames(&wav, out_index, audio_buf);
|
||||||
|
}
|
||||||
|
drwav_uninit(&wav);
|
||||||
|
rtlsdr_close(dev);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user