Skip to content

Commit

Permalink
Gigantic rewrite of all example apps
Browse files Browse the repository at this point in the history
  • Loading branch information
williamyang98 committed Jan 20, 2024
1 parent 3cdd3b1 commit 40e684d
Show file tree
Hide file tree
Showing 79 changed files with 2,727 additions and 3,965 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
[submodule "vendor/viterbi_decoder"]
path = vendor/viterbi_decoder
url = https://github.com/FiendChain/ViterbiDecoderCpp.git
[submodule "vendor/argparse"]
path = vendor/argparse
url = https://github.com/p-ranav/argparse.git
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ find_package(implot REQUIRED)
set(viterbi_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/viterbi_decoder)
find_package(viterbi CONFIG REQUIRED)

set(ARGPARSE_INSTALL OFF CACHE BOOL "Include an install target" FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/vendor/argparse)

if(WIN32)
set(rtlsdr_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vendor/librtlsdr)
find_package(rtlsdr CONFIG REQUIRED)
Expand Down
21 changes: 7 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ For those who are interested only in parts of the implementation refer to the fo
| src/ofdm | OFDM demodulation code |
| src/dab | DAB digital decoding core algorithms |
| src/basic_radio | Combines all of the DAB core algorithms into a cohesive example app |
| src/basic_scraper | Attaches itself via callbacks to a basic_radio instance to save audio/slideshow/MOT data to disk |
| src/utility | Small helper template library for general purpose use |
| src/basic_scraper | Listens to basic_radio instance to save audio/slideshow/MOT data to disk |
| examples/*.cpp | All our sample applications |

# Gallery
Expand All @@ -31,13 +30,13 @@ For those who are interested only in parts of the implementation refer to the fo
1. Download the ZIP archive from the releases page.
2. Setup rtlsdr radio drivers according to [here](https://www.rtl-sdr.com/rtl-sdr-quick-start-guide/)
3. Plug in your RTLSDR Blog v3 dongle
4. Run <code>./radio_app.exe</code>
5. Go to the simple_view tab and select a service from the list.
4. Run ```./radio_app.exe```
5. Go to the "Simple View" tab and select a service from the list.
6. Click "Run All" to listen to the channel and receive slideshows.

[Wohnort](http://www.wohnort.org/dab/) has an excellent website for viewing the list of DAB ensembles across the work. In Australia where I am, the blocks being used in Sydney are <code>[9A,9B,9C]</code>.
[Wohnort](http://www.wohnort.org/dab/) has an excellent website for viewing the list of DAB ensembles across the work. In Australia where I am, the blocks being used in Sydney are ```[9A,9B,9C]```.

Refer to <code>src/examples/README.md</code> for other example applications.
Refer to ```src/examples/README.md``` for other example applications.

If you can't find any DAB ensembles in your area, then you can download binary files from the Releases page [here](https://github.com/FiendChain/DAB-Radio/releases/tag/raw-iq-data). These contain raw IQ values as well as pre-demodulated OFDM digital frames. You can read in these files with the applications described in <code>src/examples/README.md</code>

Expand All @@ -63,7 +62,7 @@ Refer to [this github issue](https://github.com/FiendChain/DAB-Radio/issues/2#is

```./toolchains/windows/README.md``` has steps for configuring the right files to build for older CPUs.

# Inspirations
# Similar apps
- The welle.io open source radio has an excellent implementation of DAB radio. Their implementation is much more featureful and optimised than mine. Their repository can be found [here](https://github.com/albrechtl/welle.io). They also have a youtube video showcasing their wonderful software [here](https://www.youtube.com/watch?v=IJcgdmud-AI).

- There is a large community of rtl-sdr projects which can be found at [rtl-sdr.com](https://www.rtl-sdr.com/tag/dab/). This link points to a webpage showcasing several open source community projects that aim to decode DAB signals.
Expand All @@ -76,12 +75,6 @@ Refer to [this github issue](https://github.com/FiendChain/DAB-Radio/issues/2#is
- [RJVB/sse_mathfun](https://github.com/RJVB/sse_mathfun) for their SSE2 implementations of _mm_cos_pd

# TODO
- For OFDM demodulator the hand written SIMD might perform extremely poorly when compiling on gcc or clang.
- For DAB+ determine how to perform error correction on the firecode CRC16 in the AAC super frame.
- Replace imgui with a retain mode alternative.
- Automatically scan DAB ensembles and channels and persistent them on the user's drive.
- Support the rest of the DAB standard.
- MPEG-II audio for DAB channels.
- Handle strings with utf-8, utf-16 character sets.
- Decode MPEG-II audio for DAB channels.
- Add TII (transmitter identificaton information) decoding
- Add SNR meter
12 changes: 2 additions & 10 deletions cmake/Findfaad2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,9 @@ target_include_directories(faad2 PRIVATE ${FAAD_SRC_DIR})
target_include_directories(faad2 PUBLIC ${FAAD_HEADER_DIR})
set_target_properties(faad2 PROPERTIES CXX_STANDARD 17)

if(WIN32)
# win32_ver.h is required to set the package version string
configure_file(${CMAKE_CURRENT_LIST_DIR}/faad2_win32_ver.h.in ${FAAD_SRC_DIR}/win32_ver.h)
target_compile_definitions(faad2 PRIVATE
HAVE_STDINT_H
HAVE_STRING_H
HAVE_MEMCPY)
else()
target_compile_definitions(faad2 PRIVATE
PACKAGE_VERSION="libfaad2"
HAVE_STDINT_H
HAVE_STRING_H
HAVE_MEMCPY)
endif()
HAVE_MEMCPY
)
5 changes: 0 additions & 5 deletions cmake/faad2_win32_ver.h.in

This file was deleted.

85 changes: 21 additions & 64 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ project(examples)

add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/audio)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/device)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/getopt)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/gui)

set(SRC_DIR ${CMAKE_CURRENT_LIST_DIR})
Expand Down Expand Up @@ -34,87 +33,45 @@ if(NOT DEFINED RTLSDR_LIBS)
endif()
add_executable(rtl_sdr ${SRC_DIR}/rtl_sdr.cpp)
init_example(rtl_sdr)
target_link_libraries(rtl_sdr PRIVATE ${RTLSDR_LIBS} getopt)
target_link_libraries(rtl_sdr PRIVATE ${RTLSDR_LIBS} argparse::argparse)
install_dlls(rtl_sdr)

add_executable(simulate_transmitter ${SRC_DIR}/simulate_transmitter.cpp)
init_example(simulate_transmitter)
target_link_libraries(simulate_transmitter PRIVATE ofdm_core getopt)
target_link_libraries(simulate_transmitter PRIVATE ofdm_core argparse::argparse)

add_executable(convert_viterbi ${SRC_DIR}/convert_viterbi.cpp)
init_example(convert_viterbi)
target_link_libraries(convert_viterbi PRIVATE getopt)
target_link_libraries(convert_viterbi PRIVATE argparse::argparse)

add_executable(apply_frequency_shift ${SRC_DIR}/apply_frequency_shift.cpp)
init_example(apply_frequency_shift)
target_link_libraries(apply_frequency_shift PRIVATE getopt ofdm_core)
target_link_libraries(apply_frequency_shift PRIVATE argparse::argparse ofdm_core)

add_executable(read_wav ${SRC_DIR}/read_wav.cpp)
init_example(read_wav)
target_link_libraries(read_wav PRIVATE getopt)
target_link_libraries(read_wav PRIVATE argparse::argparse)

# Example applications
add_executable(ofdm_demod_gui
${SRC_DIR}/ofdm_demod_gui.cpp
${SRC_DIR}/gui/imgui_skeleton.cpp)
init_example(ofdm_demod_gui)
target_link_libraries(ofdm_demod_gui PRIVATE
ofdm_core getopt
ofdm_gui profiler_gui
imgui implot)

add_executable(ofdm_demod_cli ${SRC_DIR}/ofdm_demod_cli.cpp)
init_example(ofdm_demod_cli)
target_link_libraries(ofdm_demod_cli PRIVATE
ofdm_core getopt)

add_executable(basic_radio_app_no_demod
${SRC_DIR}/basic_radio_app_no_demod.cpp
${SRC_DIR}/gui/imgui_skeleton.cpp)
init_example(basic_radio_app_no_demod)
target_link_libraries(basic_radio_app_no_demod PRIVATE
dab_core basic_radio audio_lib
getopt easyloggingpp fmt
basic_radio_gui audio_gui
imgui)

add_executable(basic_radio_app
${SRC_DIR}/basic_radio_app.cpp
${SRC_DIR}/gui/imgui_skeleton.cpp)
add_executable(basic_radio_app_cli ${SRC_DIR}/basic_radio_app.cpp)
init_example(basic_radio_app_cli)
target_link_libraries(basic_radio_app_cli PRIVATE
argparse::argparse easyloggingpp fmt
ofdm_core dab_core basic_radio basic_scraper)
target_compile_definitions(basic_radio_app_cli PRIVATE BUILD_COMMAND_LINE)

set(COMMON_GUI_SRC ${SRC_DIR}/app_helpers/app_common_gui.cpp)
add_executable(basic_radio_app ${SRC_DIR}/basic_radio_app.cpp ${COMMON_GUI_SRC})
init_example(basic_radio_app)
target_link_libraries(basic_radio_app PRIVATE
ofdm_core dab_core basic_radio audio_lib
getopt easyloggingpp fmt
ofdm_gui basic_radio_gui audio_gui profiler_gui
imgui implot)

add_executable(basic_radio_scraper
${SRC_DIR}/basic_radio_scraper.cpp)
init_example(basic_radio_scraper)
target_link_libraries(basic_radio_scraper PRIVATE
ofdm_core dab_core basic_radio basic_scraper
getopt easyloggingpp fmt)

add_executable(basic_radio_scraper_no_demod
${SRC_DIR}/basic_radio_scraper_no_demod.cpp)
init_example(basic_radio_scraper_no_demod)
target_link_libraries(basic_radio_scraper_no_demod PRIVATE
dab_core basic_radio basic_scraper
getopt easyloggingpp fmt)

add_executable(basic_radio_benchmark ${SRC_DIR}/basic_radio_benchmark.cpp)
init_example(basic_radio_benchmark)
target_link_libraries(basic_radio_benchmark PRIVATE
dab_core basic_radio
getopt easyloggingpp fmt)
argparse::argparse easyloggingpp fmt
ofdm_core dab_core basic_radio basic_scraper audio_lib
ofdm_gui basic_radio_gui audio_gui imgui implot)

add_executable(radio_app
${SRC_DIR}/radio_app.cpp
${SRC_DIR}/gui/imgui_skeleton.cpp)
add_executable(radio_app ${SRC_DIR}/radio_app.cpp ${COMMON_GUI_SRC})
init_example(radio_app)
target_link_libraries(radio_app PRIVATE
device_lib ofdm_core dab_core basic_radio audio_lib
getopt fmt easyloggingpp
device_gui ofdm_gui basic_radio_gui audio_gui profiler_gui
imgui implot)
argparse::argparse easyloggingpp fmt
device_lib ofdm_core dab_core basic_radio audio_lib basic_scraper
device_gui ofdm_gui basic_radio_gui audio_gui imgui implot)
install_dlls(radio_app)
52 changes: 22 additions & 30 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,41 @@ Applications that use the OFDM and DAB code.
| --- | --- |
| **radio_app** | **The complete radio app with controls for the tuner** |
| rtl_sdr | Reads raw 8bit IQ values from your rtl-sdr dongle to stdout |
| ofdm_demod_cli | Demodulates a raw 8bit IQ stream into digital OFDM frames |
| ofdm_demod_gui | Demodulates a raw 8bit IQ stream into digital OFDM frames with a GUI |
| basic_radio_app | Complete app that reads raw 8bit IQ stream and demodulates and decodes it into a basic radio |
| basic_radio_app_no_demod | Reads in a digital OFDM frame from ofdm_demod_gui or ofdm_demod_cli and decodes it for a basic radio |
| basic_radio_scraper | Reads raw 8bit IQ stream, demodulates and decodes the data, then saves it to disk |
| basic_radio_scraper_no_demod | Reads in a digital OFDM frame from ofdm_demod_gui or ofdm_demod_cli, decodes the data then saves it to disk |
| basic_radio_app | OFDM demodulator and/or radio decoder that reads from a file with a gui |
| basic_radio_app_cli | OFDM demodulator and/or radio decoder that reads from a file without a gui |
| read_wav | Reads in a wav file which can be 8bit or 16bit PCM and dumps raw data to output as 8bit |
| apply_frequency_shift | Applies a frequency shift to a 8bit IQ stream |
| convert_viterbi | Decodes/encodes between a viterbi_bit_t array of soft decision bits to a packed byte |
| simulate_transmitter | Simulates a OFDM signal with a defined transmission mode, but doesn't contain any meaningful digital data. Outputs an 8bit IQ stream to stdout. |

## Example usage scenarios (using git-bash on Windows)
### 1. Run the complete radio app with rtlsdr tuner controls
<code>./radio_app</code>
Refer to ```-h``` or ```--help``` for more information on each application.

### 2. Run the radio app on a DAB ensemble while reading from rtl_sdr
### GUI Radio app with built in rtlsdr tuner controls
```./radio_app```

<code>./rtl_sdr | ./basic_radio_app</code>
### Tuner => OFDM => Radio => Audio
```./rtl_sdr -c [CHANNEL] | ./basic_radio_app```

### 3. Run data scraper
### Tuner => OFDM => Radio => Audio & Scraper
```./rtl_sdr -c [CHANNEL] | ./basic_radio_app --scraper-enable --scraper-output [DIRECTORY]```

<code>./rtl_sdr | ./basic_radio_scraper -o ./data/9C_2/</code>
### Tuner => OFDM => File_Soft
```./rtl_sdr -c [CHANNEL] | ./basic_radio_app --configuration ofdm --ofdm-enable-output > [FILENAME]```

### 4. Run OFDM demod while saving undecoded OFDM frame bits
### File_Soft => Radio => Audio
```./basic_radio_app -i [FILENAME] --configuration dab```

<code>./rtl_sdr | ./ofdm_demod_gui > ./data/frame_bits_9C.bin</code>
### Tuner => OFDM => Soft_to_Hard => File_Hard
```./rtl_sdr -c [CHANNEL] | ./basic_radio_app --configuration ofdm --ofdm-enable-output | ./convert_viterbi --type soft_to_hard > [FILENAME]```

### 5. Run OFDM demod while saving undecoded OFDM frame as packed bytes for 8x less space
An OFDM frame consists of 8bits values that represent a number from -127 to +127. We can instead represent them as -1 or +1 as a single bit. This reduces the amount of space by 8 times.

<code>./rtl_sdr | ./ofdm_demod_gui | ./convert_viterbi > ./data/frame_bytes_9C.bin</code>
### File_Hard => Hard_to_Soft => Radio => Audio
```./convert_viterbi -i [FILENAME] | ./basic_radio_app --configuration dab```

### 6. Unpack packed bytes into OFDM frame bits and run DAB radio app
### Tuner => OFDM => (Soft_to_Hard => File_Hard), (Radio => Audio)
```./rtl_sdr -c [CHANNEL] | ./basic_radio_app --configuration ofdm --ofdm-enable-output | tee >(./convert_viterbi --type soft_to_hard > [FILENAME]) | ./basic_radio_app --configuration dab```

<code>./convert_viterbi -d -i ./data/frame_bytes_9C.bin | ./basic_radio_app_no_demod</code>

**NOTE**: This is useful for testing changes to your DAB decoding implementation by replaying packed byte OFDM frames that you recorded previously.

### 7. Play radio with GUI while storing OFDM frames as packed bytes

<code>./rtl_sdr | ./ofdm_demod_gui | ./convert_viterbi | tee ./data/frame_bytes_9C.bin | ./convert_viterbi -d | ./basic_radio_app_no_demod</code>

**NOTE**: <code>tee</code> is a unix application that reads in stdin and outputs to stdout and the specified filepath.

### 8. Run data scraper with OFDM demodulator GUI while storing OFDM frames as packed bytes

<code>./rtl_sdr | ./ofdm_demod_gui | ./convert_viterbi | tee ./data/frame_bytes_9C.bin | ./convert_viterbi -d | ./basic_radio_scraper_no_demod</code>
- ```tee [...]``` is a command that copies stdin to multiple output files and stdout.
- ```>([command])``` is process substitution for bash shells. The command can then be used as a file descriptor (such as an argument for ```tee```).
39 changes: 39 additions & 0 deletions examples/app_helpers/app_audio.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include <memory>
#include "basic_radio/basic_radio.h"
#include "../audio/audio_pipeline.h"
#include "../audio/portaudio_utility.h"

static void attach_audio_pipeline_to_radio(std::shared_ptr<AudioPipeline> audio_pipeline, BasicRadio& basic_radio) {
if (audio_pipeline == nullptr) return;
basic_radio.On_DAB_Plus_Channel().Attach(
[audio_pipeline](subchannel_id_t subchannel_id, Basic_DAB_Plus_Channel& channel) {
auto& controls = channel.GetControls();
auto audio_source = std::make_shared<AudioPipelineSource>();
audio_pipeline->add_source(audio_source);
channel.OnAudioData().Attach(
[&controls, audio_source, audio_pipeline]
(BasicAudioParams params, tcb::span<const uint8_t> buf) {
if (!controls.GetIsPlayAudio()) return;
auto frame_ptr = reinterpret_cast<const Frame<int16_t>*>(buf.data());
const size_t total_frames = buf.size() / sizeof(Frame<int16_t>);
auto frame_buf = tcb::span(frame_ptr, total_frames);
const bool is_blocking = audio_pipeline->get_sink() != nullptr;
audio_source->write(frame_buf, float(params.frequency), is_blocking);
}
);
}
);
}

static PaDeviceIndex get_default_portaudio_device_index() {
#if _WIN32
constexpr PaHostApiTypeId PORTAUDIO_TARGET_HOST_API_ID = PaHostApiTypeId::paDirectSound;
const auto target_host_api_index = Pa_HostApiTypeIdToHostApiIndex(PORTAUDIO_TARGET_HOST_API_ID);
const auto target_device_index = Pa_GetHostApiInfo(target_host_api_index)->defaultOutputDevice;
#else
const auto target_device_index = Pa_GetDefaultOutputDevice();
#endif
return target_device_index;
}
Loading

0 comments on commit 40e684d

Please sign in to comment.